2 # $MirOS: contrib/code/Snippets/shuffle,v 1.7+urandom 2012/10/19 18:59:39 tg Exp $
4 # Copyright © 2006, 2010, 2011, 2012
5 # Thorsten “mirabilos” Glaser <tg@mirbsd.org>
7 # Provided that these terms and disclaimer and all copyright notices
8 # are retained or reproduced in an accompanying document, permission
9 # is granted to deal in this work without restriction, including un‐
10 # limited rights to use, publicly perform, distribute, sell, modify,
11 # merge, give away, or sublicence.
13 # This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
14 # the utmost extent permitted by applicable law, neither express nor
15 # implied; without malicious intent or gross negligence. In no event
16 # may a licensor, author or contributor be held liable for indirect,
17 # direct, other damage, loss, or other issues arising in any way out
18 # of dealing in the work, even if advised of the possibility of such
19 # damage or existence of a defect, except proven that it results out
20 # of said person’s immediate fault when using the work as intended.
22 # Provide shuffled input to mpg123, mplayer, mppdec, and other tools
23 # that do not properly employ arc4random(3) et al.
26 print -u2 "Syntax: $0 [-v] program [args ...] -- file ... [-- args ...]"
27 print -u2 "Prepend another hyphen-minus to escape multi-dash first-args"
31 [[ -z $1 || $1 = -[hH?] ]] && usage ${1:+0}
33 if [[ $1 = -v ]]; then
49 # Read in command line arguments
52 (0) if [[ $arg = -- ]]; then
54 elif [[ $arg = --+(-) ]]; then
55 cmdline[ncmdline++]=${arg#-}
57 cmdline[ncmdline++]=$arg
60 (1) if [[ $arg = -- ]]; then
66 (2) postfix[npostfix++]=$arg
71 (( ncmdline < 1 || nfiles < 1 )) && usage
74 # arc4random(3) in Pure mksh™
75 set -A seedbuf -- $(dd if=/dev/urandom bs=257 count=1 2>&- | \
76 hexdump -ve '1/1 "0x%02X "')
78 typeset -i rs_S rs_i=-1 rs_j=0 n
79 while (( ++rs_i < 256 )); do
80 (( rs_S[rs_i] = rs_i ))
83 while (( ++rs_i < 256 )); do
85 (( rs_j = (rs_j + n + seedbuf[rs_i]) & 0xFF ))
86 (( rs_S[rs_i] = rs_S[rs_j] ))
92 function arcfour_byte {
95 (( rs_i = (rs_i + 1) & 0xFF ))
97 (( rs_j = (rs_j + si) & 0xFF ))
100 (( rs_S[rs_j] = si ))
101 (( rs_out = rs_S[(si + sj) & 0xFF] ))
103 (( n = 256 * 12 + seedbuf[256] + (RANDOM & 0xFF) ))
112 typeset -Uui16 -Z11 arc4random_rv
113 function arc4random {
116 (( rs_out & 1 )) && arcfour_byte
117 # read four octets into result dword
119 (( arc4random_rv = rs_out ))
121 (( arc4random_rv |= rs_out << 8 ))
123 (( arc4random_rv |= rs_out << 16 ))
125 (( arc4random_rv |= rs_out << 24 ))
128 # arc4random_uniform(3) in Pure mksh™
129 function arc4random_uniform {
130 # Derived from code written by Damien Miller <djm@openbsd.org>
131 # published under the ISC licence, with simplifications by
132 # Jinmei Tatuya. Written in mksh by Thorsten Glaser.
134 # Calculate a uniformly distributed random number less than
135 # upper_bound avoiding “modulo bias”.
136 # Uniformity is achieved by generating new random numbers
137 # until the one returned is outside the range
138 # [0, 2^32 % upper_bound[. This guarantees the selected
139 # random number will be inside the range
140 # [2^32 % upper_bound, 2^32[ which maps back to
141 # [0, upper_bound[ after reduction modulo upper_bound.
143 typeset -Ui upper_bound=$1 min
145 if (( upper_bound < 2 )); then
150 # calculate (2^32 % upper_bound) avoiding 64-bit math
151 # if upper_bound > 2^31: 2^32 - upper_bound (only one
152 # “value area”); otherwise (x <= 2^31) use the fact
153 # that ((2^32 - x) % x) == (2^32 % x)
154 ((# min = upper_bound > 0x80000000 ? 1 + ~upper_bound :
155 (0xFFFFFFFF - upper_bound + 1) % upper_bound ))
157 # This could theoretically loop forever but each retry has
158 # p > 0.5 (worst case, usually far better) of selecting a
159 # number inside the range we need, so it should rarely need
160 # to re-roll (at all).
163 ((# arc4random_rv >= min )) && break
166 ((# arc4random_rv %= upper_bound ))
170 # Append shuffled list of files
172 while (( --nfiles )); do
173 arc4random_uniform $nfiles
174 arg=${files[arc4random_rv]}
175 if [[ -e $arg ]]; then
176 cmdline[ncmdline++]=$arg
178 print -u2 "Warning: non-existent '$arg' skipped"
180 unset files[arc4random_rv]
181 set -A files -- "${files[@]}"
184 # Append post-command arguments, if any
185 for arg in "${postfix[@]}"; do
186 cmdline[ncmdline++]=$arg
190 if (( verbose )); then
191 C_QUOTE=$'[]\t\n "#$&'\''()*;<=>?[\\`|]'
192 for i in "${cmdline[@]}"; do
193 if [[ $i = *${C_QUOTE}* ]]; then
194 print -nr " '${i//\'/\'\\\'\'}'"