diff options
author | Singustromo <singustromo@disroot.org> | 2023-07-02 12:54:20 +0200 |
---|---|---|
committer | Singustromo <singustromo@disroot.org> | 2023-07-02 12:54:20 +0200 |
commit | d0b41fb5266b188f6642db1af49b5ed8fe564e57 (patch) | |
tree | dfa29b1b2b525189fbe3c430f7506de2341ba27f | |
download | bash-framework-d0b41fb5266b188f6642db1af49b5ed8fe564e57.tar.gz bash-framework-d0b41fb5266b188f6642db1af49b5ed8fe564e57.zip |
Initial commit
-rw-r--r-- | .gitattributes | 4 | ||||
-rw-r--r-- | .gitignore | 24 | ||||
-rw-r--r-- | lib/array/contains.sh | 7 | ||||
-rwxr-xr-x | lib/bootstrap.sh | 120 | ||||
-rw-r--r-- | lib/string/GetSpaces.sh | 8 | ||||
-rw-r--r-- | lib/string/IsNumber.sh | 6 | ||||
-rw-r--r-- | lib/string/SanitizeForVariable.sh | 4 | ||||
-rw-r--r-- | lib/string/SlashReplacement.sh | 17 | ||||
-rw-r--r-- | lib/string/UUID.sh | 24 | ||||
-rw-r--r-- | lib/util/bash4.sh | 1 | ||||
-rw-r--r-- | lib/util/command.sh | 40 | ||||
-rw-r--r-- | lib/util/exit_codes.sh | 24 | ||||
-rw-r--r-- | vision | 20 | ||||
-rwxr-xr-x | your-script.sh | 16 |
14 files changed, 315 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f0b2165 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Auto detect text files and perform LF normalization +* text eol=lf +*.ico binary +*.png binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c76efe --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +## Directory-based project format: +.infinity/ +.bashful/ +libX/ + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + diff --git a/lib/array/contains.sh b/lib/array/contains.sh new file mode 100644 index 0000000..ddfa89b --- /dev/null +++ b/lib/array/contains.sh @@ -0,0 +1,7 @@ +Array::contains() { + local element + for element in "${@:2}"; do + [[ "$element" = "$1" ]] && return + done + false; return +} diff --git a/lib/bootstrap.sh b/lib/bootstrap.sh new file mode 100755 index 0000000..10593c3 --- /dev/null +++ b/lib/bootstrap.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash + +########################### +### BOOTSTRAP FUNCTIONS ### +########################### + +# $1 : relative filename +File::absolute_path() { + local file="$1" + if [ "${file::1}" == "/" ]; then + [ -r "$file" ] || return + printf "%s\n" "$file" + return + fi + local file_basename="${file##*/}" + local file_path="$(realpath "$file")" + local file_path="${file_path%/*}" + + [ -r "$file_path/$file_basename" ] || return + printf "%s\n" "$file_path/$file_basename" +} + +System::SourceFile() { + local libPath="${1:?no file provided}"; shift + + libPath="$(File::absolute_path "$libPath")" || return + + ## already imported? -> let's return + # if declare -f "$libPath" &> /dev/null && + if [ "${_VOID_ALLOW_FILERELOAD}" == 'false' ] \ + && [ -n "${_VOID_IMPORTS[*]}" ] \ + && Array::contains "$libPath" "${_VOID_IMPORTS[@]}" + then + true; return + fi + + # Check syntax before + /usr/bin/env bash -n "$libPath" \ + && builtin source "$libPath" "$@" \ + && _VOID_IMPORTS+=( "$libPath" ) +} + +System::SourcePath() { + local file libPath="${1:?no path specified}" + + shift + if [ -d "$libPath" ]; then + for file in "$libPath"/*.sh; do + System::SourceFile "$file" "$@" || return + done + return + fi + System::SourceFile "$libPath" "$@" \ + || System::SourceFile "${libPath}.sh" "$@" +} + +System::ImportOne() { + local libPath="$1" + local requestedPath="$libPath" + + shift + [[ "$requestedPath" == './'* ]] && requestedPath="${requestedPath:2}" + requestedPath="${_VOID_PATH}/${requestedPath}" + + # try relative to parent script + # try with parent + # try without parent + # try global library + # try local library + { + local localPath="$( cd "${BASH_SOURCE[1]%/*}" && pwd )" + localPath="${localPath}/${libPath}" + System::SourcePath "${localPath}" "$@" + } && return + + System::SourcePath "${requestedPath}" "$@" \ + || System::SourcePath "${libPath}" "$@" \ + || System::SourcePath "${_VOID_LIBPATH}/${libPath}" "$@" \ + || System::SourcePath "${_VOID_PATH}/${libPath}" "$@" +} + +System::Import() { + local libPath + for libPath in "$@"; do + System::ImportOne "$libPath" + done +} + +## note: aliases are visible inside functions only if +## they were initialized AFTER they were created +## this is the reason why we have to load files in a specific order +System::Bootstrap() { + System::Import array/contains && return + printf "FATAL ERROR: Unable to bootstrap (missing lib directory?)\n" 1>&2 + false; exit +} + +###################### +## INITIALZE SYSTEM ## +###################### + +# From: http://wiki.bash-hackers.org/scripting/debuggingtips +export PS4='+(${BASH_SOURCE##*/}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + +# return the highest exitcode in a chain of pipes. +set -o pipefail + +_VOID_LIBPATH="$(realpath "${BASH_SOURCE[0]}")" +declare -g -r _VOID_LIBPATH="${_VOID_LIBPATH%/*}" +declare -g -r _VOID_PATH="${_VOID_LIBPATH%/*}" + +declare -g -a _VOID_IMPORTS + +: ${_VOID_ALLOW_FILERELOAD:=false} + +import() { + eval "System::Import $@" +} + +System::Bootstrap && declare -g -r _VOID_BOOTSTRAPPED=true diff --git a/lib/string/GetSpaces.sh b/lib/string/GetSpaces.sh new file mode 100644 index 0000000..77bdf7c --- /dev/null +++ b/lib/string/GetSpaces.sh @@ -0,0 +1,8 @@ +String::GetSpaces() { + local howMany="$1" + + if [[ "$howMany" -gt 0 ]] + then + ( printf "%*s" "$howMany" ) + fi +} diff --git a/lib/string/IsNumber.sh b/lib/string/IsNumber.sh new file mode 100644 index 0000000..7537c71 --- /dev/null +++ b/lib/string/IsNumber.sh @@ -0,0 +1,6 @@ +String::IsNumber() { + local input="$1" + [ -n "$input" ] || return + local regex='^-?[0-9]+([.][0-9]+)?$' + [[ "$input" =~ $regex ]] || return +} diff --git a/lib/string/SanitizeForVariable.sh b/lib/string/SanitizeForVariable.sh new file mode 100644 index 0000000..044b9d4 --- /dev/null +++ b/lib/string/SanitizeForVariable.sh @@ -0,0 +1,4 @@ +String::SanitizeForVariableName() { + local type="$1" + echo "${type//[^a-zA-Z0-9]/_}" +} diff --git a/lib/string/SlashReplacement.sh b/lib/string/SlashReplacement.sh new file mode 100644 index 0000000..7922d7a --- /dev/null +++ b/lib/string/SlashReplacement.sh @@ -0,0 +1,17 @@ +String::ReplaceSlashes() { + local stringToMark="$1" + + # Workaround for a Bash bug that causes string replacement to fail when a \ is in the string + local slash="\\" + local slashReplacement='_%SLASH%_' + echo "${stringToMark/$slash$slash/$slashReplacement}" +} + +String::RestoreSlashes() { + local stringToMark="$1" + + # Workaround for a Bash bug that causes string replacement to fail when a \ is in the string + local slash="\\" + local slashReplacement='_%SLASH%_' + echo "${stringToMark/$slashReplacement/$slash}" +} diff --git a/lib/string/UUID.sh b/lib/string/UUID.sh new file mode 100644 index 0000000..29cbee0 --- /dev/null +++ b/lib/string/UUID.sh @@ -0,0 +1,24 @@ +String::GenerateUUID() { + ## https://gist.github.com/markusfisch/6110640 + local N B C='89ab' + + for (( N=0; N < 16; ++N )) + do + B=$(( $RANDOM%256 )) + + case $N in + 6) + printf '4%x' $(( B%16 )) + ;; + 8) + printf '%c%x' ${C:$RANDOM%${#C}:1} $(( B%16 )) + ;; + 3 | 5 | 7 | 9) + printf '%02x-' $B + ;; + *) + printf '%02x' $B + ;; + esac + done +} diff --git a/lib/util/bash4.sh b/lib/util/bash4.sh new file mode 100644 index 0000000..75f33b7 --- /dev/null +++ b/lib/util/bash4.sh @@ -0,0 +1 @@ +[[ "${BASH_VERSINFO[0]}" -lt 4 ]] && echo "The module you are trying to load requires bash >= 4" && exit 1 || true diff --git a/lib/util/command.sh b/lib/util/command.sh new file mode 100644 index 0000000..e59afb8 --- /dev/null +++ b/lib/util/command.sh @@ -0,0 +1,40 @@ +# no dependencies + +Command::GetType() { + local name="$1" + local typeMatch=$(type -t "$name" 2> /dev/null || true) + echo "$typeMatch" +} + +Command::Exists(){ + local name="$1" + local typeMatch=$(Command::GetType "$name") + [[ "$typeMatch" == "alias" || "$typeMatch" == "function" || "$typeMatch" == "builtin" ]] +} + +Alias::Exists(){ + local name="$1" + local typeMatch=$(Command::GetType "$name") + [[ "$typeMatch" == "alias" ]] +} + +Function::Exists(){ + local name="$1" + declare -f "$name" &> /dev/null +} + +Function::GetAllStartingWith() { + local startsWith="$1" + compgen -A 'function' "$startsWith" || true +} + +Function::InjectCode() { + local functionName="$1" + local injectBefore="$2" + local injectAfter="$3" + local body=$(declare -f "$functionName") + body="${body#*{}" # trim start + body="${body%\}}" # trim end + local enter=$'\n' + eval "${functionName}() { ${enter}${injectBefore}${body}${injectAfter}${enter} }" +} diff --git a/lib/util/exit_codes.sh b/lib/util/exit_codes.sh new file mode 100644 index 0000000..e9f322a --- /dev/null +++ b/lib/util/exit_codes.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# exits +# +# Those values are come from /usr/include/sysexits.h +# + +# successful termination +Util_ExitCode_OK=0 +Util_ExitCode_USAGE=64 # command line usage error +Util_ExitCode_DATAERR=65 # data format error +Util_ExitCode_NOINPUT=66 # cannot open input +Util_ExitCode_NOUSER=67 # addressee unknown +Util_ExitCode_NOHOST=68 # host name unknown +Util_ExitCode_UNAVAILABLE=69 # service unavailable +Util_ExitCode_SOFTWARE=70 # internal software error +Util_ExitCode_OSERR=71 # system error (e.g., can't fork) +Util_ExitCode_OSFILE=72 # critical OS file missing +Util_ExitCode_CANTCREAT=73 # can't create (user) output file +Util_ExitCode_IOERR=74 # input/output error +Util_ExitCode_TEMPFAIL=75 # temp failure; user is invited to retry +Util_ExitCode_PROTOCOL=76 # remote error in protocol +Util_ExitCode_NOPERM=77 # permission denied +Util_ExitCode_CONFIG=78 # configuration error diff --git a/vision b/vision new file mode 100644 index 0000000..127900b --- /dev/null +++ b/vision @@ -0,0 +1,20 @@ +== VOID BASH FRAMEWORK == +* a bash standard library + +* similiar to infinity framework + - much simpler, though + - no object oriented code + - removed alias declarations + +* get some ideas from bashful +* generalize code from previously made projects (e.g. radio-sh) + +* should be lightweight and as simple as possible + +* libraries should have a distinctive name and a version number + +* allow import of specific function in library ? + - import util/io.msg + - substring after dot is the specific function + - support rename of function? + import util/io.msg as IO.msg diff --git a/your-script.sh b/your-script.sh new file mode 100755 index 0000000..82c3ccd --- /dev/null +++ b/your-script.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +## BOOTSTRAP ## +workdir="$(realpath "${BASH_SOURCE[0]}")" +readonly workdir="${workdir%/*}" +. "$workdir"/lib/bootstrap.sh + +## MAIN EXAMPLES ## +import string/IsNumber +# Files not in dir/ can also be imported: +# import my_file.sh +# import my_dir/my_file.sh + +printf "imported: %s\n" "${_VOID_IMPORTS[@]}" + +## YOUR CODE GOES HERE ## |