Signed-off-by: Mattias Andrée
---
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
@@ -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
@@ -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
@@ -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 000..13ae1ad
--- /dev/null
+++ b/tac.1
@@ -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 000..a3cd22b
--- /dev/null
+++ b/tac.c
@@ -0,0 +1,69 @@
+/* See LICENSE file for copyright and license details. */
+#include
+#include
+#include
+#include
+
+#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, );
+ 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 = "";
+ 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, "") | fshut(stdout, "");
+
+ return ret;
+}
diff --git a/text.h b/text.h
index bceda52..7e3088d 100644
--- a/text.h
+++ b/text.h
@@ -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