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