shuffle arguments around
authorThorsten Glaser <tg@mirbsd.org>
Wed, 9 Mar 2011 14:27:01 +0000 (15:27 +0100)
committerThorsten Glaser <tg@mirbsd.org>
Wed, 9 Mar 2011 14:27:01 +0000 (15:27 +0100)
usage: mksh shuffle arg1a arg1b … -- arg2a arg2b … [-- arg3a arg3b …]
will run: arg1a arg1b arg2b arg2a arg3a arg3b
i.e. shuffle arg2 but leave arg1 and arg3 alone

examples:
mksh shuffle mplayer -- *.flac
mksh shuffle mppdec-static -- *.mpc -- /dev/dsp

mksh/shuffle [new file with mode: 0644]

diff --git a/mksh/shuffle b/mksh/shuffle
new file mode 100644 (file)
index 0000000..3cec229
--- /dev/null
@@ -0,0 +1,182 @@
+#!/usr/bin/env mksh
+# $MirOS: contrib/code/Snippets/shuffle,v 1.5 2010/11/06 14:59:20 tg Exp $
+#-
+# Copyright © 2006, 2010
+#      Thorsten “mirabilos” Glaser <tg@mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un‐
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person’s immediate fault when using the work as intended.
+#-
+# Provide shuffled input to mpg123, mplayer, mppdec, and other tools
+# that do not properly employ arc4random(3) et al.
+
+function usage {
+       print -u2 "Syntax: $0 program [args ...] -- file ... [ -- args ... ]"
+       print -u2 "To escape '--' in first args prepend another hyphen-minus"
+       exit 1
+}
+
+[[ -z $1 || $1 = -@(h|H|?) ]] && usage
+
+set -A cmdline
+set -A files
+set -A postfix
+
+integer ncmdline=0
+integer nfiles=0
+integer npostfix=0
+integer state=0
+
+# Read in command line arguments
+for arg in "$@"; do
+       case $state {
+       (0)     if [[ $arg = -- ]]; then
+                       let state++
+               elif [[ $arg = --- ]]; then
+                       cmdline[ncmdline++]=--
+               else
+                       cmdline[ncmdline++]=$arg
+               fi
+               ;;
+       (1)     if [[ $arg = -- ]]; then
+                       let state++
+               else
+                       files[nfiles++]=$arg
+               fi
+               ;;
+       (2)     postfix[npostfix++]=$arg
+               ;;
+       }
+done
+
+(( ncmdline < 1 || nfiles < 1 )) && usage
+
+
+# arc4random(3) and arc4random_uniform(3) in Pure mksh™
+set -A seedbuf -- $(dd if=/dev/arandom bs=257 count=1 2>&- | \
+    hexdump -ve '1/1 "0x%02X "')
+set -A rs_S
+typeset -i rs_S rs_i=-1 rs_j=0 n
+while (( ++rs_i < 256 )); do
+       (( rs_S[rs_i] = rs_i ))
+done
+rs_i=-1
+while (( ++rs_i < 256 )); do
+       (( n = rs_S[rs_i] ))
+       (( rs_j = (rs_j + n + seedbuf[rs_i]) & 0xFF ))
+       (( rs_S[rs_i] = rs_S[rs_j] ))
+       (( rs_S[rs_j] = n ))
+done
+rs_i=0
+rs_j=0
+typeset -i rs_out
+function arcfour_byte {
+       typeset -i si sj
+
+       (( rs_i = (rs_i + 1) & 0xFF ))
+       (( si = rs_S[rs_i] ))
+       (( rs_j = (rs_j + si) & 0xFF ))
+       (( sj = rs_S[rs_j] ))
+       (( rs_S[rs_i] = sj ))
+       (( rs_S[rs_j] = si ))
+       (( rs_out = rs_S[(si + sj) & 0xFF] ))
+}
+(( n = 1024 + seedbuf[256] + (RANDOM & 0xFF) ))
+while (( n-- )); do
+       arcfour_byte
+done
+(( n = rs_out ))
+while (( n-- )); do
+       arcfour_byte
+done
+
+typeset -Uui16 -Z11 arc4random_rv
+function arc4random {
+       # apply uncertainty
+       arcfour_byte
+       (( rs_out & 1 )) && arcfour_byte
+       # read four octets into result dword
+       arcfour_byte
+       (( arc4random_rv = rs_out ))
+       arcfour_byte
+       (( arc4random_rv |= rs_out << 8 ))
+       arcfour_byte
+       (( arc4random_rv |= rs_out << 16 ))
+       arcfour_byte
+       (( arc4random_rv |= rs_out << 24 ))
+}
+
+function arc4random_uniform {
+       # Derived from code written by Damien Miller <djm@openbsd.org>
+       # published under the ISC licence, with simplifications by
+       # Jinmei Tatuya. Written in mksh by Thorsten Glaser.
+       #-
+       # Calculate a uniformly distributed random number less than
+       # upper_bound avoiding “modulo bias”.
+       # Uniformity is achieved by generating new random numbers
+       # until the one returned is outside the range
+       # [0, 2^32 % upper_bound[. This guarantees the selected
+       # random number will be inside the range
+       # [2^32 % upper_bound, 2^32[ which maps back to
+       # [0, upper_bound[ after reduction modulo upper_bound.
+       #-
+       typeset -Ui upper_bound=$1 min
+
+       if (( upper_bound < 2 )); then
+               arc4random_rv=0
+               return
+       fi
+
+       # calculate (2^32 % upper_bound) avoiding 64-bit math
+       # if upper_bound > 2^31: 2^32 - upper_bound (only one
+       # “value area”); otherwise (x <= 2^31) use the fact
+       # that ((2^32 - x) % x) == (2^32 % x)
+       ((# min = upper_bound > 0x80000000 ? 1 + ~upper_bound :
+           (0xFFFFFFFF - upper_bound + 1) % upper_bound ))
+
+       # This could theoretically loop forever but each retry has
+       # p > 0.5 (worst case, usually far better) of selecting a
+       # number inside the range we need, so it should rarely need
+       # to re-roll (at all).
+       while :; do
+               arc4random
+               ((# arc4random_rv >= min )) && break
+       done
+
+       ((# arc4random_rv %= upper_bound ))
+}
+
+
+# Append shuffled list of files
+(( ++nfiles ))
+while (( --nfiles )); do
+       arc4random_uniform $nfiles
+       arg=${files[arc4random_rv]}
+       if [[ -e $arg ]]; then
+               cmdline[ncmdline++]=$arg
+       else
+               print -u2 "Warning: non-existent '$arg' skipped"
+       fi
+       unset files[arc4random_rv]
+       set -A files -- "${files[@]}"
+done
+
+# Append post-command arguments, if any
+for arg in "${postfix[@]}"; do
+       cmdline[${#cmdline[*]}]=$arg
+done
+
+# Run the command
+exec "${cmdline[@]}"