1*3e58855bSdholland /* $NetBSD: shuffle.c,v 1.22 2017/02/06 02:26:44 dholland Exp $ */
26e922305Sperry
36e922305Sperry /*
46e922305Sperry * Copyright (c) 1998
56e922305Sperry * Perry E. Metzger. All rights reserved.
66e922305Sperry *
76e922305Sperry * Redistribution and use in source and binary forms, with or without
86e922305Sperry * modification, are permitted provided that the following conditions
96e922305Sperry * are met:
106e922305Sperry * 1. Redistributions of source code must retain the above copyright
116e922305Sperry * notice, this list of conditions and the following disclaimer.
126e922305Sperry * 2. Redistributions in binary form must reproduce the above copyright
136e922305Sperry * notice, this list of conditions and the following disclaimer in the
146e922305Sperry * documentation and/or other materials provided with the distribution.
156e922305Sperry * 3. All advertising materials mentioning features or use of this software
166e922305Sperry * must display the following acknowledgment:
176e922305Sperry * This product includes software developed for the NetBSD Project
186e922305Sperry * by Perry E. Metzger.
196e922305Sperry * 4. The name of the author may not be used to endorse or promote products
206e922305Sperry * derived from this software without specific prior written permission.
216e922305Sperry *
226e922305Sperry * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
236e922305Sperry * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
246e922305Sperry * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
256e922305Sperry * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
266e922305Sperry * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
276e922305Sperry * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
286e922305Sperry * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
296e922305Sperry * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
306e922305Sperry * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
316e922305Sperry * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
326e922305Sperry */
336e922305Sperry
346e922305Sperry #include <sys/cdefs.h>
356e922305Sperry #ifndef lint
36*3e58855bSdholland __RCSID("$NetBSD: shuffle.c,v 1.22 2017/02/06 02:26:44 dholland Exp $");
376e922305Sperry #endif /* not lint */
386e922305Sperry
3949e4ac39Ssimonb #include <sys/time.h>
4049e4ac39Ssimonb
416e922305Sperry #include <err.h>
4249e4ac39Ssimonb #include <errno.h>
4349e4ac39Ssimonb #include <limits.h>
446e922305Sperry #include <stdio.h>
456e922305Sperry #include <stdlib.h>
466e922305Sperry #include <string.h>
476e922305Sperry #include <unistd.h>
48cdab3a7aSchristos #include <util.h>
4950a3fa9eSperry
501c06d95cSperry static size_t *get_shuffle(size_t);
516818646aSjoerg __dead static void usage(void);
521c06d95cSperry static void get_lines(const char *, char ***, size_t *);
531c06d95cSperry static size_t get_number(const char *, int);
5450a3fa9eSperry
556e922305Sperry /*
56635d3110Schristos * get_shuffle --
57635d3110Schristos * Construct a random shuffle array of t elements
58635d3110Schristos */
59635d3110Schristos static size_t *
get_shuffle(size_t t)601c06d95cSperry get_shuffle(size_t t)
616e922305Sperry {
62635d3110Schristos size_t *shuffle;
63635d3110Schristos size_t i, j, k, temp;
646e922305Sperry
65635d3110Schristos shuffle = emalloc(t * sizeof(size_t));
666e922305Sperry
676e922305Sperry for (i = 0; i < t; i++)
686e922305Sperry shuffle[i] = i;
696e922305Sperry
706e922305Sperry /*
716e922305Sperry * This algorithm taken from Knuth, Seminumerical Algorithms,
72e45cb214Sperry * 2nd Ed., page 139.
736e922305Sperry */
746e922305Sperry
756e922305Sperry for (j = t - 1; j > 0; j--) {
76*3e58855bSdholland k = arc4random_uniform(j + 1);
776e922305Sperry temp = shuffle[j];
786e922305Sperry shuffle[j] = shuffle[k];
796e922305Sperry shuffle[k] = temp;
806e922305Sperry }
816e922305Sperry
82635d3110Schristos return shuffle;
836e922305Sperry }
846e922305Sperry
85635d3110Schristos /*
86635d3110Schristos * usage --
87635d3110Schristos * Print a usage message and exit
88635d3110Schristos */
89635d3110Schristos static void
usage(void)901c06d95cSperry usage(void)
916e922305Sperry {
92a8ec668dScgd
93635d3110Schristos (void) fprintf(stderr,
94b635f565Sjmmv "usage: %s [-0] [-f <filename>] [-n <number>] [-p <number>] [<arg> ...]\n",
95a8ec668dScgd getprogname());
96635d3110Schristos exit(1);
976e922305Sperry }
986e922305Sperry
99635d3110Schristos
100635d3110Schristos /*
101635d3110Schristos * get_lines --
102635d3110Schristos * Return an array of lines read from input
103635d3110Schristos */
104635d3110Schristos static void
get_lines(const char * fname,char *** linesp,size_t * nlinesp)1051c06d95cSperry get_lines(const char *fname, char ***linesp, size_t *nlinesp)
1066e922305Sperry {
107635d3110Schristos FILE *fp;
108635d3110Schristos char *line;
1093fe322d0Smycroft size_t size, nlines = 0, maxlines = 128;
110635d3110Schristos char **lines = emalloc(sizeof(char *) * maxlines);
1116e922305Sperry
112635d3110Schristos if (strcmp(fname, "-") == 0)
113635d3110Schristos fp = stdin;
114635d3110Schristos else
115635d3110Schristos if ((fp = fopen(fname, "r")) == NULL)
116635d3110Schristos err(1, "Cannot open `%s'", fname);
1176e922305Sperry
118635d3110Schristos while ((line = fgetln(fp, &size)) != NULL) {
119635d3110Schristos if (size > 0 && line[size - 1] == '\n')
120635d3110Schristos size--;
121635d3110Schristos lines[nlines] = emalloc(size + 1);
122635d3110Schristos (void)memcpy(lines[nlines], line, size);
123635d3110Schristos lines[nlines++][size] = '\0';
124635d3110Schristos if (nlines >= maxlines) {
1253fe322d0Smycroft maxlines *= 2;
126635d3110Schristos lines = erealloc(lines, (sizeof(char *) * maxlines));
1276e922305Sperry }
1286e922305Sperry }
129635d3110Schristos lines[nlines] = NULL;
1306e922305Sperry
131635d3110Schristos *linesp = lines;
132635d3110Schristos *nlinesp = nlines;
133635d3110Schristos if (strcmp(fname, "-") != 0)
134635d3110Schristos (void)fclose(fp);
135635d3110Schristos }
1366e922305Sperry
137635d3110Schristos /*
138635d3110Schristos * get_number --
139635d3110Schristos * Return a number or exit on error
140635d3110Schristos */
141635d3110Schristos static size_t
get_number(const char * str,int ch)1421c06d95cSperry get_number(const char *str, int ch)
143635d3110Schristos {
144635d3110Schristos char *estr;
145ef83aa34Slukem long number;
146635d3110Schristos
147ef83aa34Slukem errno = 0;
148ef83aa34Slukem number = strtol(str, &estr, 0);
149635d3110Schristos if ((number == LONG_MIN || number == LONG_MAX) && errno == ERANGE)
150635d3110Schristos err(1, "bad -%c argument `%s'", ch, str);
151635d3110Schristos if (*estr)
152635d3110Schristos errx(1, "non numeric -%c argument `%s'", ch, str);
153635d3110Schristos if (number < 0)
154635d3110Schristos errx(1, "negative -%c argument `%s'", ch, str);
155635d3110Schristos return (size_t) number;
1566e922305Sperry }
1576e922305Sperry
1586e922305Sperry int
main(int argc,char * argv[])1591c06d95cSperry main(int argc, char *argv[])
1606e922305Sperry {
161932c7df0Slukem int nflag = 0, pflag = 0, ch;
162635d3110Schristos char *fname = NULL;
163a7b28c97Sfair size_t *shuffle = NULL;
164635d3110Schristos char **lines = NULL;
165932c7df0Slukem size_t nlines = 0, pick = 0, i;
1660d30aed4Schristos char sep = '\n';
1676e922305Sperry
1685446fd12Scube while ((ch = getopt(argc, argv, "0f:n:p:")) != -1) {
1696e922305Sperry switch(ch) {
1700d30aed4Schristos case '0':
1710d30aed4Schristos sep = '\0';
1720d30aed4Schristos break;
173635d3110Schristos case 'f':
174635d3110Schristos fname = optarg;
1756e922305Sperry break;
1766e922305Sperry case 'n':
177635d3110Schristos nlines = get_number(optarg, ch);
1786e922305Sperry nflag = 1;
1796e922305Sperry break;
1806e922305Sperry case 'p':
181635d3110Schristos pick = get_number(optarg, ch);
1826e922305Sperry pflag = 1;
1836e922305Sperry break;
1846e922305Sperry case '?':
1856e922305Sperry default:
1866e922305Sperry usage();
1876e922305Sperry }
1886e922305Sperry }
1896e922305Sperry argc -= optind;
1906e922305Sperry argv += optind;
1916e922305Sperry
192635d3110Schristos if ((fname && nflag) || (nflag && (argc > 0)))
1936e922305Sperry usage();
1946e922305Sperry
195635d3110Schristos if (fname != NULL)
196635d3110Schristos get_lines(fname, &lines, &nlines);
197635d3110Schristos else if (nflag == 0) {
198635d3110Schristos lines = argv;
199635d3110Schristos nlines = argc;
2006e922305Sperry }
2016e922305Sperry
202ff669e2eSperry if (nlines > 0)
203635d3110Schristos shuffle = get_shuffle(nlines);
2046e922305Sperry
2056e922305Sperry if (pflag) {
206635d3110Schristos if (pick > nlines)
2076e922305Sperry errx(1, "-p specified more components than exist.");
208635d3110Schristos nlines = pick;
2096e922305Sperry }
2106e922305Sperry
211635d3110Schristos for (i = 0; i < nlines; i++) {
2126e922305Sperry if (nflag)
213f49a04b8Schristos printf("%ld", (long)shuffle[i]);
2146e922305Sperry else
215f49a04b8Schristos printf("%s", lines[shuffle[i]]);
216f49a04b8Schristos putc(sep, stdout);
2176e922305Sperry }
2186e922305Sperry
219635d3110Schristos return 0;
2206e922305Sperry }
221