3 # This defines some general, useful functions.
5 #hmmm: starting to get a bit beefy in here. perhaps there is a good way to refactor the functions into more specific folders, if they aren't really totally general purpose?
9 # test whether we've been here before or not.
11 type function_sentinel &>/dev/null
13 # there was no error, so we can skip the inits.
14 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then
15 echo "skipping function definitions, because already defined."
22 if [ -z "$skip_all" ]; then
24 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then
25 echo "feisty meow function definitions beginning now..."
28 # a handy little method that can be used for date strings. it was getting
29 # really tiresome how many different ways the script did the date formatting.
30 function date_stringer() {
32 if [ -z "$sep" ]; then sep='_'; fi
33 date +"%Y$sep%m$sep%d$sep%H%M$sep%S" | tr -d '/\n/'
36 # a wrapper for the which command that finds items on the path. some OSes
37 # do not provide which, so we want to not be spewing errors when that
42 local WHICHER="$(\which which 2>/dev/null)"
44 # there is no which command here. we produce nothing due to this.
47 echo $($WHICHER $to_find 2>/dev/null)
50 # makes a directory of the name specified and then tries to change the
51 # current directory to that directory.
53 if [ ! -d "$1" ]; then mkdir -p "$1"; fi
57 # returns true if the variable is an array.
59 [[ "$(declare -p $1)" =~ "declare -a" ]]
62 # returns true if the name provided is a defined alias.
68 # makes the status of pipe number N (passed as first parameter) into the
69 # main return value (i.e., the value for $?). this is super handy to avoid
70 # repeating the awkward looking code below in multiple places.
71 function promote_pipe_return()
73 ( exit ${PIPESTATUS[$1]} )
78 function fm_username()
80 # see if we can get the user name from the login name. oddly this sometimes doesn't work.
81 local custom_user="$(logname 2>/dev/null)"
82 if [ -z "$custom_user" ]; then
83 # try the normal unix user variable.
86 if [ -z "$custom_user" ]; then
87 # try the windows user variable.
88 custom_user="$USERNAME"
95 # displays the value of a variable in bash friendly format.
100 local varname="$1"; shift
101 if [ -z "$varname" ]; then
105 if is_alias "$varname"; then
106 #echo found $varname is alias
107 local tmpfile="$(mktemp $TMP/aliasout.XXXXXX)"
108 alias $varname | sed -e 's/.*=//' >$tmpfile
109 echo "alias $varname=$(cat $tmpfile)"
111 elif [ -z "${!varname}" ]; then
112 echo "$varname undefined"
114 if is_array "$varname"; then
115 #echo found $varname is array var
117 eval temparray="(\${$varname[@]})"
118 echo "$varname=(${temparray[@]})"
119 #hmmm: would be nice to print above with elements enclosed in quotes, so that we can properly
120 # see ones that have spaces in them.
122 #echo found $varname is simple
123 echo "$varname=${!varname}"
132 # when passed a list of things, this will return the unique items from that list as an echo.
135 # do the uniquification: split the space separated items into separate lines, then
136 # sort the list, then run the uniq tool on the list. results will be packed back onto
137 # one line when invoked like: local fredlist="$(uniquify a b c e d a e f a e d b)"
138 echo $* | tr ' ' '\n' | sort | uniq
141 # sets the variable in parameter 1 to the value in parameter 2, but only if
142 # that variable was undefined.
143 function set_var_if_undefined()
145 local var_name="$1"; shift
146 local var_value="$1"; shift
147 if [ -z "${!var_name}" ]; then
148 eval export $var_name="$var_value"
154 function success_sound()
156 if [ ! -z "$CLAM_FINISH_SOUND" ]; then
157 bash $FEISTY_MEOW_SCRIPTS/multimedia/sound_play.sh "$CLAM_FINISH_SOUND"
161 function error_sound()
163 if [ ! -z "$CLAM_ERROR_SOUND" ]; then
164 bash $FEISTY_MEOW_SCRIPTS/multimedia/sound_play.sh "$CLAM_ERROR_SOUND"
170 # echoes the maximum number of columns that the terminal supports. usually
171 # anything you print to the terminal with length less than (but not equal to)
172 # maxcols will never wrap.
173 function get_maxcols()
175 # calculate the number of columsn in the terminal.
176 local cols=$(stty size | awk '{print $2}')
182 # checks the result of the last command that was run, and if that failed,
183 # then this complains and exits from bash. the function parameters are
184 # used as the message to print as a complaint.
185 function exit_on_error()
187 if [ $? -ne 0 ]; then
188 echo -e "\n\nan important action failed and this script will stop:\n\n$*\n\n*** Exiting script..."
194 # like exit_on_error, but will keep going after complaining.
195 function continue_on_error()
197 if [ $? -ne 0 ]; then
198 echo -e "\n\na problem occurred, but we can continue:\n\n$*\n\n=> Continuing script..."
205 # accepts any number of arguments and outputs them to the feisty meow event log.
206 function log_feisty_meow_event()
208 echo -e "$(date_stringer) -- ${USER}@$(hostname): $*" >> "$FEISTY_MEOW_EVENT_LOG"
213 # wraps secure shell with some parameters we like, most importantly to enable X forwarding.
217 # we remember the old terminal title, then force the TERM variable to a more generic
218 # version for the other side (just 'linux'); we don't want the remote side still
219 # thinking it's running xterm.
222 #hmmm: why were we doing this? it scorches the user's logged in session, leaving it without proper terminal handling.
223 # # we save the value of TERM; we don't want to leave the user's terminal
224 # # brain dead once we come back from this function.
225 # local oldterm="$TERM"
228 /usr/bin/ssh -Y -C "${args[@]}"
230 # # restore the terminal variable also.
233 restore_terminal_title
234 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then
235 echo TERM title restored to prior value
241 # locates a process given a search pattern to match in the process list.
242 # supports a single command line flag style parameter of "-u USERNAME";
243 # if the -u flag is found, a username is expected afterwards, and only the
244 # processes of that user are considered.
246 local -a patterns=("${@}")
248 #echo patterns list is: "${patterns[@]}"
252 if [ "${patterns[0]}" == "-u" ]; then
253 user_flag="-u ${patterns[1]}"
254 #echo "found a -u parm and user=${patterns[1]}"
255 # void the two elements with that user flag so we don't use them as patterns.
256 unset patterns[0] patterns[1]=
262 local PID_DUMP="$(mktemp "$TMP/zz_pidlist.XXXXXX")"
265 if [ "$OS" == "Windows_NT" ]; then
266 # gets cygwin's (god awful) ps to show windoze processes also.
267 local EXTRA_DOZER_FLAGS="-W"
268 # pattern to use for peeling off the process numbers.
269 local pid_finder_pattern='s/ *\([0-9][0-9]*\) *.*$/\1/p'
272 # flags which clean up the process listing output on unixes.
273 # apparently cygwin doesn't count as a type of unix, because their
274 # crummy specialized ps command doesn't support normal ps flags.
275 local EXTRA_UNIX_FLAGS="-o pid,args"
276 # pattern to use for peeling off the process numbers.
277 local pid_finder_pattern='s/^[[:space:]]*\([0-9][0-9]*\).*$/\1/p'
280 /bin/ps $EXTRA_DOZER_FLAGS $EXTRA_UNIX_FLAGS $user_flag | tail -n +2 >$PID_DUMP
282 #echo got all this stuff in the pid dump file:
286 # search for the pattern the user wants to find, and just pluck the process
287 # ids out of the results.
289 for i in "${patterns[@]}"; do
290 PIDS_SOUGHT+=($(cat $PID_DUMP \
292 | sed -n -e "$pid_finder_pattern"))
295 #echo pids sought list became:
296 #echo "${PIDS_SOUGHT[@]}"
299 if [ ${#PIDS_SOUGHT[*]} -ne 0 ]; then
300 local PIDS_SOUGHT2=$(printf -- '%s\n' ${PIDS_SOUGHT[@]} | sort | uniq)
302 PIDS_SOUGHT=${PIDS_SOUGHT2[*]}
303 echo ${PIDS_SOUGHT[*]}
308 # finds all processes matching the pattern specified and shows their full
309 # process listing (whereas psfind just lists process ids).
312 echo "psa finds processes by pattern, but there was no pattern on the command line."
315 local -a patterns=("${@}")
316 p=$(psfind "${patterns[@]}")
322 if [ "${patterns[0]}" == "-u" ]; then
323 # void the two elements with that user flag so we don't use them as patterns.
324 unset patterns[0] patterns[1]=
328 echo "Processes matching ${patterns[@]}..."
330 if [ -n "$IS_DARWIN" ]; then
333 # only print the header the first time.
334 if [ -z "$fuzil_sentinel" ]; then
337 ps $i -w -u | sed -e '1d'
342 # cases besides mac os x's darwin.
343 if [ "$OS" == "Windows_NT" ]; then
344 # special case for windows.
347 ps -W -p $curr | tail -n +2
350 # normal OSes can handle a nice simple query.
358 #hmmm: holy crowbars, this is an old one. do we ever still have any need of it?
359 # an unfortunately similarly named function to the above 'ps' as in process
360 # methods, but this 'ps' stands for postscript. this takes a postscript file
361 # and converts it into pcl3 printer language and then ships it to the printer.
362 # this mostly makes sense for an environment where one's default printer is
363 # pcl. if the input postscript causes ghostscript to bomb out, there has been
364 # some good success running ps2ps on the input file and using the cleaned
365 # postscript file for printing.
366 function ps2pcl2lpr() {
368 gs -sDEVICE=pcl3 -sOutputFile=- -sPAPERSIZE=letter "$i" | lpr -l
374 #hmmm: ugly absolute path here.
376 restore_terminal_title
379 # switches from a /X/path form to an X:/ form. this also processes cygwin paths.
380 function unix_to_dos_path() {
381 # we usually remove dos slashes in favor of forward slashes.
383 if [[ ! "$OS" =~ ^[Ww][iI][nN] ]]; then
384 # fake this value for non-windows (non-cygwin) platforms.
387 # for cygwin, we must replace the /home/X path with an absolute one, since cygwin
388 # insists on the /home form instead of /c/cygwin/home being possible. this is
389 # super frustrating and nightmarish.
390 DOSSYHOME="$(cygpath -am "$HOME")"
393 if [ ! -z "$SERIOUS_SLASH_TREATMENT" ]; then
394 # unless this flag is set, in which case we force dos slashes.
395 echo "$1" | sed -e "s?^$HOME?$DOSSYHOME?g" | sed -e 's/\\/\//g' | sed -e 's/\/cygdrive//' | sed -e 's/\/\([a-zA-Z]\)\/\(.*\)/\1:\/\2/' | sed -e 's/\//\\/g'
397 echo "$1" | sed -e "s?^$HOME?$DOSSYHOME?g" | sed -e 's/\\/\//g' | sed -e 's/\/cygdrive//' | sed -e 's/\/\([a-zA-Z]\)\/\(.*\)/\1:\/\2/'
401 # # switches from an X:/ form to a /cygdrive/X/path form. this is only useful
402 # # for the cygwin environment currently.
403 # function dos_to_unix_path() {
404 # # we always remove dos slashes in favor of forward slashes.
405 ##old: echo "$1" | sed -e 's/\\/\//g' | sed -e 's/\([a-zA-Z]\):\/\(.*\)/\/\1\/\2/'
406 # echo "$1" | sed -e 's/\\/\//g' | sed -e 's/\([a-zA-Z]\):\/\(.*\)/\/cygdrive\/\1\/\2/'
409 # returns a successful value (0) if this system is debian or ubuntu.
410 function debian_like() {
411 # decide if we think this is debian or ubuntu or a variant.
412 DEBIAN_LIKE=$(if [ ! -z "$(grep -i debian /etc/issue)" \
413 -o ! -z "$(grep -i ubuntu /etc/issue)" ]; then echo 1; else echo 0; fi)
414 if [ $DEBIAN_LIKE -eq 1 ]; then
415 # success; this is debianish.
418 # this seems like some other OS.
423 # this function wraps the normal sudo by ensuring we replace the terminal
424 # label before we launch what they're passing to sudo. we also preserve
425 # specific variables that enable the main user's ssh credentials to still
426 # be relied on for ssh forwarding, even if the '-i' flag is passed to cause
427 # a fresh shell (which normally doesn't get the launching user's environment
432 # hoist our X authorization info in case environment is passed along;
433 # this can allow root to use our display to show X.org windows.
434 if [ -z "$IMPORTED_XAUTH" -a ! -z "$DISPLAY" ]; then
435 export IMPORTED_XAUTH="$(xauth list $DISPLAY | head -n 1 | awk '{print $3}')"
436 local REMOVE_IMP_XAUTH=true
439 # launch sudo with just the variables we want to reach the other side.
442 if [ ! -z "$IMPORTED_XAUTH" ]; then varmods+="IMPORTED_XAUTH=$IMPORTED_XAUTH "; fi
443 if [ ! -z "$SSH_AUTH_SOCK" ]; then varmods+="SSH_AUTH_SOCK=$SSH_AUTH_SOCK"; fi
444 /usr/bin/sudo $varmods "$@"
447 # take the xauth info away again if it wasn't set already.
448 if [ ! -z "$REMOVE_IMP_XAUTH" ]; then
451 restore_terminal_title
455 # trashes the .#blah files that cvs and subversion leave behind when finding conflicts.
456 # this kind of assumes you've already checked them for any salient facts.
457 function clean_cvs_junk() {
459 find $i -follow -type f -iname ".#*" -exec perl $FEISTY_MEOW_SCRIPTS/files/safedel.pl {} ";"
463 # overlay for nechung binary so that we can complain less grossly about it when it's missing.
465 local wheres_nechung=$(whichable nechung)
466 if [ -z "$wheres_nechung" ]; then
467 echo "The nechung oracle program cannot be found. You may want to consider"
468 echo "rebuilding the feisty meow applications with this command:"
469 echo "bash $FEISTY_MEOW_SCRIPTS/generator/produce_feisty_meow.sh"
476 # recreates all the generated files that the feisty meow scripts use.
477 function regenerate() {
478 # do the bootstrapping process again.
480 echo "regenerating feisty meow script environment."
481 bash $FEISTY_MEOW_SCRIPTS/core/reconfigure_feisty_meow.sh
483 # force a full reload by turning off sentinel variables and methods.
484 unset -v CORE_VARIABLES_LOADED FEISTY_MEOW_LOADING_DOCK USER_CUSTOMIZATIONS_LOADED
485 unalias CORE_ALIASES_LOADED &>/dev/null
486 unset -f function_sentinel
487 # reload feisty meow environment in current shell.
488 log_feisty_meow_event "reloading the feisty meow scripts for $USER in current shell."
489 source "$FEISTY_MEOW_SCRIPTS/core/launch_feisty_meow.sh"
490 # run nechung oracle to give user a new fortune.
492 restore_terminal_title
495 # merges a set of custom scripts into the feisty meow environment. can be
496 # passed a name to use as the custom scripts source folder (found on path
497 # $FEISTY_MEOW_SCRIPTS/customize/{name}), or it will try to guess the name
498 # by using the login name.
499 function recustomize()
501 local custom_user="$1"; shift
502 if [ -z "$custom_user" ]; then
503 # default to login name if there was no name provided.
504 custom_user="$(fm_username)"
505 # we do intend to use the login name here to get the login name and to ignore
506 # if the user has sudo root access; we don't want to provide a custom
509 # chop off any email address style formatting to leave just the name.
510 custom_user="$(echo "$custom_user" | cut -f1 -d'@')"
514 if [ ! -d "$FEISTY_MEOW_SCRIPTS/customize/$custom_user" ]; then
515 echo -e "the customization folder for '$custom_user' is missing:
517 $FEISTY_MEOW_SCRIPTS/customize/$custom_user
519 we will skip recustomization, but these other customizations are available:
521 # a little tr and sed magic to fix the carriage returns into commas.
522 local line="$(find $FEISTY_MEOW_SCRIPTS/customize -mindepth 1 -maxdepth 1 -type d -exec basename {} ';' | tr '\n' '&' | sed 's/&/, /g' | sed -e 's/, $//')"
523 # make the line feeds and carriage returns manageable with tr.
524 # convert the ampersand, our weird replacement for EOL, with a comma + space in sed.
525 # last touch with sed removes the last comma.
530 # recreate the feisty meow loading dock.
531 regenerate >/dev/null
533 # jump into the loading dock and make our custom link.
534 pushd "$FEISTY_MEOW_LOADING_DOCK" &>/dev/null
535 if [ -h custom ]; then
536 # there's an existing link, so remove it.
539 # make sure we cleaned up the area before we re-link.
540 if [ -h custom -o -d custom -o -f custom ]; then
542 Due to an over-abundance of caution, we are not going to remove an unexpected
543 'custom' object found in the file system. This object is located in the
544 feisty meow loading dock here: $(pwd)
545 And here is a description of the rogue 'custom' object:
549 If you are pretty sure that this is just a remnant of an older approach in
550 feisty meow, where we copied the custom directory rather than linking it
551 (and it most likely is just such a bit of cruft of that nature), then please
552 remove that old remnant 'custom' item, for example by saying:
553 /bin/rm -rf \"custom\" ; popd
554 Sorry for the interruption, but we want to make sure this removal wasn't
555 automatic if there is even a small amount of doubt about the issue."
559 # create the custom folder as a link to the customizations.
560 ln -s "$FEISTY_MEOW_SCRIPTS/customize/$custom_user" custom
564 # now take into account all the customizations by regenerating the feisty meow environment.
567 restore_terminal_title
570 # generates a random password where the first parameter is the number of characters
571 # in the password (default 20) and the second parameter specifies whether to use
572 # special characters (1) or not (0).
573 # found function at http://legroom.net/2010/05/06/bash-random-password-generator
574 function random_password()
576 [ "$2" == "0" ] && CHAR="[:alnum:]" || CHAR="[:graph:]"
577 cat /dev/urandom | tr -cd "$CHAR" | head -c ${1:-32}
581 function add_cygwin_drive_mounts() {
582 for i in c d e f g h q z ; do
583 #hmmm: improve this by not adding the link if already there, or if the drive is not valid.
584 ln -s /cygdrive/$i $i
588 ############################
590 # takes a file to modify, and then it will replace any occurrences of the
591 # pattern provided as the second parameter with the text in the third
593 function replace_pattern_in_file()
595 local file="$1"; shift
596 local pattern="$1"; shift
597 local replacement="$1"; shift
598 if [ -z "$file" -o -z "$pattern" -o -z "$replacement" ]; then
599 echo "replace_pattern_in_file: needs a filename, a pattern to replace, and the"
600 echo "text to replace that pattern with."
603 sed -i -e "s%$pattern%$replacement%g" "$file"
606 # similar to replace_pattern_in_file, but also will add the new value
607 # when the old one did not already exist in the file.
608 function replace_if_exists_or_add()
610 local file="$1"; shift
611 local phrase="$1"; shift
612 local replacement="$1"; shift
613 if [ -z "$file" -o ! -f "$file" -o -z "$phrase" -o -z "$replacement" ]; then
614 echo "replace_if_exists_or_add: needs a filename, a phrase to replace, and the"
615 echo "text to replace that phrase with."
618 grep "$phrase" "$file" >/dev/null
619 # replace if the phrase is there, otherwise add it.
620 if [ $? -eq 0 ]; then
621 replace_pattern_in_file "$file" "$phrase" "$replacement"
623 # this had better be the complete line.
624 echo "$replacement" >>"$file"
628 ############################
630 # finds a variable (first parameter) in a particular property file
631 # (second parameter). the expected format for the file is:
633 function seek_variable()
635 local find_var="$1"; shift
636 local file="$1"; shift
637 if [ -z "$find_var" -o -z "$file" -o ! -f "$file" ]; then
638 echo -e "seek_variable: needs two parameters, firstly a variable name, and\nsecondly a file where the variable's value will be sought." 1>&2
643 if [ ${#line} -eq 0 ]; then continue; fi
644 # split the line into the variable name and value.
645 IFS='=' read -a assignment <<< "$line"
646 local var="${assignment[0]}"
647 local value="${assignment[1]}"
648 if [ "${value:0:1}" == '"' ]; then
649 # assume the entry was in quotes and remove them.
650 value="${value:1:$((${#value} - 2))}"
652 if [ "$find_var" == "$var" ]; then
658 # finds a variable (first parameter) in a particular XML format file
659 # (second parameter). the expected format for the file is:
660 # ... name="varX" value="valueX" ...
661 function seek_variable_in_xml()
663 local find_var="$1"; shift
664 local file="$1"; shift
665 if [ -z "$find_var" -o -z "$file" -o ! -f "$file" ]; then
666 echo "seek_variable_in_xml: needs two parameters, firstly a variable name, and"
667 echo "secondly an XML file where the variable's value will be sought."
672 if [ ${#line} -eq 0 ]; then continue; fi
673 # process the line to make it more conventional looking.
674 line="$(echo "$line" | sed -e 's/.*name="\([^"]*\)" value="\([^"]*\)"/\1=\2/')"
675 # split the line into the variable name and value.
676 IFS='=' read -a assignment <<< "$line"
677 local var="${assignment[0]}"
678 local value="${assignment[1]}"
679 if [ "${value:0:1}" == '"' ]; then
680 # assume the entry was in quotes and remove them.
681 value="${value:1:$((${#value} - 2))}"
683 if [ "$find_var" == "$var" ]; then
689 ############################
691 # goes to a particular directory passed as parameter 1, and then removes all
692 # the parameters after that from that directory.
693 function push_whack_pop()
695 local dir="$1"; shift
696 pushd "$dir" &>/dev/null
697 if [ $? -ne 0 ]; then echo failed to enter dir--quitting.; fi
698 rm -rf $* &>/dev/null
699 if [ $? -ne 0 ]; then echo received a failure code when removing.; fi
705 # new breed of definer functions goes here. still in progress.
707 # defines an alias and remembers that this is a new or modified definition.
708 # if the feisty meow codebase is unloaded, then so are all the aliases that
710 function define_yeti_alias()
712 # if alias exists already, save old value for restore,
713 # otherwise save null value for restore,
714 # have to handle unaliasing if there was no prior value of one
716 # add alias name to a list of feisty defined aliases.
718 #hmmm: first implem, just do the alias and get that working...
727 #hmmm: this points to an extended functions file being needed; not all of these are core.
729 # displays documentation in "md" formatted files.
732 local file="$1"; shift
733 pandoc "$file" | lynx -stdin
738 # just shows a separator line for an 80 column console, or uses the first
739 # parameter as the number of columns to expect.
743 if [ -z "$count" ]; then
748 for ((i=0; i < $count - 1; i++)); do
754 # alias for separator.
762 # count the number of sub-directories in a directory and echo the result.
763 function count_directories()
765 local subbydir="$1"; shift
766 numdirs="$(find "$subbydir" -mindepth 1 -maxdepth 1 -type d | wc -l)"
770 # takes a string and capitalizes just the first character. any capital letters in the remainder of
771 # the string are made lower case. the processed string is returned by an echo.
772 function capitalize_first_char()
774 local to_dromedary="$1"; shift
775 to_dromedary="$(tr '[:lower:]' '[:upper:]' <<< ${to_dromedary:0:1})$(tr '[:upper:]' '[:lower:]' <<< ${to_dromedary:1})"
779 # given a source path and a target path, this will make a symbolic link from
780 # the source to the destination, but only if the source actually exists.
781 function make_safe_link()
783 local src="$1"; shift
784 local target="$1"; shift
786 if [ -d "$src" ]; then
787 ln -s "$src" "$target"
788 exit_on_error "Creating symlink from '$src' to '$target'"
790 echo "Created symlink from '$src' to '$target'."
793 # pretty prints the json files provided as parameters.
794 function clean_json()
796 if [ -z "$*" ]; then return; fi
799 local file="$1"; shift
800 if [ -z "$file" ]; then break; fi
801 if [ ! -f "$file" ]; then "echo File '$file' does not exist."; continue; fi
802 temp_out="$TMP/$file.view"
803 cat "$file" | python -m json.tool > "$temp_out"
804 show_list+=($temp_out)
805 continue_on_error "pretty printing '$file'"
807 filedump "${show_list[@]}"
813 # only print our special headers or text fields.
817 grep -i "\"text\":\|^=.*" |
818 sed -e "s/\\\\r/$CR/g" -e "s/\\\\n/\\$LF/g"
823 # echoes the machine's hostname. can be used like so:
824 # local my_host=$(get_hostname)
825 function get_hostname()
827 # there used to be more variation in how to do this, but adopting mingw
828 # and cygwin tools really helped out.
829 local this_host=unknown
830 if [ "$OS" == "Windows_NT" ]; then
831 this_host=$(hostname)
832 elif [ ! -z "$(echo $MACHTYPE | grep apple)" ]; then
833 this_host=$(hostname)
834 elif [ ! -z "$(echo $MACHTYPE | grep suse)" ]; then
835 this_host=$(hostname --long)
836 elif [ -x "$(whichable hostname)" ]; then
837 this_host=$(hostname)
842 # makes sure that the provided "folder" is a directory and is writable.
843 function test_writeable()
845 local folder="$1"; shift
846 if [ ! -d "$folder" -o ! -w "$folder" ]; then return 1; fi
852 # given a filename and a string to seek and a number of lines, then this
853 # function will remove the first occurrence of a line in the file that
854 # matches the string, and it will also axe the next N lines as specified.
855 function create_chomped_copy_of_file()
857 local filename="$1"; shift
858 local seeker="$1"; shift
859 local numlines=$1; shift
861 #echo into create_chomped_copy...
862 #var filename seeker numlines
864 # make a backup first, oy.
865 \cp -f "$filename" "/tmp/$(basename ${filename}).bkup-${RANDOM}"
866 exit_on_error "backing up file: $filename"
868 # make a temp file to write to before we move file into place in bind.
869 local new_version="/tmp/$(basename ${filename}).bkup-${RANDOM}"
870 \rm -f "$new_version"
871 exit_on_error "cleaning out new version of file from: $new_version"
877 # don't bother looking at the lines if we're already in skip mode.
878 if [[ $skip_count == 0 ]]; then
879 # find the string they're seeking.
880 if [[ ! "$line" =~ .*${seeker}.* ]]; then
882 echo "$line" >> "$new_version"
884 # a match! start skipping. we will delete this line and the next N lines.
886 #echo first skip count is now $skip_count
890 # we're already skipping. let's keep going until we hit the limit.
892 #echo ongoing skip count is now $skip_count
893 if (( $skip_count > $numlines )); then
894 echo "Done skipping, and back to writing output file."
900 #echo file we created looks like this:
903 if [ ! -z "$found_any" ]; then
904 # put the file back into place under the original name.
905 \mv "$new_version" "$filename"
906 exit_on_error "moving the new version into place in: $filename"
908 # cannot always be considered an error, but we can at least gripe.
909 echo "Did not find any matches for seeker '$seeker' in file: $filename"
915 # space 'em all: fixes naming for all of the files of the appropriate types
916 # in the directories specified.
917 function spacemall() {
918 local -a dirs=("${@}")
919 if [ ${#dirs[@]} -eq 0 ]; then
923 local charnfile="$(mktemp $TMP/zz_charn.XXXXXX)"
924 #hmmm: any way to do the below more nicely or reusably?
925 find "${dirs[@]}" -follow -maxdepth 1 -mindepth 1 -type f | \
927 "csv\|doc\|docx\|eml\|html\|jpeg\|jpg\|m4a\|mov\|mp3\|ods\|odt\|pdf\|png\|ppt\|pptx\|txt\|vsd\|vsdx\|xls\|xlsx\|xml\|zip" | \
928 sed -e 's/^/"/' | sed -e 's/$/"/' | \
929 xargs bash "$FEISTY_MEOW_SCRIPTS/files/spacem.sh"
930 # drop the temp file now that we're done.
936 # tty relevant functions...
938 # keep_awake: sends a message to the screen from the background.
939 function keep_awake()
941 # just starts the keep_awake process in the background.
942 bash $FEISTY_MEOW_SCRIPTS/tty/keep_awake_process.sh &
943 # this should leave the job running as %1 or a higher number if there
944 # are pre-existing background jobs.
949 # site avenger functions...
953 THISDIR="$FEISTY_MEOW_SCRIPTS/site_avenger"
954 source "$FEISTY_MEOW_SCRIPTS/site_avenger/shared_site_mgr.sh"
960 # you have hit the borderline functional zone...
962 #hmmm: not really doing anything yet; ubuntu seems to have changed from pulseaudio in 17.04?
963 # restarts the sound driver.
964 function fix_sound_driver() {
965 # stop bash complaining about blank function body.
968 # sudo service alsasound restart
969 #elif pulse something
978 # ...and here's the end of the borderline functional zone.
982 # NOTE: no more function definitions are allowed after this point.
984 function function_sentinel()
989 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then echo "feisty meow function definitions done."; fi
993 # test code for set_var_if_undefined.
995 if [ $run_test != 0 ]; then
996 echo running tests on set_var_if_undefined.
998 set_var_if_undefined flagrant forknordle
999 exit_on_error "testing if defined variable would be whacked"
1000 if [ $flagrant != petunia ]; then
1001 echo set_var_if_undefined failed to leave the test variable alone
1004 unset bobblehead_stomper
1005 set_var_if_undefined bobblehead_stomper endurance
1006 if [ $bobblehead_stomper != endurance ]; then
1007 echo set_var_if_undefined failed to set a variable that was not defined yet