Home | History | Annotate | Line # | Download | only in shuffle
shuffle.c revision 1.1
      1 /*	$NetBSD: shuffle.c,v 1.1 1998/09/23 21:05:59 perry Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1998
      5  * 	Perry E. Metzger.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgment:
     17  *	This product includes software developed for the NetBSD Project
     18  *	by Perry E. Metzger.
     19  * 4. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 #include <sys/cdefs.h>
     35 #ifndef lint
     36 __RCSID("$NetBSD: shuffle.c,v 1.1 1998/09/23 21:05:59 perry Exp $");
     37 #endif /* not lint */
     38 
     39 #include <err.h>
     40 #include <stdio.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 #include <unistd.h>
     44 #include <sys/time.h>
     45 
     46 char **global_inputbuf;
     47 int global_inputlen;
     48 
     49 /*
     50  * enomem --
     51  *	die when out of memory.
     52  */
     53 void
     54 enomem()
     55 {
     56 	errx(2, "Cannot allocate memory.");
     57 }
     58 
     59 /*
     60  * emalloc --
     61  *	malloc, but die on error.
     62  */
     63 void *
     64 emalloc(len)
     65 	size_t len;
     66 {
     67 	void *p;
     68 
     69 	if ((p = malloc(len)) == NULL)
     70 		enomem();
     71 	return(p);
     72 }
     73 
     74 /*
     75  * ecalloc --
     76  *	calloc, but die on error.
     77  */
     78 void *
     79 ecalloc(size_t nmemb, size_t size)
     80 {
     81 	void *p;
     82 
     83 	if ((p = calloc(nmemb, size)) == NULL)
     84 		enomem();
     85 	return(p);
     86 }
     87 
     88 
     89 /*
     90  * estrdup --
     91  *	strdup, but die on error.
     92  */
     93 char *
     94 estrdup(const char *str)
     95 {
     96 	char *p;
     97 
     98 	if ((p = strdup(str)) == NULL)
     99 		enomem();
    100 	return(p);
    101 }
    102 
    103 /*
    104  * erealloc --
    105  *	realloc, but die on error.
    106  */
    107 void *
    108 erealloc(ptr, size)
    109 	void *ptr;
    110 	size_t size;
    111 {
    112 	if ((ptr = realloc(ptr, size)) == NULL)
    113 		enomem();
    114 	return(ptr);
    115 }
    116 
    117 int *get_shuffle(int t)
    118 {
    119 	int *shuffle;
    120 	int i, j, k, temp;
    121 
    122 	shuffle = (int *)ecalloc(t, sizeof(int));
    123 
    124 	for (i = 0; i < t; i++)
    125 		shuffle[i] = i;
    126 
    127 	/*
    128 	 * This algorithm taken from Knuth, Seminumerical Algorithms,
    129 	 * page 139.
    130 	 */
    131 
    132 	/* for (j = 0; j < t; j++) {
    133 	   k = random()%t; */
    134 
    135 	for (j = t-1; j > 0 ; j--) {
    136 		k = random() % (j+1);
    137 		temp = shuffle[j];
    138 		shuffle[j] = shuffle[k];
    139 		shuffle[k] = temp;
    140 	}
    141 
    142 	return(shuffle);
    143 }
    144 
    145 void
    146 usage(void)
    147 {
    148 	errx(1, "usage: shuffle [-c arg ...] [-n number] [-p number] [file]");
    149 }
    150 
    151 void
    152 swallow_input(FILE *input)
    153 {
    154 	int insize;
    155 	int numlines;
    156 	char **inputbuf, buf[BUFSIZ];
    157 
    158 
    159 	insize = BUFSIZ;
    160 	numlines = 0;
    161 
    162 	inputbuf = emalloc(sizeof(char *) * insize);
    163 
    164 	while (fgets(buf, BUFSIZ, input) != NULL) {
    165 		inputbuf[numlines] = estrdup(buf);
    166 		numlines++;
    167 		if (numlines >= insize) {
    168 			insize *= 2;
    169 			inputbuf = erealloc(inputbuf,
    170 			    (sizeof(char *) * insize));
    171 		}
    172 	}
    173 	inputbuf[numlines] = NULL;
    174 
    175 	global_inputbuf = inputbuf;
    176 	global_inputlen = numlines;
    177 
    178 }
    179 
    180 int
    181 main(int argc, char *argv[])
    182 {
    183 	extern char *optarg;
    184 	extern int optind;
    185 	int cflag, nflag, pflag, ch;
    186 	int *shuffle;
    187 	int i, p, t;
    188 	FILE *input;
    189 	struct timeval tv;
    190 
    191 	cflag = nflag = pflag = 0;
    192 	while ((ch = getopt(argc, argv, "cn:p:")) != -1) {
    193 		switch(ch) {
    194 		case 'c':
    195 			cflag = 1;
    196 			break;
    197 		case 'n':
    198 			if ((t = atoi(optarg)) <= 0)
    199 				errx(1, "%d: number is invalid\n", t);
    200 			nflag = 1;
    201 			break;
    202 		case 'p':
    203 			if ((p = atoi(optarg)) <= 0)
    204 				errx(1, "%d: number is invalid\n", p);
    205 			pflag = 1;
    206 			break;
    207 		case '?':
    208 		default:
    209 			usage();
    210 		}
    211 	}
    212 	argc -= optind;
    213 	argv += optind;
    214 
    215 	if ((cflag && nflag) || (nflag && (argc > 0)))
    216 		usage();
    217 
    218 	if (cflag)
    219 		t = argc;
    220 
    221 	if (!(cflag || nflag)) {
    222 
    223 		if (argc > 1)
    224 			usage();
    225 		if (argc == 1) {
    226 			if ((input = fopen(argv[0], "r")) == NULL)
    227 				err(1, "could not open %s", argv[0]);
    228 		}
    229 		else {
    230 			input = stdin;
    231 		}
    232 
    233 		swallow_input(input);
    234 		t = global_inputlen;
    235 	}
    236 
    237 	gettimeofday(&tv, NULL);
    238 	srandom(getpid() ^ ~getuid() ^ tv.tv_sec ^ tv.tv_usec);
    239 
    240 	shuffle = get_shuffle(t);
    241 
    242 	if (pflag) {
    243 		if (p > t)
    244 			errx(1, "-p specified more components than exist.");
    245 		t = p;
    246 	}
    247 
    248 	for (i = 0; i < t; i++) {
    249 		if (nflag)
    250 			printf("%d\n", shuffle[i]);
    251 		else if (cflag)
    252 			printf("%s\n", argv[shuffle[i]]);
    253 		else
    254 			printf("%s", global_inputbuf[shuffle[i]]);
    255 	}
    256 
    257 	return(0);
    258 }
    259