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

From: Mattias Andrée <maandree_AT_kth.se>
Date: Sat, 26 Mar 2016 13:50:47 +0100

Signed-off-by: Mattias Andrée <maandree_AT_kth.se>
---
 Makefile |   1 +
 README   |   1 +
 shuf.1   |  26 +++++++++++
 shuf.c   | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 177 insertions(+)
 create mode 100644 shuf.1
 create mode 100644 shuf.c
diff --git a/Makefile b/Makefile
index 6b2bfdf..e407663 100644
--- a/Makefile
+++ b/Makefile
_AT_@ -149,6 +149,7 @@ BIN =\
 	sha512sum\
 	sha512-224sum\
 	sha512-256sum\
+	shuf\
 	sleep\
 	sort\
 	split\
diff --git a/README b/README
index d60d8fc..e752a09 100644
--- a/README
+++ b/README
_AT_@ -78,6 +78,7 @@ The following tools are implemented:
 0=*|x sha512sum       .
 0=* x sha512-224sum   .
 0=* x sha512-256sum   .
+0=* x shuf            (-e, -i, -n, -o, -z)
 0=*|o sleep           .
 0#*|o sort            .
 0=*|o split           .
diff --git a/shuf.1 b/shuf.1
new file mode 100644
index 0000000..ce0dcd0
--- /dev/null
+++ b/shuf.1
_AT_@ -0,0 +1,26 @@
+.Dd 2016-03-26
+.Dt SHUF 1
+.Os sbase
+.Sh NAME
+.Nm shuf
+.Nd shuffle the lines in a file
+.Sh SYNOPSIS
+.Nm
+.Op Fl r
+.Op Fl s Ar source
+.Op Ar file
+.Sh DESCRIPTION
+.Nm
+reads each line from
+.Ar file
+and prints its line in random order.
+.Nm
+reads from stdin.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl r
+Output random lines ad infinitum.
+.It Fl s Ar source
+Get random bytes from
+.Ar source .
+.El
diff --git a/shuf.c b/shuf.c
new file mode 100644
index 0000000..a325bb2
--- /dev/null
+++ b/shuf.c
_AT_@ -0,0 +1,149 @@
+/* See LICENSE file for copyright and license details. */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "text.h"
+#include "util.h"
+
+static int source = -1;
+static char *sflag = 0;
+static int (*random_byte)(void);
+
+static void
+usage(void)
+{
+	eprintf("usage: %s [-r] [-s source] [file]\n", argv0);
+}
+
+static int
+random_byte_file(void)
+{
+	unsigned char r;
+	ssize_t n = read(source, &r, 1);
+	if (n < 0)
+		eprintf("read %s:", sflag);
+	if (!n)
+		eprintf("read %s: end of file reached\n");
+	return r;
+}
+
+static int
+random_byte_libc(void)
+{
+	double r;
+	r = rand();
+	r /= (double)RAND_MAX + 1;
+	r *= 256;
+	return (int)r;
+}
+
+static size_t
+random_int(size_t max)
+{
+	size_t n = max;
+	size_t r = 0;
+	size_t mask = max;
+	int s = 1;
+
+	while (((mask + 1) & ~mask) != (mask + 1))
+		mask |= mask >> s++;
+
+	do {
+		while (n) {
+			n >>= 8;
+			r <<= 8;
+			r |= random_byte();
+		}
+		r &= mask;
+	} while (r > max);
+
+	return r;
+}
+
+static void
+shuf(FILE *fp, int repeat)
+{
+	struct linebuf lines = EMPTY_LINEBUF;
+	struct line line;
+	size_t i, j;
+
+	getlines(fp, &lines);
+
+	if (source < 0) {
+		srand((intptr_t)(lines.lines) | time(NULL));
+		random_byte = random_byte_libc;
+	} else {
+		random_byte = random_byte_file;
+	}
+
+	if (repeat) {
+		if (!lines.nlines)
+			eprintf("no lines to repeat\n");
+		for (;;) {
+			i = random_int(lines.nlines - 1);
+			line = lines.lines[i];
+			fwrite(line.data, 1, line.len, stdout);
+		}
+	}
+
+	for (i = lines.nlines; i--;) {
+		j = random_int(i);
+		line = lines.lines[i];
+		lines.lines[i] = lines.lines[j];
+		lines.lines[j] = line;
+	}
+	for (i = lines.nlines; i--;) {
+		line = lines.lines[i];
+		fwrite(line.data, 1, line.len, stdout);
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	FILE *fp;
+	int rflag = 0;
+	int ret = 0;
+
+	ARGBEGIN {
+	case 'r':
+		rflag = 1;
+		break;
+	case 's':
+		if (source >= 0)
+			close(source);
+		source = open(sflag = EARGF(usage()), O_RDONLY);
+		if (source < 0)
+			eprintf("open %s:", sflag);
+		break;
+	default:
+		usage();
+	} ARGEND
+
+	if (argc > 1)
+		usage();
+
+	if (!argc) {
+		shuf(stdin, rflag);
+	} else {
+		if (!strcmp(*argv, "-")) {
+			*argv = "<stdin>";
+			fp = stdin;
+		} else if (!(fp = fopen(*argv, "r"))) {
+			eprintf("fopen %s:", *argv);
+		}
+		shuf(fp, rflag);
+		if (fp != stdin && fshut(fp, *argv))
+			ret = 1;
+	}
+
+	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
+	if (source >= 0)
+		close(source);
+
+	return ret;
+}
-- 
2.7.3
Received on Sat Mar 26 2016 - 13:50:47 CET

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