[hackers] [sbase][PATCH] Add tac(1)

From: Mattias Andrée <maandree_AT_kth.se>
Date: Sat, 26 Mar 2016 12:08:28 +0100

Signed-off-by: Mattias Andrée <maandree_AT_kth.se>
---
 Makefile           |  1 +
 README             |  1 +
 libutil/getlines.c |  3 ++-
 tac.1              | 20 ++++++++++++++++
 tac.c              | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 text.h             |  3 ++-
 6 files changed, 95 insertions(+), 2 deletions(-)
 create mode 100644 tac.1
 create mode 100644 tac.c
diff --git a/Makefile b/Makefile
index 6b2bfdf..fc52e62 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -155,6 +155,7 @@ BIN =\
 	sponge\
 	strings\
 	sync\
+	tac\
 	tail\
 	tar\
 	tee\
diff --git a/README b/README
index d60d8fc..9c3bc09 100644
--- a/README
+++ b/README
_AT_@ -84,6 +84,7 @@ The following tools are implemented:
 0=*|x sponge          .
 0#*|o strings         .
 0=*|x sync            .
+0=* x tac             .
 0=*|o tail            .
 0=*|x tar             .
 0=*|o tee             .
diff --git a/libutil/getlines.c b/libutil/getlines.c
index b912769..96fec53 100644
--- a/libutil/getlines.c
+++ b/libutil/getlines.c
_AT_@ -23,7 +23,8 @@ getlines(FILE *fp, struct linebuf *b)
 		b->lines[b->nlines - 1].len = linelen;
 	}
 	free(line);
-	if (b->lines && b->nlines && linelen && b->lines[b->nlines - 1].data[linelen - 1] != '\n') {
+	b->nolf = b->lines && b->nlines && linelen && b->lines[b->nlines - 1].data[linelen - 1] != '\n';
+	if (b->nolf) {
 		b->lines[b->nlines - 1].data = erealloc(b->lines[b->nlines - 1].data, linelen + 2);
 		b->lines[b->nlines - 1].data[linelen] = '\n';
 		b->lines[b->nlines - 1].data[linelen + 1] = '\0';
diff --git a/tac.1 b/tac.1
new file mode 100644
index 0000000..13ae1ad
--- /dev/null
+++ b/tac.1
_AT_@ -0,0 +1,20 @@
+.Dd 2016-03-26
+.Dt TAC 1
+.Os sbase
+.Sh NAME
+.Nm tac
+.Nd concatenate files, but reverse the order of their lines
+.Sh SYNOPSIS
+.Nm
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm
+reads each
+.Ar file
+and prints the their lines in reverse order.
+The files themself are however printed in order.
+If no
+.Ar file
+is given
+.Nm
+reads from stdin.
diff --git a/tac.c b/tac.c
new file mode 100644
index 0000000..a3cd22b
--- /dev/null
+++ b/tac.c
_AT_@ -0,0 +1,69 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "text.h"
+#include "util.h"
+
+static void
+usage(void)
+{
+	eprintf("usage: %s [file ...]\n", argv0);
+}
+
+static void
+tac(FILE *fp)
+{
+	struct linebuf buf = EMPTY_LINEBUF;
+	struct line line;
+	getlines(fp, &buf);
+	while (buf.nlines--) {
+		line = buf.lines[buf.nlines];
+		if (buf.nolf) {
+			/* If the last line is not LF-terminated, the
+			 * first output line should not be either. */
+			buf.nolf = 0;
+			line.len--;
+			line.data[line.len] = '\0';
+		}
+		fwrite(line.data, 1, line.len, stdout);
+		free(line.data);
+	}
+	free(buf.lines);
+}
+
+int
+main(int argc, char *argv[])
+{
+	FILE *fp;
+	int ret = 0;
+
+	ARGBEGIN {
+	default:
+		usage();
+	} ARGEND
+
+	if (!argc) {
+		tac(stdin);
+	} else {
+		for (; *argv; argc--, argv++) {
+			if (!strcmp(*argv, "-")) {
+				*argv = "<stdin>";
+				fp = stdin;
+			} else if (!(fp = fopen(*argv, "r"))) {
+				weprintf("fopen %s:", *argv);
+				ret = 1;
+				continue;
+			}
+			tac(fp);
+			if (fp != stdin && fshut(fp, *argv))
+				ret = 1;
+		}
+	}
+
+	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
+
+	return ret;
+}
diff --git a/text.h b/text.h
index bceda52..7e3088d 100644
--- a/text.h
+++ b/text.h
_AT_@ -9,8 +9,9 @@ struct linebuf {
 	struct line *lines;
 	size_t nlines;
 	size_t capacity;
+	int nolf;
 };
-#define EMPTY_LINEBUF {NULL, 0, 0,}
+#define EMPTY_LINEBUF {NULL, 0, 0, 0}
 void getlines(FILE *, struct linebuf *);
 
 void concat(FILE *, const char *, FILE *, const char *);
-- 
2.7.3
Received on Sat Mar 26 2016 - 12:08:28 CET

This archive was generated by hypermail 2.3.0 : Sat Mar 26 2016 - 12:12:14 CET