# Copyright (c) 2017 The NetBSD Foundation, Inc. # All rights reserved. # # This code is derived from software contributed to The NetBSD Foundation # by Johnny C. Lam. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # NAME # random.subr -- generate random number (badly) # # SYNOPSIS # task_random [-i []] # # echo $RANDOM # # DESCRIPTION # The task_random function sets RANDOM to a random integer from the # range 0..32767. It should always be called before using the $RANDOM # value. The seed can be explicitly set in order to ensure a # deterministic sequence of $RANDOM values. # # The options are as follows: # # -i # Explicitly initialize with a new seed. If the seed is not # given, then a new seed is chosen based on the current time. # # If the shell already has a built-in implementation for $RANDOM, # then the task_random function simply returns 0. # # RETURN VALUES # The task_random function returns 0 on success, and >0 if an error # occurs. # # ENVIRONMENT # The following variables are used if they are set: # # AWK The name or path to the awk(1) utility. # # DATE The name or path to the date(1) utility. # # EXAMPLE # for i in 1 2 3 4 5; do # task_random && echo $RANDOM # done # # BUGS # The pseudorandom number sequence will eventually enter a cycle of # length 209, so task_random should periodically reseeded if a long # sequence of random numbers is needed. # __task_random__="yes" __task_random_init__="_random_init" task_random() { : ${AWK:=awk} : ${DATE:=date} local randomseed= local arg local OPTIND=1 while getopts ":i:" arg "$@"; do case $arg in i) randomseed=${OPTARG} ;; *) return 127 ;; esac done shift $(( ${OPTIND} - 1 )) [ $# -eq 0 ] || return 127 if [ -n "$__task_random_exists__" ]; then # # If the shell already has a $RANDOM implementation, then # setting RANDOM to a value initializes the seed for the # random sequence. # [ -z "$randomseed" ] || RANDOM=$randomseed return 0 fi local randomseed_init= if [ -n "$randomseed" ]; then # # This function was called with an explicit seed, so the # next time this function is called without an argument, # a new seed must be generated. # randomseed_init="yes" elif [ -z "$randomseed_init" ]; then # # If a random seed has not been initialized, then set the # seed to the time prepended with 1 and appended with a # process PID. This ensures the it is a number not # beginning with zero and that it differs between multiple # invocations of this function within a short span of time. # randomseed_init="yes" local randomseed_pid ( : ) & randomseed_pid=$! wait $randomseed_pid randomseed=$( ${DATE} +"1%H%M%S$randomseed_pid" ) else # A random seed was previously generated, so just use # $RANDOM as the next seed. # randomseed=$RANDOM fi # Use awk's rand() to generate random numbers; however, the PRNG # for awk needs to be seeded appropriately between invocations. # RANDOM=$( ${AWK} -v SEED=$randomseed -v RAND_MAX=32768 \ 'BEGIN { srand(SEED); print int( RAND_MAX * rand() ) } ') } _random_init() { # Test for the presence of a $RANDOM implementation when this file is # loaded, so that entropy is not wasted. # case $RANDOM in $RANDOM) __task_random_exists__= ;; *) __task_random_exists__="yes" ;; esac } # Static variable for whether a builtin $RANDOM exists. __task_random_exists__=