xref: /aosp_15_r20/external/toybox/toys/other/shuf.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* shuf.c - Output lines in random order.
2  *
3  * Copyright 2023 Rob Landley <[email protected]>
4  *
5  * See https://man7.org/linux/man-pages/man1/shuf.1.html
6 
7 USE_SHUF(NEWTOY(shuf, "zen#<0", TOYFLAG_USR|TOYFLAG_BIN))
8 
9 config SHUF
10   bool "shuf"
11   default y
12   help
13     usage: shuf [-ze] [-n COUNT] [FILE...]
14 
15     Write lines of input to output in random order.
16 
17     -z	Input/output lines are NUL terminated.
18     -n	Stop after COUNT many output lines.
19     -e	Echo mode: arguments are inputs to shuffle, not files to read.
20 */
21 
22 #define FOR_shuf
23 #include "toys.h"
24 
GLOBALS(long n;char ** lines;long count;)25 GLOBALS(
26   long n;
27 
28   char **lines;
29   long count;
30 )
31 
32 static void do_shuf_line(char **pline, long len)
33 {
34   if (!pline) return;
35   if (!(TT.count&255))
36     TT.lines = xrealloc(TT.lines, sizeof(void *)*(TT.count+256));
37   TT.lines[TT.count++] = *pline; // TODO: repack?
38   *pline = 0;
39 }
40 
do_shuf(int fd,char * name)41 static void do_shuf(int fd, char *name)
42 {
43   do_lines(fd, '\n'*!FLAG(z), do_shuf_line);
44 }
45 
shuf_main(void)46 void shuf_main(void)
47 {
48   if (FLAG(e)) {
49     TT.lines = toys.optargs;
50     TT.count = toys.optc;
51   } else loopfiles(toys.optargs, do_shuf);
52 
53   if (!FLAG(n) || TT.n>TT.count) TT.n = TT.count;
54 
55   srandom(millitime());
56   while (TT.n--) {
57     long ll = random()%TT.count;
58     writeall(1, TT.lines[ll], strlen(TT.lines[ll])+FLAG(z));
59     if (!FLAG(e)) free(TT.lines[ll]);
60     else if (!FLAG(z)) writeall(1, "\n", 1);
61     TT.lines[ll] = TT.lines[--TT.count];
62   }
63 }
64