/var/lib/sorcery/modules/libmisc

     1	#!/bin/bash
     2	
     3	#---------------------------------------------------------------------
     4	## @param string to explode
     5	## @param delimiter
     6	## @param name of array to put it in
     7	##
     8	## Turns a string into an array. Each element of the array
     9	## is separated in the string by the delimiter.
    10	##
    11	## Note: The array you want the fields put into must be
    12	## declared before you call this function.
    13	## <pre>
    14	## EXAMPLE
    15	##
    16	## my_array=()
    17	## explode "a_string_to_explode" "_" "my_array"
    18	## echo my_array[*]
    19	##
    20	## Produces "a" "string" "to" "explode".
    21	## </pre>
    22	#---------------------------------------------------------------------
    23	function explode()
    24	{ # $1==string to explode, $2==delimiter, $3==name of array to put it in
    25	
    26	  [[ "$3" ]] || return 1
    27	  local l=$1
    28	  local i=0
    29	  while [[ $l ]]; do
    30	    local result=${l%%$2*}
    31	    l=${l#"$result"}
    32	    l=${l#$2}
    33	    eval "$3[$i]=\"\$result\""
    34	    ((i++))
    35	  done
    36	  # this adds an empty array element at the end if the line ended in $2
    37	  local lc=${1//\*$2/true}
    38	  if [ "$lc" = "true" ]; then
    39	    eval "$3[$i]=\"\""
    40	  fi
    41	}
    42	
    43	
    44	#---------------------------------------------------------------------
    45	## @Type API
    46	## @param sed command
    47	## @param file
    48	##
    49	## First argument is a sed command.  Second argument is a file.
    50	## sedit performs the sed command on the file, modifiying the
    51	## original file.  For example,
    52	## <br>sedit "s/foo/bar/g" /tmp/somefile <br>
    53	## will replace all occurances of foo with bar in /tmp/somefile.
    54	## This function is often used in spells to make changes to source
    55	## files before compiling.  See the sed man page for more information.
    56	##
    57	#---------------------------------------------------------------------
    58	function real_sedit()  {
    59	  sed -i "$1" "$2"
    60	}
    61	
    62	#---------------------------------------------------------------------
    63	## @Type API
    64	## @param string to check
    65	## @return 0 if true
    66	## @return 1 if false
    67	##
    68	## Argument is a string to check if the string contains all digits or
    69	## not
    70	#--------------------------------------------------------------------
    71	function isdigit() {
    72	  echo $1 | grep '[[:digit:]]+' >> /dev/null 2>&1
    73	}
    74	
    75	#--------------------------------------------------------------------
    76	## @Type API
    77	## @param string to check
    78	## @return 0 if true
    79	## @return 1 if false
    80	##
    81	## Argument is a string to check if it contains all chars a-zA-Z
    82	#--------------------------------------------------------------------
    83	function isalpha() {
    84	  echo $1 | grep '[[:alpha:]]+' >> /dev/null 2>&1
    85	}
    86	
    87	#---------------------------------------------------------------------
    88	## @Type API
    89	## @param question
    90	## @param default answer
    91	##
    92	## @return 0 on yes
    93	## @return 1 on no
    94	##
    95	## Asks the user a yes/no question.  First argument is the question to
    96	## ask, second argument is the default answer.  If a timeout occurs
    97	## before the question is answered, the given default answer is
    98	## applied. Pressing spacebar or enter applies the default answer
    99	## immediatelly without waiting for timeout.
   100	## Returns true or false based on the answer given.
   101	##
   102	#---------------------------------------------------------------------
   103	function real_query()  {
   104	  debug "libmisc" "Running query() with the following arguments: '$1' and '$2'"
   105	  local _response
   106	
   107	  while true; do
   108	    _response=""
   109	
   110	    if [[ -z $SILENT ]]; then
   111	      echo -e -n "${QUERY_COLOR}$1 [$2] ${DEFAULT_COLOR}"
   112	
   113	      read -t $PROMPT_DELAY -n 1 _response
   114	      echo
   115	    fi
   116	
   117	    _response=${_response:=$2}
   118	    case  $_response in
   119	      n|N) return 1 ;;
   120	      y|Y) return 0 ;;
   121	    esac
   122	  done
   123	}
   124	
   125	#---------------------------------------------------------------------
   126	## @param return_var
   127	## @param question
   128	## @param default answer
   129	##
   130	## @return 0 user supplied answer
   131	## @return 1 default answer is used
   132	##
   133	## Asks user for string, with default answer and timeout (like query)
   134	##
   135	#---------------------------------------------------------------------
   136	function real_query_string() {
   137	
   138	    debug  "libmisc" "Running question() with the following arguments: '$1' and '$2'"
   139	
   140	    local RESPONSE=""
   141	    local RETURN=0
   142	    local ANSWER_first ANSWER_rest
   143	
   144	    local DEFAULT=""
   145	    [ -n "$3" ] && DEFAULT=" [$3] "
   146	
   147	    if [ -z "$SILENT" ]; then
   148	        echo -e -n "${QUERY_COLOR}$2${DEFAULT}${DEFAULT_COLOR}"
   149	        read -t $PROMPT_DELAY -n 1 ANSWER_first
   150	        if [[ $ANSWER_first ]] ; then
   151	          read -e ANSWER_rest
   152	        fi
   153	        RESPONSE="${ANSWER_first}${ANSWER_rest}"
   154	        echo
   155	    fi
   156	
   157	    [ -z "$RESPONSE" ] && RETURN=1 && RESPONSE="$3"
   158	
   159	    eval $1=\"\${RESPONSE}\"
   160	    return $RETURN
   161	}
   162	
   163	
   164	
   165	#---------------------------------------------------------------------
   166	## @Type API
   167	## @param message to echo
   168	## @Stdout message
   169	## echo's the given arguments if SILENT is not set.
   170	#---------------------------------------------------------------------
   171	function real_message()    {
   172	  if  [  !  -n  "$SILENT"  ];  then
   173	    if  [  "$1"  ==  "-n"  ];  then
   174	      shift  1
   175	      echo  -n  -e  "$*"
   176	    else
   177	      echo  -e  "$*"
   178	    fi
   179	  fi
   180	
   181	}
   182	
   183	#---------------------------------------------------------------------
   184	## @Type Private
   185	## @param error message to echo
   186	## @Stdout message
   187	## @Stderr message
   188	## echo's the given arguments if SILENT is not set.
   189	#---------------------------------------------------------------------
   190	function error_message() {
   191	  real_message "$@" 1>&2
   192	}
   193	
   194	#---------------------------------------------------------------------
   195	## @param type
   196	## @param message
   197	##
   198	## Enters a debug message if the type of debug message != 'no'
   199	##
   200	## The 'type' is usually the name of the file the debug statement
   201	## comes from. i.e. DEBUG_liblock, DEBUG_libsorcery, DEBUG_cast etc.
   202	##
   203	#---------------------------------------------------------------------
   204	function debug()  {
   205	
   206	  [[ $DEBUG ]] || return 0
   207	
   208	  local debugVar="DEBUG_${1}"
   209	  local i
   210	  if [[ ${!debugVar} != "no" ]] ; then
   211	    echo -n "$1($$): " >>$DEBUG
   212	    shift
   213	    for i in "$@" ; do
   214	      echo -n " \"$i\"" >>$DEBUG
   215	    done
   216	    echo >>$DEBUG
   217	  fi
   218	
   219	  true
   220	}
   221	
   222	#---------------------------------------------------------------------
   223	## @Stdout progress spinner
   224	## Displays progress spinner like the one in fsck.
   225	##
   226	#---------------------------------------------------------------------
   227	function progress_spinner()  {
   228	  let  PROGRESS_SPINNER=$PROGRESS_SPINNER+1
   229	  if ((  PROGRESS_SPINNER > ${#PROGRESS_SPINNER_CHARS}-1  )); then
   230	    PROGRESS_SPINNER=0;
   231	  fi
   232	  echo -en "\b${PROGRESS_SPINNER_CHARS:$PROGRESS_SPINNER:1}"
   233	}
   234	
   235	
   236	#---------------------------------------------------------------------
   237	## @param opt "dot"
   238	## @param count
   239	## @param total
   240	## @param opt length
   241	## <pre>
   242	## Displays progress bar in the form of [---->   ] 100%
   243	## Or just a dot in the dot format
   244	##</pre>
   245	#---------------------------------------------------------------------
   246	function progress_bar()  {
   247	debug "libmisc" "progress_bar - $*"
   248	  local percent i len num_dash
   249	
   250	  if [[ $1 == -dot ]] ; then
   251	    len=0
   252	    shift 1
   253	  else
   254	    len=${3:-${COLUMNS:-70}}
   255	  fi
   256	
   257	  # Can't make a bar because there's no total, or the length isn't
   258	  #  long enough, or if there is no length
   259	  if [[ $# -lt 2 ]] || [[ $len -lt 8 ]]; then
   260	    message -n "."
   261	    return 0
   262	  fi
   263	
   264	  if [[ $1 -lt 1 ]] || [[ $2 -lt 1 ]]; then return 1; fi
   265	
   266	  percent=$((100*$1/$2))
   267	  percent=$(printf "%.0f" $percent)
   268	
   269	  if [[ $LAST_PERCENT == $percent ]] ; then
   270	    progress_spinner
   271	    return 0
   272	  fi
   273	
   274	  LAST_PERCENT=$percent
   275	
   276	
   277	  dash_len=$(($len-8))
   278	  num_dash=$(( $dash_len*$1/$2 ))
   279	
   280	  #Format: [--->  ] 100%
   281	
   282	  #opening of the bar
   283	  BAR_STRING="["
   284	
   285	  #The '=' signs
   286	  for (( i=0 ; i<$num_dash ; i++ )) ; do
   287	    BAR_STRING="${BAR_STRING}="
   288	  done
   289	
   290	  #Now a pretty indicator
   291	  [ $num_dash -lt $((dash_len-1)) ] && BAR_STRING="${BAR_STRING}>"
   292	
   293	  #Fill the rest of the bar up with blanks
   294	  for (( i++ ; i<$dash_len-1 ; i++ )) ; do
   295	    BAR_STRING="${BAR_STRING} "
   296	  done
   297	
   298	  #Put on the closing for the bar and the percent
   299	  BAR_STRING="${BAR_STRING}] ${percent}%"
   300	
   301	  #Clear the rest of the space
   302	  for (( i+=3+${#percent} ; i<$len ; i++ )) ; do
   303	    BAR_STRING="${BAR_STRING} "
   304	  done
   305	
   306	  clear_line
   307	  message -n "$BAR_STRING"
   308	  progress_spinner
   309	
   310	 return 0
   311	
   312	
   313	}
   314	#---------------------------------------------------------------------
   315	## @Stdout Clear line
   316	## Clears the current line and puts the cursor at the begining of it.
   317	##
   318	#---------------------------------------------------------------------
   319	function clear_line()  {
   320	
   321	  echo -en '\r\033[2K'
   322	  return 0
   323	
   324	}
   325	
   326	
   327	#---------------------------------------------------------------------
   328	##
   329	## @Globals SOUND
   330	## Plays a given soundfile if sounds are on and the file exists in
   331	## the chosen theme.
   332	##
   333	#---------------------------------------------------------------------
   334	function sound()  {
   335	
   336	  case  $SOUND  in
   337	    on)  SOUND_FILE=$SOUND_DIRECTORY/$SOUND_THEME/$1
   338	         if  [  -e  $SOUND_FILE  ];  then
   339	           (  cd  /  ;  play  $SOUND_FILE  2>/dev/null  & )
   340	           debug "libmisc" "Playing $SOUND_FILE"
   341	         else
   342	           debug "libmisc" "Error playing $SOUND_FILE: no such file"
   343	         fi
   344	    ;;
   345	  esac
   346	
   347	}
   348	
   349	
   350	#---------------------------------------------------------------------
   351	## @param filename
   352	##
   353	## Runs the editor given by EDITOR on the file given in the first
   354	## argument.  If EDITOR is not set, "nano -w" is used.
   355	##
   356	#---------------------------------------------------------------------
   357	function edit_file()  {
   358	
   359	  ${EDITOR:-nano -w} $1
   360	
   361	}
   362	
   363	
   364	#---------------------------------------------------------------------
   365	## @param string
   366	## @param (optional) upvar
   367	## @Stdout escaped string
   368	##
   369	## Adds escape sequences to strings for special characters.
   370	## Does NOT escape the string ".*".
   371	## Used for putting escape sequences in regexp strings that should
   372	## be treated literaly.
   373	##
   374	#---------------------------------------------------------------------
   375	function esc_str()
   376	{
   377	  if [[ $1 == ".*" ]]; then
   378	    if [[ -z $2 ]]; then
   379	      echo "$1"
   380	    else
   381	      upvar $2 "$1"
   382	    fi
   383	  else
   384	    local escaped="${1//./\.}"
   385	    escaped="${escaped//\*/\*}"
   386	    if [[ -z $2 ]]; then
   387	      echo "$escaped"
   388	    else
   389	      upvar $2 "$escaped"
   390	    fi
   391	  fi
   392	}
   393	
   394	#---------------------------------------------------------------------
   395	## @param string
   396	## @param upvar
   397	##
   398	## esc_str with a minor modification used for escaping provider
   399	## queries, where only the provider name needs to be escaped.
   400	## Does NOT escape strings matching "\.\*(.*".
   401	##
   402	#---------------------------------------------------------------------
   403	function esc_provider_str()
   404	{
   405	  local _escaped
   406	  # the pattern is intentionally not quoted, so glob matching occurs
   407	  if [[ $1 == .\*\(* ]]; then
   408	    esc_str "${1:2}" _escaped
   409	    _escaped=".*$_escaped"
   410	  else
   411	    _escaped="${1//./\.}"
   412	    _escaped="${_escaped//\*/\*}"
   413	  fi
   414	  upvar $2 "$_escaped"
   415	}
   416	
   417	#---------------------------------------------------------------------
   418	## Properly quotes and backquotes parameters so they can be passed
   419	## through su at the start of many sorcery commands
   420	##
   421	## Expected usage is:
   422	##
   423	##  PARAMS=$(consolidate_params "$@")
   424	##  su -c "$0 $PARAMS" root
   425	#---------------------------------------------------------------------
   426	function consolidate_params() {
   427	  local param
   428	  for param in "$@"; do
   429	    # add and remove a dummy space, so echo parameters can be used as values
   430	    echo -n "" $param | sed 's/ //; s/ /\\ /g'
   431	    echo -n " "
   432	  done
   433	}
   434	
   435	#---------------------------------------------------------------------
   436	## @Stdin directories.
   437	## @Stdout directories' basenames
   438	## Takes newline separated list of directories
   439	## and outputs their base names.
   440	##
   441	#---------------------------------------------------------------------
   442	function get_basenames() {
   443	  local directory
   444	  while read directory; do
   445	    smgl_basename "$directory"
   446	  done
   447	}
   448	
   449	
   450	#---------------------------------------------------------------------
   451	## @Stdin directories.
   452	## @Stdout directories' basenames
   453	## Takes newline separated list of pathnames
   454	## and outputs their directory names.
   455	##
   456	#---------------------------------------------------------------------
   457	function get_dirnames() {
   458	  local path_name
   459	  while read path_name; do
   460	    smgl_dirname "$path_name"
   461	  done
   462	}
   463	
   464	#---------------------------------------------------------------------
   465	## @param directory to check
   466	## @Stderr error if the input is not a directory, but something else
   467	## @return 0 on success
   468	## @return >0 on failure
   469	##
   470	## Checks whether the passed directory exists and if not, tries to
   471	## create it, first checking if it already exists as something else.
   472	#---------------------------------------------------------------------
   473	function ensure_dir() {
   474	  if ! [[ -d $1 ]]; then
   475	    if [[ -e $1 ]]; then
   476	      message "${PROBLEM_COLOR}$1 exists, but is not a directory!" \
   477	        "Please correct this and retry.${DEFAULT_COLOR}"
   478	      return 1
   479	    else
   480	      mkdir -p "$1"
   481	    fi
   482	  fi
   483	}
   484	
   485	#---------------------------------------------------------------------
   486	##  @param The function to call on each itteration and it's arguments
   487	##  @param The separator string. If there is no $3, only the first char counts
   488	##  @param The string to itterate over. (optional)
   489	##  @Stdin is used when $3 isn't given.
   490	##
   491	##  $3 is optional, when it isn't used,
   492	##  stdin is used and only the first letter in $2 is used. Note, using stdin
   493	##  can have odd side effects when your function uses read and stdin itself.
   494	##
   495	## Special vars:
   496	##  BREAK: Use this to break out of the loop prematurely, also causes a return of 1.
   497	##
   498	## If the function returns the value of the last return
   499	## Notes:
   500	##  In stdin mode, if the string does not terminate with the delimiter, the last
   501	##  token will be ignored.
   502	#----------------------------------------------------------------------
   503	function iterate()
   504	{ # $1=callback+args, $2=separator, $3=(opt)string
   505	#  debug "libmisc" "iterate - $@"
   506	
   507	  [[ $# -lt 2 ]] && return 2
   508	
   509	  local oldIFS="$IFS"
   510	  local token
   511	  local func="$1"
   512	  local returnValue=0
   513	  if [[ $# -gt 2 ]] ; then
   514	    IFS="$2"
   515	    shift 2
   516	    for token in $* ; do
   517	      IFS="$oldIFS"
   518	
   519	      eval "$func \"\$token\""
   520	      [[ $BREAK ]] && break
   521	
   522	    done
   523	    IFS="$oldIFS"
   524	  else
   525	    while read -r -d "$2" token ; do
   526	      eval "$func \"\$token\""
   527	      [[ $BREAK ]] && break
   528	    done
   529	#   debug "leftover token: $token"
   530	  fi
   531	
   532	  returnValue=$?
   533	  [[ $BREAK ]] && debug "libmisc" "iterate - I was BREAKed."
   534	  unset BREAK
   535	  return $returnValue
   536	}
   537	
   538	#---------------------------------------------------------------------
   539	## @param return_var (must not be i, foo, temp, number, returnvar, stuff, msgstr or hashname)
   540	## @param default choice
   541	## @param elements, ..
   542	##
   543	## gives the user some nice select list and puts the selected
   544	## item in return_var
   545	##
   546	#---------------------------------------------------------------------
   547	function select_list()
   548	{
   549	    local i
   550	    local foo temp number
   551	    local returnvar=$1
   552	    local _default=$2
   553	    local stuff=()
   554	    local _default_num=0
   555	
   556	    shift 2
   557	    hash_unset select_list_hash
   558	    # see note in select_provider
   559	    stuff=(0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
   560	
   561	    let i=0
   562	    for foo in "$@"; do
   563	        message "\t$DEFAULT_COLOR(${stuff[$i]})  $SPELL_COLOR$foo$DEFAULT_COLOR"
   564	        hash_put select_list_hash "${stuff[$i]}" "$foo"
   565	        [[ "$foo" ]] && [[ "$foo" == "$_default" ]] && _default_num=${stuff[$i]}
   566	        let i++
   567	    done
   568	
   569	    local msgstr="\n${QUERY_COLOR}Which one do you want? [$_default_num]$DEFAULT_COLOR "
   570	    select_list_sub "$returnvar" select_list_hash "$msgstr" "$_default_num"
   571	    hash_unset select_list_hash
   572	}
   573	
   574	#---------------------------------------------------------------------
   575	## Common code for select_list and select_provider
   576	## The user should have already printed out the menu, this handles
   577	## getting a valid answer from the user.
   578	## @param name of return value
   579	## @param name of hash table mapping answers to results
   580	## @param message to print out for the query
   581	## @param default answer
   582	#---------------------------------------------------------------------
   583	function select_list_sub() {
   584	  local returnvar=$1
   585	  local hashname=$2
   586	  local msgstr=$3
   587	  local _default=$4
   588	
   589	  local _result _answer
   590	
   591	  while [[ ! $_result ]] ; do
   592	    message -n "$msgstr"
   593	    read   -t  $PROMPT_DELAY  -n  1  _answer
   594	    [[ "$_answer" ]] || _answer=$_default
   595	    hash_get_ref $hashname $_answer _result
   596	  done
   597	  echo
   598	  eval $returnvar=\"\$_result\"
   599	}
   600	
   601	#---------------------------------------------------------------------
   602	## @param title
   603	##
   604	## sets the terminal title if TERM=xterm|rxvt or the window title if
   605	## TERM=screen
   606	##
   607	#---------------------------------------------------------------------
   608	function set_term_title()
   609	{
   610	    if [[ $SET_TERM_TITLE != on ]]; then return; fi
   611	    case $TERM in
   612	        xterm*|rxvt*)  echo -ne "\e]0;$@\007"
   613	                       ;;
   614	              screen)  echo -ne "\ek$@\e\\"
   615	                       ;;
   616	                   *) ;;
   617	    esac
   618	}
   619	
   620	
   621	#---------------------------------------------------------------------
   622	## @param return_var
   623	## @param elements, ...
   624	##
   625	## Removes from the list string(s). Strings are kept to be unique and
   626	## are separated by spaces
   627	##
   628	#---------------------------------------------------------------------
   629	function real_list_remove() {
   630	    local var=$1
   631	    shift
   632	
   633	    local i
   634	
   635	    for i in $@; do
   636	        eval "$var=\`echo \" \$$var \" | sed  \
   637	               -e \"s/[[:space:]]\\+/ /g\"   \
   638	               -e \"s/ $i / /g\"             \
   639	               -e \"s/^ *//\"                \
   640	               -e \"s/ *$//\"\`"
   641	    done
   642	}
   643	
   644	
   645	#---------------------------------------------------------------------
   646	## @param return_var
   647	## @param elements, ...
   648	##
   649	## Puts in the list string(s). Strings are kept to be unique and are
   650	## separated by spaces
   651	##
   652	#---------------------------------------------------------------------
   653	function real_list_add() {
   654	  local var=$1
   655	  shift
   656	  local i
   657	  for i in $@; do
   658	    local found=0
   659	    function list_add_sub() {
   660	      if [[ $i == $1 ]]; then
   661	        found=1
   662	      fi
   663	    }
   664	    iterate list_add_sub " " "$(eval "echo \$$var")"
   665	    if [ $found -eq 0 ]; then
   666	      eval "$var=\`echo \"\$$var $i\"\`"
   667	      eval "$var=\"\${$var/# /}\""
   668	    fi
   669	  done
   670	}
   671	
   672	
   673	#---------------------------------------------------------------------
   674	## @param string
   675	## @param elements, ...
   676	##
   677	## return 0 at least one element is in list
   678	## return 1 none of supplied elements is not in list
   679	##
   680	## Finds if at least one of the given elements is in the string. They
   681	## can be delimited by spaces, tabs or newlines. The search elements
   682	## must not contain any of these or they won't match. The matching is
   683	## exact, regular expressions and globbing patterns are not supported.
   684	##
   685	## Warning, this function takes a real string, not a variable name as
   686	## other list_* functions. Use the ${var[*]} form when passing arrays
   687	##
   688	#---------------------------------------------------------------------
   689	function real_list_find() {
   690	  local item token
   691	  [[ "$1" ]] || return 1
   692	  local input=$1
   693	  shift
   694	
   695	  for item in $input
   696	  do
   697	    for token in "$@"
   698	    do
   699	      [[ "$item" == "$token" ]] && return 0
   700	    done
   701	  done
   702	  return 1
   703	}
   704	
   705	#---------------------------------------------------------------------
   706	## @param spell
   707	## @param variable to read
   708	## @param variable name
   709	##
   710	## finds the persistent var for spell and sets variable name to the value
   711	## of the variable to read
   712	##
   713	#---------------------------------------------------------------------
   714	function real_persistent_read() {
   715	  local exfile pfile tb_dir
   716	  local varin=$2
   717	  local varout=$3
   718	  tablet_find_spell_dir $1 tb_dir  || return 2
   719	  tablet_get_spell_file $tb_dir EXPORTS exfile || return 4
   720	  tablet_get_persistent_config $tb_dir pfile || return 3
   721	  if grep -q "^${varin}$" $exfile
   722	  then
   723	    eval "$3="'$( . "$pfile" && echo "${!varin}" )'
   724	  else
   725	    return 1
   726	  fi &&
   727	  return 0
   728	}
   729	
   730	#---------------------------------------------------------------------
   731	## @param variables, ...
   732	##
   733	## Adds variable names to the list of persistent variables
   734	##
   735	#---------------------------------------------------------------------
   736	function real_persistent_add() {
   737	  if [[ $PROTECT_SORCERY ]] ; then
   738	    for each in "$@" ; do
   739	      if is_sorcery_var $each; then
   740	        # complain, but not if we're persistent loading, bug 9416
   741	        if ! [[ $PERSISTENT_LOADING ]] ; then
   742	          complain_sorcery_var $each
   743	        fi
   744	      else
   745	        list_add PERSISTENT_VARIABLES $each
   746	      fi
   747	    done
   748	  else
   749	    list_add PERSISTENT_VARIABLES "$@"
   750	  fi
   751	}
   752	
   753	
   754	#---------------------------------------------------------------------
   755	## @param variables, ...
   756	##
   757	## Removes variable names from the list of persistent variables
   758	##
   759	#---------------------------------------------------------------------
   760	function real_persistent_remove() {
   761	    list_remove PERSISTENT_VARIABLES "$@"
   762	}
   763	
   764	
   765	#---------------------------------------------------------------------
   766	## Loads persistent variables stored in file "$SPELL_CONFIG"
   767	##
   768	#---------------------------------------------------------------------
   769	function real_persistent_load() {
   770	    local PERSISTENT_LOADING=yes
   771	    local PERSISTENT_FILE=${1:-$SPELL_CONFIG.p}
   772	    lock_file "$PERSISTENT_FILE"
   773	    if [[ -f $PERSISTENT_FILE ]]; then
   774	        . "$PERSISTENT_FILE"
   775	        local line
   776	        persistent_add $(while read line; do echo ${line%%=*}; done < "$PERSISTENT_FILE")
   777	    fi
   778	    unlock_file "$PERSISTENT_FILE"
   779	}
   780	
   781	
   782	#---------------------------------------------------------------------
   783	## Saves variables marked as persistent to file "$SPELL_CONFIG". The
   784	## File is completely overwritten. Also unsets all persistent
   785	## variables
   786	##
   787	#---------------------------------------------------------------------
   788	function real_persistent_save() {
   789	    local VAR
   790	    local TMP
   791	    local file=$SPELL_CONFIG.p
   792	    local tfile
   793	    lock_start_transaction "$file" tfile
   794	    > "$tfile"
   795	    for VAR in $PERSISTENT_VARIABLES; do
   796	        config_get_option $VAR TMP
   797	        echo "$VAR=\"$TMP\"" >> "$tfile"
   798	        unset "$VAR"
   799	    done
   800	    lock_commit_transaction "$file"
   801	    unset PERSISTENT_VARIABLES
   802	}
   803	
   804	
   805	#---------------------------------------------------------------------
   806	## Unsets all persistent variables. Mainly usable as replacement of
   807	## persistent_save for functions which can be called by nonroot users
   808	## ( for example from 'gaze what' )
   809	##
   810	#---------------------------------------------------------------------
   811	function real_persistent_clear() {
   812	    local VAR
   813	    for VAR in $PERSISTENT_VARIABLES; do
   814	        unset "$VAR"
   815	    done
   816	    unset PERSISTENT_VARIABLES
   817	}
   818	
   819	
   820	
   821	#---------------------------------------------------------------------
   822	## @param config file variable
   823	## @param return variable (optional)
   824	##
   825	## @return 1 option is not present in the config
   826	## @return 0 option is present in the config (even if the option is empty string "") and it's set.
   827	##
   828	## Retrieves setting from $SPELL_CONFIG file and optionally sets user
   829	## supplied variable. Function is here to make possible changes to
   830	## config system easy, since all other functions are using this
   831	## function and are not working with variables directly
   832	##
   833	#---------------------------------------------------------------------
   834	function config_get_option() {
   835	  if [[ $PROTECT_SORCERY ]] ; then
   836	    if is_sorcery_var $1; then
   837	      complain_sorcery_var $1
   838	      return 1
   839	    fi
   840	  fi
   841	
   842	  # afrayedknot hates variable leakage
   843	  if real_list_find $1 $PERSISTENT_VARIABLES; then
   844	    # variable is known and valid
   845	    [[ -n $2 ]] && eval "$2=\"\$$1\""
   846	    return 0
   847	  else
   848	    # variable is not known and is invalid
   849	    eval "unset \"$1\""
   850	    return 1
   851	  fi
   852	}
   853	
   854	
   855	#---------------------------------------------------------------------
   856	## @param Name of variable
   857	## @param return variable
   858	##
   859	## @return 0 if the variable could be found, 1 if no old persistent file exists.
   860	##
   861	## Get value of a persistent variable from the previous cast when
   862	## there is a -r. The persistent data moves to a seperate directory
   863	## when cast -r is run, this makes the new persistent config clean
   864	## but allows us to provide the user defaults from the last cast.
   865	#---------------------------------------------------------------------
   866	function config_get_last_option() {
   867	  if [[ $PROTECT_SORCERY ]] ; then
   868	    if is_sorcery_var $1; then
   869	      complain_sorcery_var $1
   870	      return 1
   871	    fi
   872	  fi
   873	
   874	  local pfile
   875	  if test -f $ABANDONED_PERSIST/$SPELL.p; then
   876	    pfile=$ABANDONED_PERSIST/$SPELL.p
   877	  # leave this out for now until theres a way to disable it
   878	  # having it enabled will make it difficult to get back to the true defaults
   879	  #else
   880	  #  local tb_dir
   881	  #  tablet_find_spell_dir $SPELL tb_dir &&
   882	  #  tablet_get_persistent_config $tb_dir pfile || return 1
   883	  fi
   884	
   885	  local foo
   886	  if [[ $pfile ]] ; then
   887	    foo=$(
   888	      persistent_load $pfile
   889	      if real_list_find $1 $PERSISTENT_VARIABLES; then
   890	        eval "echo \"\$$1\""
   891	      fi
   892	    )
   893	  else
   894	    return 1
   895	  fi
   896	
   897	  eval "$2=\"\$foo\""
   898	  return 0
   899	}
   900	
   901	
   902	#---------------------------------------------------------------------
   903	## @param config file variable
   904	## @param value
   905	##
   906	## Stores string to given variable and makes the variable persistent
   907	## Function is here to make possible changes to config system easy,
   908	## since all other functions are using this function and are not
   909	## working with variables directly
   910	##
   911	#---------------------------------------------------------------------
   912	function config_set_option() {
   913	    persistent_add $1
   914	    eval "$1=\"$2\""
   915	}
   916	
   917	
   918	#---------------------------------------------------------------------
   919	## @param config file variable
   920	## @param question
   921	## @param default answer
   922	##
   923	## @return 0 in all cases
   924	##
   925	## Asks user for string, with default answer and timeout (like query)
   926	## Return variable is also marked as persistent
   927	##
   928	#---------------------------------------------------------------------
   929	function real_config_query() {
   930	    local ANSWER
   931	    if config_get_option "$1" ANSWER; then
   932	        # option allready ANSWERed in config
   933	        message "[[ ${QUERY_COLOR}$2${DEFAULT_COLOR} -> ${QUERY_COLOR}$ANSWER${DEFAULT_COLOR} ]]"
   934	
   935	        # already have a reasonable value...
   936	        [ "$ANSWER" == "y" ] || [ "$ANSWER" == "n" ] && return 0
   937	    fi
   938	
   939	    local default
   940	    config_get_last_option "$1" default
   941	    [[ $default ]] || default="$3"
   942	
   943	    if query "$2" "$default"; then
   944	        config_set_option "$1" y
   945	    else
   946	        config_set_option "$1" n
   947	    fi
   948	    return 0
   949	}
   950	
   951	#---------------------------------------------------------------------
   952	## @param config file variable
   953	## @param question
   954	## @param default answer [y|n]
   955	## @param option_yes - can't be empty string
   956	## @param option_no - can't be empty string
   957	##
   958	## @return 0 in all cases
   959	##
   960	## Asks user for string, with default answer and timeout (like query)
   961	## The string is added to the variable
   962	## If you want to use empty string, place there dummy string and remove
   963	## it later by list_remove function. Also for one config variable, all
   964	## option_yes and option_no have to be different.
   965	##
   966	## Return variable is also marked as persistent
   967	#---------------------------------------------------------------------
   968	function real_config_query_option() {
   969	    local ANSWER key
   970	
   971	    # If the option exists
   972	    # If the option contains option_yes or option_no
   973	    if config_get_option "$1" ANSWER && list_find "$ANSWER" $4 $5; then
   974	        # Then option allready ANSWERed in config
   975	
   976	        # Find out if the option was 'y' or 'n'
   977	        list_find "$ANSWER" $4 && key=y
   978	        list_find "$ANSWER" $5 && key=n
   979	
   980	        if [[ "$key" ]]; then
   981	            message "[[ ${QUERY_COLOR}$2${DEFAULT_COLOR} -> ${QUERY_COLOR}$key${DEFAULT_COLOR} ]]"
   982	            return 0
   983	        fi
   984	    fi
   985	
   986	    local last_answer default=$3
   987	    config_get_last_option "$1" last_answer
   988	    # Find out if the option was 'y' or 'n'
   989	    list_find "$last_answer" $4 && default=y
   990	    list_find "$last_answer" $5 && default=n
   991	
   992	    if query "$2" "$default"; then
   993	        list_add ANSWER $4
   994	    else
   995	        list_add ANSWER $5
   996	    fi
   997	
   998	    config_set_option "$1" "$ANSWER"
   999	    return 0
  1000	}
  1001	
  1002	
  1003	#---------------------------------------------------------------------
  1004	## @param config file variable, return variable
  1005	## @param question
  1006	## @param default answer
  1007	##
  1008	## @return 0 in all cases
  1009	##
  1010	## Asks user for string, with default answer and timeout (like query)
  1011	## Return variable is also marked as persistent
  1012	##
  1013	#---------------------------------------------------------------------
  1014	function real_config_query_string() {
  1015	    local ANSWER
  1016	
  1017	    if config_get_option "$1" ANSWER; then
  1018	        # option already answered in config
  1019	        message "[[ ${QUERY_COLOR}$2${DEFAULT_COLOR} -> '${QUERY_COLOR}$ANSWER${DEFAULT_COLOR}' ]]"
  1020	    else
  1021	        local default
  1022	        config_get_last_option "$1" default
  1023	        [[ $default ]] || default="$3"
  1024	        query_string ANSWER "$2" "$default"
  1025	        config_set_option "$1" "$ANSWER"
  1026	    fi
  1027	    return 0
  1028	}
  1029	
  1030	
  1031	#---------------------------------------------------------------------
  1032	## @param config file variable, return variable
  1033	## @param question
  1034	## @param elements, ...
  1035	##
  1036	## @return 0 in all cases
  1037	##
  1038	## Asks user for string, with numbered possibilities listed
  1039	## Return variable is also marked as persistent
  1040	##
  1041	#---------------------------------------------------------------------
  1042	function real_config_query_list() {
  1043	  local ANSWER
  1044	  local VARIABLE="$1"
  1045	  local QUESTION="$2"
  1046	  shift
  1047	  shift
  1048	
  1049	  if config_get_option "$VARIABLE" ANSWER; then
  1050	    # option already ANSWERed in config
  1051	    (
  1052	        for foo in "$@"; do
  1053	            [ "$foo" == "$ANSWER" ] && exit 0
  1054	        done
  1055	        echo "!!!! WARNING !!!!"
  1056	        echo "!!!! stored option '$ANSWER' in config is not in list of provided options !!!!"
  1057	        echo "!!!! WARNING !!!!"
  1058	    )
  1059	    message "[[ ${QUERY_COLOR}${QUESTION}${DEFAULT_COLOR} -> '${QUERY_COLOR}$ANSWER${DEFAULT_COLOR}' ]]"
  1060	  else
  1061	    # if there was an answer before, find it
  1062	    local default default_num=0 foo
  1063	    config_get_last_option "$VARIABLE" default
  1064	
  1065	    # we have to ask the user
  1066	    message "$QUESTION"
  1067	    select_list ANSWER "$default" "$@"
  1068	    config_set_option "$VARIABLE" "$ANSWER"
  1069	  fi
  1070	
  1071	  eval $VARIABLE=\"$ANSWER\"
  1072	  return 0
  1073	}
  1074	
  1075	#---------------------------------------------------------------------
  1076	## Output a list of source numbers associated with the current spell.
  1077	## This is the number portion of SOURCE[[:digit:]], eg '', "2", "3", etc.
  1078	## A prefix may be given and it will be prepended to each result value.
  1079	#---------------------------------------------------------------------
  1080	function real_get_source_nums() {
  1081	  local foo
  1082	  compgen -v SOURCE |
  1083	  grep '^SOURCE[[:digit:]]*$'|sort -u|
  1084	  while read foo; do echo "${1}${foo/SOURCE/}"; done
  1085	}
  1086	
  1087	function get_spell_files() {
  1088	  for src in $(real_get_source_nums SOURCE); do
  1089	    echo ${!src}
  1090	  done
  1091	}
  1092	
  1093	
  1094	#---------------------------------------------------------------------
  1095	## misc_is_function <function name>
  1096	## @param function name
  1097	## @return 0 if argument is a valid function
  1098	## @return 1 otherwise
  1099	## Returns true if input argument is the name of an
  1100	## existing function, false otherwise.
  1101	##
  1102	#---------------------------------------------------------------------
  1103	function misc_is_function()  {
  1104	  local  FUNCTION_NAME=$1
  1105	  [[ "$(type -t $FUNCTION_NAME)" == "function" ]]
  1106	}
  1107	
  1108	
  1109	#---------------------------------------------------------------------
  1110	## Remove files listed and any directories which become empty after
  1111	## subsequent file removal.
  1112	##
  1113	## @param Filename to read as input, a pipe may not be used.
  1114	## @param Base directory to remove directories up to (usually $INSTALL_ROOT)
  1115	#---------------------------------------------------------------------
  1116	function remove_files_and_dirs() {
  1117	    cat $1 | while read file; do
  1118	      if [[ ! -d $file ]]; then
  1119	        rm -f $file
  1120	      else
  1121	        # also try to remove empty dirs, as
  1122	        # the leaves are not taken care of by the next block #15804
  1123	        rmdir $file &>/dev/null
  1124	      fi
  1125	    done
  1126	
  1127	    # remove possibly empty directories, rmdir WILL have error output
  1128	    # because some directories wont be empty for one of many reasons,
  1129	    # this is OKAY
  1130	    cat $1|get_dirnames|sort -u|while read dir; do
  1131	      until [[ $dir == ${2:-/} ]] ; do
  1132	        rmdir $dir &>/dev/null || break
  1133	        smgl_dirname "$dir" dir
  1134	      done
  1135	    done
  1136	}
  1137	
  1138	#---------------------------------------------------------------------
  1139	## Safely creates $TMP_DIR and exports the variable so we can use it
  1140	## even in subprocesses called through make.
  1141	## @param name of the script needing the tmp dir
  1142	#---------------------------------------------------------------------
  1143	function mk_tmp_dirs() {
  1144	  debug "$FUNCNAME" "Making tmp dirs for $$"
  1145	  local SCRIPT_NAME=$1 STATS tmp
  1146	  SCRIPT_NAME=${SCRIPT_NAME:-misc}
  1147	
  1148	  local BASE_DIR=${2:-/tmp/sorcery}
  1149	  local SCRIPT_DIR=$BASE_DIR/$SCRIPT_NAME
  1150	  local FULL_DIR=$SCRIPT_DIR/$$
  1151	
  1152	
  1153	
  1154	  tmp=$(ls -lnd $BASE_DIR 2>/dev/null|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
  1155	  if [[ "$tmp" != "drwxr-xr-x:0:0" ]] ; then
  1156	    test -d $BASE_DIR &&
  1157	    message "$BASE_DIR has unknown permissions and ownership, replacing it" >&2
  1158	    rm -rf $BASE_DIR
  1159	    install -d -o root -g root -m 755 "$BASE_DIR" &&
  1160	    chmod a-s "$BASE_DIR" # work around installer bug where /tmp is suid/sgid
  1161	  fi || mk_tmp_dir_error $BASE_DIR
  1162	
  1163	  # double check
  1164	  tmp=$(ls -lnd $BASE_DIR 2>/dev/null|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
  1165	  if [[ "$tmp" != "drwxr-xr-x:0:0" ]] ; then
  1166	    mk_tmp_dir_error $BASE_DIR
  1167	  fi
  1168	
  1169	
  1170	  tmp=$(ls -lnd $SCRIPT_DIR 2>/dev/null|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
  1171	  if [[ "$tmp" != "drwxr-xr-x:0:0" ]] ; then
  1172	    rm -rf $SCRIPT_DIR
  1173	    install -d -o root -g root -m 755 "$SCRIPT_DIR"
  1174	  fi || mk_tmp_dir_error $SCRIPT_DIR
  1175	
  1176	  # double check
  1177	  tmp=$(ls -lnd $SCRIPT_DIR|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
  1178	  if [[ "$tmp" != "drwxr-xr-x:0:0" ]] ; then
  1179	    mk_tmp_dir_error $SCRIPT_DIR
  1180	  fi
  1181	
  1182	
  1183	  if [[ -e $FULL_DIR ]]; then
  1184	    message "Looks like you had an old $SCRIPT_NAME on PID $$. Cleaning it out..." >&2
  1185	    rm -rf $FULL_DIR
  1186	  fi
  1187	
  1188	  install -d -o root -g root -m 755 "$FULL_DIR" || mk_tmp_dir_error $FULL_DIR
  1189	  tmp=$(ls -lnd $SCRIPT_DIR|awk '{printf "%s:%s:%s\n" ,$1,$3,$4; }')
  1190	  if [[ "$tmp" != "drwxr-xr-x:0:0" ]] ; then
  1191	    mk_tmp_dir_error $FULL_DIR
  1192	  fi
  1193	
  1194	  # in order for TMP_DIR to make it through make and into pass_three/four
  1195	  # we must export
  1196	  export TMP_DIR=$FULL_DIR
  1197	}
  1198	
  1199	# emergency exit function if we fail to make a TMP_DIR
  1200	function mk_tmp_dir_error() {
  1201	    message "Failed to make temporary dir, this" \
  1202	            "might be due to a security attack,\nplease" \
  1203	            "check the permissions of $1. Bailing out..." 2>/dev/null
  1204	    exit 1
  1205	}
  1206	
  1207	#---------------------------------------------------------------------
  1208	##
  1209	## 'which' is not in basesystem, heres our own simple version of it
  1210	## it should be just as fast as the real one.
  1211	##
  1212	## @param executable to look for
  1213	## @param variable to return path in (pass by reference)
  1214	##
  1215	## Marches through $PATH looking for the executable specified
  1216	##
  1217	#---------------------------------------------------------------------
  1218	function smgl_which() {
  1219	  local target=$1 location
  1220	  local BREAK
  1221	  function smgl_which_sub() {
  1222	    if test -f "$1/$target" -a -x "$1/$target" ; then
  1223	      echo "$1/$target"
  1224	      BREAK=yes
  1225	    fi
  1226	  }
  1227	  if [ -z "$1" -o -z "$2" ]; then
  1228	    echo "smgl_which: Not enough arguments"
  1229	    return 1
  1230	  fi
  1231	  location=$(iterate smgl_which_sub : "$PATH")
  1232	  if [[ "$location" ]]  ; then
  1233	    eval "$2=$location"
  1234	  else
  1235	    echo "which: no $target in ($PATH)"
  1236	    return 1
  1237	  fi
  1238	}
  1239	
  1240	#---------------------------------------------------------------------
  1241	##
  1242	## Finds the make command and complains if its missing (shouldnt ever
  1243	## happen)
  1244	##
  1245	## @param variable to return path in (pass by reference)
  1246	##
  1247	#---------------------------------------------------------------------
  1248	function find_make() {
  1249	  local ___REAL_MAKE
  1250	  smgl_which make ___REAL_MAKE
  1251	  rc=$?
  1252	  if [[ $rc != 0 ]] ; then
  1253	    message "Cannot find make command!!"
  1254	    message "Bailing out"
  1255	    return $rc
  1256	  fi
  1257	  eval "$1=$___REAL_MAKE"
  1258	}
  1259	
  1260	
  1261	#---------------------------------------------------------------------
  1262	## @param tmp_dir, should begin with /tmp/sorcery
  1263	#---------------------------------------------------------------------
  1264	function cleanup_tmp_dir() {
  1265	  local TMP_DIR=$1
  1266	  if  [  ${TMP_DIR:0:13} ==  "/tmp/sorcery/"  ];  then
  1267	    rm -rf $TMP_DIR
  1268	  else
  1269	    message "Cowardly refusing to remove $TMP_DIR, this may be a bug," \
  1270	            "please alert the sorcery team."
  1271	  fi
  1272	}
  1273	
  1274	#---------------------------------------------------------------------
  1275	## @param Variable to check if its a known sorcery variable
  1276	## @return Success if its a sorcery variable, false if not
  1277	##
  1278	## This function will probably need to be updated as sorcery variables
  1279	## come in and out of flux, the variables listed are roughly defined as
  1280	## things defined at the point in which a spell file is run, and when it
  1281	## is not run in a seperate subshell, note that the build phase of cast
  1282	## is seperate from the frontend, so a spell could technically modify
  1283	## something like "SPELLS" and not have a major problem. However the
  1284	## philosophy is to not discriminate about what sorcery variables
  1285	## are technically okay to use in what files and ones that arent,
  1286	## instead we'll treat all usages of sorcery variables equally.  The rules
  1287	## are of course subject to change without warning and its just easier
  1288	## to be consistent, also theres a good chance that if a variable is safe
  1289	## in some circumstances and not others, and one uses it where its safe,
  1290	## someone will forget and start using it in places that arent safe...
  1291	#---------------------------------------------------------------------
  1292	function is_sorcery_var() {
  1293	  local vars="TOP_LEVEL DISPLAY PATH TMP_DIR SAFE_CAST \
  1294	  FAILED_LIST SUCCESS_LIST SPELL SPELLS spells MAKEFILE DEPS_ONLY \
  1295	  CAST_PASS download_log IW_LOG OPTS SOLO QUIET INSTALL_QUEUE \
  1296	  OVERRIDE_CFLAGS OVERRIDE_CXXFLAGS OVERRIDE_LDFLAGS NO_OPTIMIZATION_FLAGS \
  1297	  DOT_PROGRESS VOYEUR_OVERRIDE RECONFIGURE RECAST_DOWN COMPILE RECAST_UP \
  1298	  FORCE_DOWNLOAD SOURCE_CACHE SILENT FIX DEBUG SEPARATE \
  1299	  CAST_HASH BACK_CAST_HASH CANNOT_CAST_HASH uncommitted_hash NEW_DEPENDS \
  1300	  spell_depends DEPENDS_CONFIG UP_DEPENDS SPELL_CONFIG GRIMOIRE_DIR \
  1301	  SCRIPT_DIRECTORY SECTION_DIRECTORY GRIMOIRE SPELL_DIRECTORY SECTION \
  1302	  BUILD_API VOYEUR_STDOUT VOYEUR_STDERR C_LOG C_FIFO INSTALL_ROOT HOST \
  1303	  BUILD INST_LOG MD5_LOG INSTALLWATCHFILE INSTW_LOGFILE CAST_EXIT_STATUS"
  1304	
  1305	  # the \\< \\> matches word boundaries
  1306	  real_list_find "$vars" "$1"
  1307	}
  1308	
  1309	
  1310	#---------------------------------------------------------------------
  1311	## @param Variable name used in complaint
  1312	## @stdout Complain vehemently that a variable name is used by sorcery and
  1313	## @stdout the user should file a bug because a spell is using the variable
  1314	#---------------------------------------------------------------------
  1315	function complain_sorcery_var() {
  1316	  message "${PROBLEM_COLOR}WARNING: ATTEMPTING TO USE" \
  1317	          "${DEFAULT_COLOR}${SPELL_COLOR}\"$1\"" \
  1318	           "${DEFAULT_COLOR}${PROBLEM_COLOR}AS A SPELL VARIABLE."
  1319	  message "THIS VARIABLE IS A USED BY SORCERY, THIS IS PROBABLY" \
  1320	          "A SPELL BUG\nAND SORCERY MAY BEHAVE IN UNDEFINED WAYS IF" \
  1321	          "YOU CONTINUE."
  1322	  message "\n\nPLEASE REPORT A BUG IMMEDIATELY!!${DEFAULT_COLOR}\n\n"
  1323	}
  1324	
  1325	#-------------------------------------------------------------------
  1326	## @param (optional) Architecture to use
  1327	## Sets the SPECFILE glocal variable and the SMGL_COMPAT_ARCHS global array
  1328	## SPECFILE contains the compiler and other arch specifications
  1329	## SMGL_COMPAT_ARCHS is an array that holds architectures which are compatible
  1330	## with the desired architecture. The desired architecture is determined as
  1331	## follows:
  1332	## <pre>
  1333	## 1) If function is gien an argument, the argument is used, or
  1334	## 2) If cross-install is on, the TARGET architecture is used, or
  1335	## 3) The local ARCHITECTURE is used
  1336	## </pre>
  1337	## The least specific arch is in SMGL_COMPAT_ARCHS[0],
  1338	## SMGL_COMPAT_ARCHS[1] is more specific, et cetera. For example:
  1339	## desired architecture="athlon-xp" might result in:
  1340	## SPECFILE=/usr/share/archspecs/ia32/amd/athlon-xp
  1341	## SMGL_COMPAT_ARCHS=("ia32" "amd" "athlon-xp")
  1342	##
  1343	## ARCHITECTURE is also modified to be an array, the reverse of
  1344	## SMGL_COMPAT_ARCHS. The result is an array from most specific arch to least
  1345	## specific. $ARCHITECTURE does not change meaning since $A == ${A[0]}.
  1346	#-------------------------------------------------------------------
  1347	function set_architecture() {
  1348	
  1349	  $STD_DEBUG
  1350	  local specdir
  1351	  local i j
  1352	  unset SPECFILE
  1353	
  1354	  # If given an argument, treat as the architecture to use
  1355	  local arch=${1}
  1356	
  1357	  # If no arch is specified, see if this is a cross-install, if so, set arch to
  1358	  # the target arch
  1359	  if  [[ ! $arch ]] &&
  1360	      [[ $CROSS_INSTALL == on ]] &&
  1361	      [[ $TARGET ]]
  1362	  then
  1363	    debug "libmisc" "set_architecture: using cross-install's target arch"
  1364	    arch=${arch:-$TARGET}
  1365	  fi
  1366	
  1367	  # If no arch given and this isn't a cross-install, then default to the ARCHITECTURE var
  1368	  if ! [[ $arch ]] ; then
  1369	    arch=$ARCHITECTURE
  1370	  fi
  1371	
  1372	  # Find the specfile to use
  1373	  local find_compat=0
  1374	  find --version|grep -q 'version 4\.1\(\.\|$\)' && find_compat=1
  1375	  for specdir in ${ARCH_SPECS[@]} ; do
  1376	    if [[ $find_compat == 1 ]] ; then
  1377	      # older find still exists run in slower compatibility mode
  1378	      SPECFILE=$(find ${specdir} -perm -400 -type f -name "$arch" -print 2>/dev/null)
  1379	    else
  1380	      # -L so symlinks work, -type f so it won't match dirs, -quit so it
  1381	      # stops searching when it finds the answer
  1382	      SPECFILE=$(find -L ${specdir} -perm -400 -type f -name "$arch" -print -quit 2>/dev/null)
  1383	    fi
  1384	    [ $SPECFILE ] && break
  1385	  done
  1386	  if [[ ! $SPECFILE ]] ; then
  1387	    message "${PROBLEM_COLOR}Cannot find arch spec for $arch!"
  1388	    message "Reverting to null!"
  1389	    message "Please run sorcery afterwards and pick another architecture!$DEFAULT_COLOR"
  1390	    echo
  1391	    sleep 2
  1392	    SPECFILE=$(find -L ${specdir} -perm -400 -type f -name "null" -print -quit 2>/dev/null)
  1393	  fi
  1394	  debug "libmisc" "set_architecture: SPECFILE=$SPECFILE"
  1395	
  1396	  # turn the path into an array, but remove $specdir from the start first
  1397	  unset SMGL_COMPAT_ARCHS
  1398	  explode "${SPECFILE#$specdir/}" '/' SMGL_COMPAT_ARCHS
  1399	
  1400	    unset ARCHITECTURE
  1401	    # Reverse the array so that the most specific arch is first
  1402	    j=0
  1403	    for(( i=${#SMGL_COMPAT_ARCHS[@]}-1; i>=0; i--)) ; do
  1404	      ARCHITECTURE[j++]=${SMGL_COMPAT_ARCHS[i]}
  1405	    done
  1406	
  1407	    source "$SPECFILE"
  1408	}
  1409	
  1410	#---------------------------------------------------------------------
  1411	## @param Function body
  1412	## @param Function name
  1413	## @param (optional) Function name
  1414	## @param ...
  1415	## Creates functions with identical bodies. It is useful if you need
  1416	## to override a bunch of functions which have already been defined.
  1417	#---------------------------------------------------------------------
  1418	function define_functions() {
  1419	  local funcName
  1420	  local funcContent="$1"
  1421	  shift
  1422	  for funcName in $* ; do
  1423	    debug "libmisc" "define_functions - redefining $funcName"
  1424	    eval "function $funcName () { \
  1425	      $funcContent \
  1426	    }"
  1427	  done
  1428	}
  1429	
  1430	#---------------------------------------------------------------------
  1431	## @param target
  1432	## @param value
  1433	## Set variable by name, useful for setting variables that were passed
  1434	## by reference.
  1435	## Example:
  1436	## <pre>
  1437	## function uber() {
  1438	##   local x
  1439	##  unter x 5
  1440	##  echo $x
  1441	## }
  1442	## function unter() {
  1443	##   local var=$1 y=$2
  1444	##   read -p "enter value (default $2)" y
  1445	##   upvar $var $y
  1446	## }
  1447	## </pre>
  1448	#---------------------------------------------------------------------
  1449	function upvar() {
  1450	  # This makes eval see things as name=$2,
  1451	  # which makes leading/trailing whitespace and special chars magically work.
  1452	  eval "$1=\$2"
  1453	
  1454	}
  1455	
  1456	#---------------------------------------------------------------------
  1457	## Like uniq, but doesnt require a sorted list, implemented in awk.
  1458	#---------------------------------------------------------------------
  1459	function awkuniq() {
  1460	  awk '{ if ( seen[$0] != 1) { seen[$0]=1;print $0;}}'
  1461	}
  1462	
  1463	#---------------------------------------------------------------------
  1464	## Dirname written in bash, optionally uses an upvar (making it forkless).
  1465	#---------------------------------------------------------------------
  1466	function smgl_dirname() {
  1467	  local __dir=() __dirname i __last_dir
  1468	
  1469	  # Pull of any trailing /'s, there could be more than/one///
  1470	  __dirname=$1
  1471	  while [[ $__dirname != "$__last_dir" ]]; do
  1472	    __last_dir=$__dirname
  1473	    __dirname=${__dirname%/}
  1474	  done
  1475	  if [[ -z $__dirname ]]; then
  1476	    __dirname=/
  1477	  fi
  1478	
  1479	  explode "$__dirname" / __dir
  1480	  [[ ${__dirname:0:1} == / ]] && unset __dirname || __dirname="."
  1481	  for (( i=0; i < ${#__dir[@]}-1; i++ )); do
  1482	    __dirname="$__dirname/${__dir[$i]}"
  1483	  done
  1484	
  1485	  # Pull of any trailing /'s
  1486	  while [[ $__dirname != "$__last_dir" ]]; do
  1487	    __last_dir=$__dirname
  1488	    __dirname=${__dirname%/}
  1489	  done
  1490	  if [[ -z $__dirname ]]; then
  1491	    __dirname=/
  1492	  else
  1493	    if [[ ${__dirname:0:2} == // ]]; then
  1494	      __dirname="${__dirname:1}"
  1495	    elif [[ ${__dirname:0:2} == ./ ]]; then
  1496	      # usually not strictly necessary, but let's be conformant with dirname
  1497	      __dirname="${__dirname:2}"
  1498	    fi
  1499	  fi
  1500	
  1501	  if [[ "$2" ]] ; then
  1502	    upvar "$2" "$__dirname"
  1503	  else
  1504	    echo "$__dirname"
  1505	  fi
  1506	}
  1507	
  1508	#---------------------------------------------------------------------
  1509	## basename written in bash, optionally uses upvar (making it forkless).
  1510	## Does not have ability to remove file extension like the real basename.
  1511	#---------------------------------------------------------------------
  1512	function smgl_basename() {
  1513	  local __base=$1
  1514	  local __last_base
  1515	
  1516	  # trim off leading /'s (foo/bar//// -> foo/bar)
  1517	  while [[ "$__base" != "$__last_base" ]] ; do
  1518	    __last_base=$__base
  1519	    __base=${__base%/}
  1520	  done
  1521	
  1522	  # if there are no more /'s then this does nothing which is what we want
  1523	  __base=${__base##*/}
  1524	
  1525	  [[ -z $__base ]] && __base=/
  1526	
  1527	  if [[ "$2" ]] ; then
  1528	    upvar "$2" "$__base"
  1529	  else
  1530	    echo "$__base"
  1531	  fi
  1532	}
  1533	
  1534	#--------------------------------------------------------------------
  1535	## @param new path to add
  1536	## @param old path
  1537	## @Stdout new path
  1538	##
  1539	## TODO: remove grimoire functions libgcc gcc_prepend_path and use
  1540	## this one
  1541	#--------------------------------------------------------------------
  1542	function envar_prepend_path()
  1543	{
  1544	  if test -z $2
  1545	  then
  1546	    echo $1
  1547	  else
  1548	    echo $1:$2
  1549	  fi
  1550	}
  1551	
  1552	#---------------------------------------------------------------------
  1553	## Appends the input to the notice log, prepending the spell name as a
  1554	## title, appending a newline and teeing the input back unchanged.
  1555	#---------------------------------------------------------------------
  1556	function append_to_notice_log() {
  1557	  local notice_log=$TMP_DIR/notice_log
  1558	  local tmp_log=$TMP_DIR/tmp_notice_log
  1559	
  1560	  tee $tmp_log
  1561	  if [[ -s $tmp_log ]]; then
  1562	    # use echo -e instead of message if we ever bring SILENT back
  1563	    echo -e "$SPELL_COLOR$SPELL:$DEFAULT_COLOR" >> $notice_log
  1564	    cat $tmp_log >> $notice_log
  1565	    echo >> $notice_log
  1566	  fi
  1567	  rm -f $tmp_log
  1568	}
  1569	
  1570	#---------------------------------------------------------------------
  1571	## Appends the failure reason (input) to a log for use in displaying
  1572	## the final FAILED_LIST and the activity log
  1573	##
  1574	## @param reason
  1575	#---------------------------------------------------------------------
  1576	function log_failure_reason() {
  1577	  [[ -z $CAST_BACKUPDIR ]] && return 1
  1578	
  1579	  # delve has a different TMP_DIR, so we can't use it
  1580	  local failure_reason_log=$CAST_BACKUPDIR/failure_reason_log
  1581	  local frl
  1582	  local spell=${2:-$SPELL}
  1583	
  1584	  grep -sq "^$spell ($1)$" $failure_reason_log && return 0
  1585	
  1586	  lock_start_transaction "$failure_reason_log" frl
  1587	  echo "$spell ($1)" >> "$frl"
  1588	  lock_commit_transaction "$failure_reason_log"
  1589	}
  1590	
  1591	#---------------------------------------------------------------------
  1592	## Checks if there is an entry for the spell in the failure_reason_log
  1593	## If there is one, set the upvar to the reason
  1594	##
  1595	## @param upvar
  1596	#---------------------------------------------------------------------
  1597	function get_failure_reason() {
  1598	  local failure_reason_log=$CAST_BACKUPDIR/failure_reason_log
  1599	  local _upvar=$1
  1600	  local _reason
  1601	
  1602	  # if a spell has more than one entry, the first will be used
  1603	  lock_file $failure_reason_log
  1604	  _reason=$(grep -s -m1 "^$SPELL " $failure_reason_log | sed 's,^[^(]*(\([^)]*\)).*$,\1,')
  1605	  unlock_file $failure_reason_log
  1606	
  1607	  upvar $_upvar "$_reason"
  1608	}
  1609	
  1610	#---------------------------------------------------------------------
  1611	## Checks if the file is compressed and displays it
  1612	##
  1613	## @param file
  1614	## @param use dialog (optional)
  1615	## @param message to print on error (optional)
  1616	#---------------------------------------------------------------------
  1617	function show_file() {
  1618	  local file=$1
  1619	  local dialog=${2:-yes}
  1620	  local msg=${3:-"File not found."}
  1621	  if [[ -f $file && -s $file ]]; then
  1622	    case "$(file -b "$file")" in
  1623	      *text) if [[ $dialog == "yes" ]]; then
  1624	                eval $DIALOG '--textbox  $1  0  0'
  1625	              else
  1626	                cat "$file" | $PAGER
  1627	              fi ;;
  1628	      bzip2*) bzcat "$file" | $PAGER ;;
  1629	      gzip*) gzip -cd "$file" | $PAGER ;;
  1630	      xz*|XZ*|LZMA*) xz -cd "$file" | $PAGER ;;
  1631	      7-zip*) 7z -d "$file" | $PAGER ;;
  1632	          *) message "Unknown file type."
  1633	              return 1 ;;
  1634	    esac
  1635	  else
  1636	    if [[ $dialog == yes ]]; then
  1637	      eval $DIALOG '--msgbox  "'$msg'"  0  0'
  1638	    else
  1639	      message "$msg" 1>&2
  1640	    fi
  1641	    return 1
  1642	  fi
  1643	}
  1644	
  1645	# Standard debug line:
  1646	# file "function@line" "all" "args"
  1647	STD_DEBUG='eval local _stddbg_file=${BASH_SOURCE[0]} ;
  1648	  _stddbg_file=${_stddbg_file##*/};
  1649	  debug "${_stddbg_file}" "${FUNCNAME[0]}@$LINENO" "$@"'
  1650	
  1651	
  1652	#---------------------------------------------------------------------
  1653	## @License
  1654	##
  1655	## This software is free software; you can redistribute it and/or modify
  1656	## it under the terms of the GNU General Public License as published by
  1657	## the Free Software Foundation; either version 2 of the License, or
  1658	## (at your option) any later version.
  1659	##
  1660	## This software is distributed in the hope that it will be useful,
  1661	## but WITHOUT ANY WARRANTY; without even the implied warranty of
  1662	## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1663	## GNU General Public License for more details.
  1664	##
  1665	## You should have received a copy of the GNU General Public License
  1666	## along with this software; if not, write to the Free Software
  1667	## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  1668	##
  1669	#---------------------------------------------------------------------