/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 #---------------------------------------------------------------------