1 1.22 dholland /* $NetBSD: shuffle.c,v 1.22 2017/02/06 02:26:44 dholland Exp $ */ 2 1.1 perry 3 1.1 perry /* 4 1.1 perry * Copyright (c) 1998 5 1.1 perry * Perry E. Metzger. All rights reserved. 6 1.1 perry * 7 1.1 perry * Redistribution and use in source and binary forms, with or without 8 1.1 perry * modification, are permitted provided that the following conditions 9 1.1 perry * are met: 10 1.1 perry * 1. Redistributions of source code must retain the above copyright 11 1.1 perry * notice, this list of conditions and the following disclaimer. 12 1.1 perry * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 perry * notice, this list of conditions and the following disclaimer in the 14 1.1 perry * documentation and/or other materials provided with the distribution. 15 1.1 perry * 3. All advertising materials mentioning features or use of this software 16 1.1 perry * must display the following acknowledgment: 17 1.1 perry * This product includes software developed for the NetBSD Project 18 1.1 perry * by Perry E. Metzger. 19 1.1 perry * 4. The name of the author may not be used to endorse or promote products 20 1.1 perry * derived from this software without specific prior written permission. 21 1.1 perry * 22 1.1 perry * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 1.1 perry * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 1.1 perry * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 1.1 perry * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 1.1 perry * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 1.1 perry * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 1.1 perry * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 1.1 perry * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 1.1 perry * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 1.1 perry * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 1.1 perry */ 33 1.1 perry 34 1.1 perry #include <sys/cdefs.h> 35 1.1 perry #ifndef lint 36 1.22 dholland __RCSID("$NetBSD: shuffle.c,v 1.22 2017/02/06 02:26:44 dholland Exp $"); 37 1.1 perry #endif /* not lint */ 38 1.1 perry 39 1.11 simonb #include <sys/time.h> 40 1.11 simonb 41 1.1 perry #include <err.h> 42 1.11 simonb #include <errno.h> 43 1.11 simonb #include <limits.h> 44 1.1 perry #include <stdio.h> 45 1.1 perry #include <stdlib.h> 46 1.1 perry #include <string.h> 47 1.1 perry #include <unistd.h> 48 1.19 christos #include <util.h> 49 1.10 perry 50 1.10 perry static size_t *get_shuffle(size_t); 51 1.21 joerg __dead static void usage(void); 52 1.10 perry static void get_lines(const char *, char ***, size_t *); 53 1.10 perry static size_t get_number(const char *, int); 54 1.2 perry 55 1.1 perry /* 56 1.3 christos * get_shuffle -- 57 1.3 christos * Construct a random shuffle array of t elements 58 1.3 christos */ 59 1.3 christos static size_t * 60 1.10 perry get_shuffle(size_t t) 61 1.1 perry { 62 1.3 christos size_t *shuffle; 63 1.3 christos size_t i, j, k, temp; 64 1.1 perry 65 1.3 christos shuffle = emalloc(t * sizeof(size_t)); 66 1.1 perry 67 1.1 perry for (i = 0; i < t; i++) 68 1.1 perry shuffle[i] = i; 69 1.1 perry 70 1.1 perry /* 71 1.1 perry * This algorithm taken from Knuth, Seminumerical Algorithms, 72 1.18 perry * 2nd Ed., page 139. 73 1.1 perry */ 74 1.1 perry 75 1.3 christos for (j = t - 1; j > 0; j--) { 76 1.22 dholland k = arc4random_uniform(j + 1); 77 1.1 perry temp = shuffle[j]; 78 1.1 perry shuffle[j] = shuffle[k]; 79 1.1 perry shuffle[k] = temp; 80 1.1 perry } 81 1.1 perry 82 1.3 christos return shuffle; 83 1.1 perry } 84 1.1 perry 85 1.3 christos /* 86 1.3 christos * usage -- 87 1.3 christos * Print a usage message and exit 88 1.3 christos */ 89 1.3 christos static void 90 1.10 perry usage(void) 91 1.1 perry { 92 1.8 cgd 93 1.3 christos (void) fprintf(stderr, 94 1.15 jmmv "usage: %s [-0] [-f <filename>] [-n <number>] [-p <number>] [<arg> ...]\n", 95 1.8 cgd getprogname()); 96 1.3 christos exit(1); 97 1.1 perry } 98 1.1 perry 99 1.3 christos 100 1.3 christos /* 101 1.3 christos * get_lines -- 102 1.3 christos * Return an array of lines read from input 103 1.3 christos */ 104 1.3 christos static void 105 1.10 perry get_lines(const char *fname, char ***linesp, size_t *nlinesp) 106 1.1 perry { 107 1.3 christos FILE *fp; 108 1.3 christos char *line; 109 1.9 mycroft size_t size, nlines = 0, maxlines = 128; 110 1.3 christos char **lines = emalloc(sizeof(char *) * maxlines); 111 1.3 christos 112 1.3 christos if (strcmp(fname, "-") == 0) 113 1.3 christos fp = stdin; 114 1.3 christos else 115 1.3 christos if ((fp = fopen(fname, "r")) == NULL) 116 1.3 christos err(1, "Cannot open `%s'", fname); 117 1.3 christos 118 1.3 christos while ((line = fgetln(fp, &size)) != NULL) { 119 1.3 christos if (size > 0 && line[size - 1] == '\n') 120 1.3 christos size--; 121 1.3 christos lines[nlines] = emalloc(size + 1); 122 1.3 christos (void)memcpy(lines[nlines], line, size); 123 1.3 christos lines[nlines++][size] = '\0'; 124 1.3 christos if (nlines >= maxlines) { 125 1.9 mycroft maxlines *= 2; 126 1.3 christos lines = erealloc(lines, (sizeof(char *) * maxlines)); 127 1.1 perry } 128 1.1 perry } 129 1.3 christos lines[nlines] = NULL; 130 1.1 perry 131 1.3 christos *linesp = lines; 132 1.3 christos *nlinesp = nlines; 133 1.3 christos if (strcmp(fname, "-") != 0) 134 1.3 christos (void)fclose(fp); 135 1.3 christos } 136 1.1 perry 137 1.3 christos /* 138 1.3 christos * get_number -- 139 1.3 christos * Return a number or exit on error 140 1.3 christos */ 141 1.3 christos static size_t 142 1.10 perry get_number(const char *str, int ch) 143 1.3 christos { 144 1.3 christos char *estr; 145 1.12 lukem long number; 146 1.3 christos 147 1.12 lukem errno = 0; 148 1.12 lukem number = strtol(str, &estr, 0); 149 1.3 christos if ((number == LONG_MIN || number == LONG_MAX) && errno == ERANGE) 150 1.3 christos err(1, "bad -%c argument `%s'", ch, str); 151 1.3 christos if (*estr) 152 1.3 christos errx(1, "non numeric -%c argument `%s'", ch, str); 153 1.3 christos if (number < 0) 154 1.3 christos errx(1, "negative -%c argument `%s'", ch, str); 155 1.3 christos return (size_t) number; 156 1.1 perry } 157 1.1 perry 158 1.1 perry int 159 1.10 perry main(int argc, char *argv[]) 160 1.1 perry { 161 1.20 lukem int nflag = 0, pflag = 0, ch; 162 1.3 christos char *fname = NULL; 163 1.6 fair size_t *shuffle = NULL; 164 1.3 christos char **lines = NULL; 165 1.20 lukem size_t nlines = 0, pick = 0, i; 166 1.13 christos char sep = '\n'; 167 1.2 perry 168 1.16 cube while ((ch = getopt(argc, argv, "0f:n:p:")) != -1) { 169 1.1 perry switch(ch) { 170 1.13 christos case '0': 171 1.13 christos sep = '\0'; 172 1.13 christos break; 173 1.3 christos case 'f': 174 1.3 christos fname = optarg; 175 1.1 perry break; 176 1.1 perry case 'n': 177 1.3 christos nlines = get_number(optarg, ch); 178 1.1 perry nflag = 1; 179 1.1 perry break; 180 1.1 perry case 'p': 181 1.3 christos pick = get_number(optarg, ch); 182 1.1 perry pflag = 1; 183 1.1 perry break; 184 1.1 perry case '?': 185 1.1 perry default: 186 1.1 perry usage(); 187 1.1 perry } 188 1.1 perry } 189 1.1 perry argc -= optind; 190 1.1 perry argv += optind; 191 1.1 perry 192 1.3 christos if ((fname && nflag) || (nflag && (argc > 0))) 193 1.1 perry usage(); 194 1.1 perry 195 1.3 christos if (fname != NULL) 196 1.3 christos get_lines(fname, &lines, &nlines); 197 1.3 christos else if (nflag == 0) { 198 1.3 christos lines = argv; 199 1.3 christos nlines = argc; 200 1.1 perry } 201 1.1 perry 202 1.5 perry if (nlines > 0) 203 1.5 perry shuffle = get_shuffle(nlines); 204 1.1 perry 205 1.1 perry if (pflag) { 206 1.3 christos if (pick > nlines) 207 1.1 perry errx(1, "-p specified more components than exist."); 208 1.3 christos nlines = pick; 209 1.1 perry } 210 1.1 perry 211 1.3 christos for (i = 0; i < nlines; i++) { 212 1.1 perry if (nflag) 213 1.14 christos printf("%ld", (long)shuffle[i]); 214 1.1 perry else 215 1.14 christos printf("%s", lines[shuffle[i]]); 216 1.14 christos putc(sep, stdout); 217 1.1 perry } 218 1.1 perry 219 1.3 christos return 0; 220 1.1 perry } 221