--- 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.3Received 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