Home | History | Annotate | Line # | Download | only in shuffle
shuffle.c revision 1.2
      1 /*	$NetBSD: shuffle.c,v 1.2 1998/09/23 21:45:44 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.2 1998/09/23 21:45:44 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 void enomem(void);
     47 void *emalloc(size_t len);
     48 void *ecalloc(size_t nmemb, size_t size);
     49 char *estrdup(const char *str);
     50 void *erealloc(void *ptr, size_t size);
     51 
     52 int *get_shuffle(int t);
     53 void usage(void);
     54 void swallow_input(FILE *input);
     55 
     56 char **global_inputbuf;
     57 int global_inputlen;
     58 
     59 /*
     60  * enomem --
     61  *	die when out of memory.
     62  */
     63 void
     64 enomem(void)
     65 {
     66 	errx(2, "Cannot allocate memory.");
     67 }
     68 
     69 /*
     70  * emalloc --
     71  *	malloc, but die on error.
     72  */
     73 void *
     74 emalloc(size_t len)
     75 {
     76 	void *p;
     77 
     78 	if ((p = malloc(len)) == NULL)
     79 		enomem();
     80 	return(p);
     81 }
     82 
     83 /*
     84  * ecalloc --
     85  *	calloc, but die on error.
     86  */
     87 void *
     88 ecalloc(size_t nmemb, size_t size)
     89 {
     90 	void *p;
     91 
     92 	if ((p = calloc(nmemb, size)) == NULL)
     93 		enomem();
     94 	return(p);
     95 }
     96 
     97 
     98 /*
     99  * estrdup --
    100  *	strdup, but die on error.
    101  */
    102 char *
    103 estrdup(const char *str)
    104 {
    105 	char *p;
    106 
    107 	if ((p = strdup(str)) == NULL)
    108 		enomem();
    109 	return(p);
    110 }
    111 
    112 /*
    113  * erealloc --
    114  *	realloc, but die on error.
    115  */
    116 void *
    117 erealloc(void *ptr, size_t size)
    118 {
    119 	if ((ptr = realloc(ptr, size)) == NULL)
    120 		enomem();
    121 	return(ptr);
    122 }
    123 
    124 int *get_shuffle(int t)
    125 {
    126 	int *shuffle;
    127 	int i, j, k, temp;
    128 
    129 	shuffle = (int *)ecalloc(t, sizeof(int));
    130 
    131 	for (i = 0; i < t; i++)
    132 		shuffle[i] = i;
    133 
    134 	/*
    135 	 * This algorithm taken from Knuth, Seminumerical Algorithms,
    136 	 * page 139.
    137 	 */
    138 
    139 	/* for (j = 0; j < t; j++) {
    140 	   k = random()%t; */
    141 
    142 	for (j = t-1; j > 0 ; j--) {
    143 		k = random() % (j+1);
    144 		temp = shuffle[j];
    145 		shuffle[j] = shuffle[k];
    146 		shuffle[k] = temp;
    147 	}
    148 
    149 	return(shuffle);
    150 }
    151 
    152 void
    153 usage(void)
    154 {
    155 	errx(1, "usage: shuffle [-c arg ...] [-n number] [-p number] [file]");
    156 }
    157 
    158 void
    159 swallow_input(FILE *input)
    160 {
    161 	int insize;
    162 	int numlines;
    163 	char **inputbuf, buf[BUFSIZ];
    164 
    165 
    166 	insize = BUFSIZ;
    167 	numlines = 0;
    168 
    169 	inputbuf = emalloc(sizeof(char *) * insize);
    170 
    171 	while (fgets(buf, BUFSIZ, input) != NULL) {
    172 		inputbuf[numlines] = estrdup(buf);
    173 		numlines++;
    174 		if (numlines >= insize) {
    175 			insize *= 2;
    176 			inputbuf = erealloc(inputbuf,
    177 			    (sizeof(char *) * insize));
    178 		}
    179 	}
    180 	inputbuf[numlines] = NULL;
    181 
    182 	global_inputbuf = inputbuf;
    183 	global_inputlen = numlines;
    184 
    185 }
    186 
    187 int
    188 main(int argc, char *argv[])
    189 {
    190 	extern char *optarg;
    191 	extern int optind;
    192 	int cflag, nflag, pflag, ch;
    193 	int *shuffle;
    194 	int i, p, t;
    195 	FILE *input;
    196 	struct timeval tv;
    197 
    198 	cflag = nflag = pflag = 0;
    199 	p = t = 0;
    200 
    201 	while ((ch = getopt(argc, argv, "cn:p:")) != -1) {
    202 		switch(ch) {
    203 		case 'c':
    204 			cflag = 1;
    205 			break;
    206 		case 'n':
    207 			if ((t = atoi(optarg)) <= 0)
    208 				errx(1, "%d: number is invalid\n", t);
    209 			nflag = 1;
    210 			break;
    211 		case 'p':
    212 			if ((p = atoi(optarg)) <= 0)
    213 				errx(1, "%d: number is invalid\n", p);
    214 			pflag = 1;
    215 			break;
    216 		case '?':
    217 		default:
    218 			usage();
    219 		}
    220 	}
    221 	argc -= optind;
    222 	argv += optind;
    223 
    224 	if ((cflag && nflag) || (nflag && (argc > 0)))
    225 		usage();
    226 
    227 	if (cflag)
    228 		t = argc;
    229 
    230 	if (!(cflag || nflag)) {
    231 
    232 		if (argc > 1)
    233 			usage();
    234 		if (argc == 1) {
    235 			if ((input = fopen(argv[0], "r")) == NULL)
    236 				err(1, "could not open %s", argv[0]);
    237 		}
    238 		else {
    239 			input = stdin;
    240 		}
    241 
    242 		swallow_input(input);
    243 		t = global_inputlen;
    244 	}
    245 
    246 	gettimeofday(&tv, NULL);
    247 	srandom(getpid() ^ ~getuid() ^ tv.tv_sec ^ tv.tv_usec);
    248 
    249 	shuffle = get_shuffle(t);
    250 
    251 	if (pflag) {
    252 		if (p > t)
    253 			errx(1, "-p specified more components than exist.");
    254 		t = p;
    255 	}
    256 
    257 	for (i = 0; i < t; i++) {
    258 		if (nflag)
    259 			printf("%d\n", shuffle[i]);
    260 		else if (cflag)
    261 			printf("%s\n", argv[shuffle[i]]);
    262 		else
    263 			printf("%s", global_inputbuf[shuffle[i]]);
    264 	}
    265 
    266 	return(0);
    267 }
    268