xref: /netbsd-src/usr.bin/shuffle/shuffle.c (revision 3e58855b269d4770da95710d8f7d53c644408e68)
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