From bbf7e1736c1e735f711dcb6aaa3c949b14efd25e Mon Sep 17 00:00:00 2001 From: Benjamin Lupton Date: Thu, 2 Jan 2025 00:28:28 +0800 Subject: [PATCH] redo `is-*` commands, redo `fs-rm`, add `dorothy commands` - `dorothy`: add `commands` action, to list the available commands - `echo-style`: fix `--=` usage causing crash - `fs-own`: support variant spelling of `recursive` option - `fs-rm`: redo earlier rewrites to something more robust and intuitive - `fs-trim`: improve logic - `gocryptfs-helper`: use `fs-structure`, use `fs-rm --no-confirm-if-empty` to maintain prior behaviour - `is-broken-symlink`, `is-directory`, `is-empty-directory`, `is-empty-file`, `is-executable`, `is-file`, `is-missing`, `is-nonempty-file`, `is-not-directory`, `is-not-symlink`, `is-present`, `is-readable`, `is-symlink`, `is-writable`: discreet and consistent exit statuses - promote `is-nonempty-file` from beta to stable - `mount-helper`: use `fs-rm --no-confirm-if-empty` to maintain prior behaviour - `setup-util`: use `fs-structure` - add `setup-util-firefox` - `ssh-helper`: add `fs-rm --reason` to maintain prior UX consistency - `eject-all`: use `fs-structure` --- commands.beta/convert-helper | 4 +- commands.beta/eject-all | 4 +- commands.beta/is-nonempty-file | 109 ------- commands.beta/is-nonempty-file.bash | 22 -- commands/dorothy | 27 +- commands/echo-style | 12 +- commands/fs-own | 4 +- commands/fs-rm | 423 +++++++++++++++++++++------- commands/fs-trim | 9 +- commands/get-font | 2 +- commands/gocryptfs-helper | 6 +- commands/is-broken-symlink | 47 ++-- commands/is-broken-symlink.bash | 19 +- commands/is-directory | 56 ++-- commands/is-directory.bash | 14 +- commands/is-empty-directory | 79 +++++- commands/is-empty-directory.bash | 19 +- commands/is-empty-file | 81 +++++- commands/is-empty-file.bash | 23 +- commands/is-executable | 76 +++-- commands/is-executable.bash | 5 +- commands/is-file | 49 ++-- commands/is-file.bash | 14 +- commands/is-missing | 37 +-- commands/is-missing.bash | 4 +- commands/is-nonempty-file | 165 +++++++++++ commands/is-nonempty-file.bash | 34 +++ commands/is-not-directory | 100 ++++++- commands/is-not-directory.bash | 14 +- commands/is-not-symlink | 52 ++-- commands/is-not-symlink.bash | 7 +- commands/is-present | 21 +- commands/is-present.bash | 2 +- commands/is-readable | 62 ++-- commands/is-readable.bash | 5 +- commands/is-symlink | 37 ++- commands/is-symlink.bash | 9 +- commands/is-writable | 58 ++-- commands/is-writable.bash | 5 +- commands/mount-helper | 10 +- commands/setup-util | 4 +- commands/setup-util-firefox | 30 ++ commands/ssh-helper | 5 +- 43 files changed, 1270 insertions(+), 495 deletions(-) delete mode 100755 commands.beta/is-nonempty-file delete mode 100755 commands.beta/is-nonempty-file.bash create mode 100755 commands/is-nonempty-file create mode 100755 commands/is-nonempty-file.bash create mode 100755 commands/setup-util-firefox diff --git a/commands.beta/convert-helper b/commands.beta/convert-helper index 964d4fd6e..bf8423089 100755 --- a/commands.beta/convert-helper +++ b/commands.beta/convert-helper @@ -148,7 +148,7 @@ function convert_helper() ( outfile="$filename [trimmed].m4a" run ffmpeg -y -i "$path" -af 'silenceremove=stop_periods=-1:stop_duration=0.5:stop_threshold=-60dB' "$outfile" if [[ $option_delete == 'yes' ]]; then - fs-rm --no-confirm --quiet -- "$path" + fs-rm --quiet --no-confirm -- "$path" fi filename="$filename [trimmed]" path="$outfile" @@ -199,7 +199,7 @@ function convert_helper() ( whisper --model base --language English --output_format srt "$path" fi if [[ $option_delete == 'yes' ]]; then - fs-rm --no-confirm --quiet -- "$path" + fs-rm --quiet --no-confirm -- "$path" fi done ) diff --git a/commands.beta/eject-all b/commands.beta/eject-all index 75f128807..9a16f0eaf 100755 --- a/commands.beta/eject-all +++ b/commands.beta/eject-all @@ -38,7 +38,7 @@ function eject_all() ( echo-style --h1='eject-all' echo-style --h2='before' - ls /Volumes + fs-structure -- /Volumes echo-style --g2='before' eval-helper --quiet \ @@ -48,7 +48,7 @@ function eject_all() ( -- osascript -e 'tell application "Finder" to eject (every disk whose ejectable is true)' echo-style --h2='after' - ls /Volumes + fs-structure -- /Volumes echo-style --g2='after' echo-style --h1='eject-all' diff --git a/commands.beta/is-nonempty-file b/commands.beta/is-nonempty-file deleted file mode 100755 index bf2ca1da9..000000000 --- a/commands.beta/is-nonempty-file +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bash - -function is_nonempty_file_test() ( - source "$DOROTHY/sources/bash.bash" - echo-style --h1="TEST: $0" - - local dir file - dir="$(fs-temp --directory='is-nonempty-file' --directory --touch)" - file="$(fs-temp --directory='is-nonempty-file' --file --touch)" - - eval-tester --name='no args' --status=22 --ignore-stderr \ - -- is-nonempty-file -- - - eval-tester --name='empty args' --status=22 \ - -- is-nonempty-file -- '' '' - - eval-tester --name='missing' --status=9 \ - -- is-nonempty-file -- "$DOROTHY/this-does-not-exist" - - eval-tester --name='empty dirs' --status=9 \ - -- is-nonempty-file -- "$dir" "$dir" - - eval-tester --name='empty files' --status=1 \ - -- is-nonempty-file -- "$file" "$file" - - eval-tester --name='non-empty dir' --status=9 \ - -- is-nonempty-file -- "$DOROTHY" - - eval-tester --name='non-empty file' \ - -- is-nonempty-file -- "$DOROTHY/README.md" - - echo-style --g1="TEST: $0" - return 0 -) -function is_nonempty_file() ( - source "$DOROTHY/sources/bash.bash" - - # ===================================== - # Arguments - - function help { - cat <<-EOF >/dev/stderr - ABOUT: - Checks if a is a file that has contents, aka a file that is not zero-length. - - USAGE: - is-nonempty-file [...options] [--] ... - - OPTIONS: - --sudo - If specified, use sudo on filesystem interactions. - --user= - --group= - If specified use this user and/or group for filesystem interactions. - - RETURNS: - [0] if all s are files that have content. - [1] if any s are not a file that has content. - [9] if any s are not a file. - [22] if empty arguments are provided. - EOF - if [[ $# -ne 0 ]]; then - echo-error "$@" - fi - return 22 # EINVAL 22 Invalid argument - } - - # process - local item option_inputs=() option_sudo='no' option_user='' option_group='' - while [[ $# -ne 0 ]]; do - item="$1" - shift - case "$item" in - '--help' | '-h') help ;; - '--no-sudo'* | '--sudo'*) - option_sudo="$(get-flag-value --affirmative --fallback="$option_sudo" -- "$item")" - ;; - '--user='*) option_user="${item#*=}" ;; - '--group='*) option_group="${item#*=}" ;; - '--') - option_inputs+=("$@") - shift "$#" - break - ;; - '--'*) help "An unrecognised flag was provided: $item" ;; - *) option_inputs+=("$item") ;; - esac - done - - # check - if [[ ${#option_inputs[@]} -eq 0 ]]; then - help "No s provided." - fi - - # ===================================== - # Action - - sudo-helper --inherit --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- is-nonempty-file.bash -- "${option_inputs[@]}" - return -) - -# fire if invoked standalone -if [[ $0 == "${BASH_SOURCE[0]}" ]]; then - if [[ $* == '--test' ]]; then - is_nonempty_file_test - else - is_nonempty_file "$@" - fi -fi diff --git a/commands.beta/is-nonempty-file.bash b/commands.beta/is-nonempty-file.bash deleted file mode 100755 index fa51e2b5d..000000000 --- a/commands.beta/is-nonempty-file.bash +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -if [[ $1 == '--' ]]; then - shift -fi -if [[ $# -eq 0 ]]; then - exit 22 # EINVAL 22 Invalid argument -fi -while [[ $# -ne 0 ]]; do - if [[ -z $1 ]]; then - exit 22 # EINVAL 22 Invalid argument - fi - if [[ ! -f $1 ]]; then - # not a file nor symlink to a file - exit 9 # EBADF 9 Bad file descriptor - fi - if [[ ! -s $1 ]]; then - exit 1 - fi - shift -done -exit 0 diff --git a/commands/dorothy b/commands/dorothy index 70b87c02e..e96cc99f1 100755 --- a/commands/dorothy +++ b/commands/dorothy @@ -144,6 +144,10 @@ function dorothy_() ( Updates Dorothy to the latest version. + dorothy commands + + List all the Dorothy commands that are available to you. + dorothy permissions Correct permissions of new Dorothy commands, ensuring they are executable, and that git is aware of them. @@ -264,6 +268,9 @@ function dorothy_() ( 'todo' | 'todos') action='todos' ;; + 'commands') + action='commands' + ;; '--'*) help "An unrecognised flag was provided: $item" ;; *) help "An unrecognised argument was provided: $item" ;; esac @@ -1533,7 +1540,7 @@ function dorothy_() ( DOROTHY_FORCE_LOAD=yes DOROTHY_THEME_OVERRIDE='$theme' . '$DOROTHY/init.sh' - echo-style 'Your are now using the ' --invert='$ACTIVE_POSIX_SHELL' ' shell with Dorothy loaded from ' --code='$DOROTHY' \$'\n' 'Use the ' --code='exit' ' command to return to your parent shell.' \$'\n' 'Use ' --code='ls $DOROTHY/commands' ' to see available commands.' + echo-style 'Your are now using the ' --invert='$ACTIVE_POSIX_SHELL' ' shell with Dorothy loaded from ' --code='$DOROTHY' \$'\n' 'Use the ' --code='exit' ' command to return to your parent shell.' \$'\n' 'Use ' --code='dorothy commands' ' to see available commands.' EOF ) return @@ -1544,7 +1551,7 @@ function dorothy_() ( #!/usr/bin/env nu \$env.DOROTHY_THEME_OVERRIDE = '$theme' source '$DOROTHY/init.nu' - echo-style 'Your are now using the ' --invert='nu' ' shell with Dorothy loaded from ' --code='$DOROTHY' "\n" 'Use the ' --code='exit' ' command to return to your parent shell.' "\n" 'Use ' --code='ls $DOROTHY/commands' ' to see available commands.' + echo-style 'Your are now using the ' --invert='nu' ' shell with Dorothy loaded from ' --code='$DOROTHY' "\n" 'Use the ' --code='exit' ' command to return to your parent shell.' "\n" 'Use ' --code='dorothy commands' ' to see available commands.' EOF )" return @@ -1555,7 +1562,7 @@ function dorothy_() ( #!/usr/bin/env fish set DOROTHY_THEME_OVERRIDE '$theme' source '$DOROTHY/init.fish' - echo-style 'Your are now using the ' --invert='fish' ' shell with Dorothy loaded from ' --code='$DOROTHY' \n 'Use the ' --code='exit' ' command to return to your parent shell.' \n 'Use ' --code='ls $DOROTHY/commands' ' to see available commands.' + echo-style 'Your are now using the ' --invert='fish' ' shell with Dorothy loaded from ' --code='$DOROTHY' \n 'Use the ' --code='exit' ' command to return to your parent shell.' \n 'Use ' --code='dorothy commands' ' to see available commands.' EOF )" return @@ -1597,6 +1604,20 @@ function dorothy_() ( # ===================================== # Actions: ... + function act_commands { + # pre-requisites + assert_dorothy_configured + + local paths=() + mapfile -t paths < <(echo-if-present -- "$DOROTHY/commands" "$DOROTHY/commands.beta" "$DOROTHY/user/commands" "$DOROTHY/user/commands.local") + setup-util-eza --quiet --optional + if __command_exists eza; then + eza "${paths[@]}" + else + ls "${paths[@]}" + fi + } + function act_permissions { # pre-requisites assert_dorothy_configured diff --git a/commands/echo-style b/commands/echo-style index 3e412c680..81c9ef264 100755 --- a/commands/echo-style +++ b/commands/echo-style @@ -220,14 +220,14 @@ function echo_style() ( } for item in "${items[@]}"; do # check flag status - if [[ ${item:0:2} != '--' || $item == '--' || $item == '--=' ]]; then - # not a flag, just item content, e.g. 'Hello', '--', '--=' - buffer_left+="$item" - continue - elif [[ ${item:0:3} == '--=' ]]; then - # empty flag, just item content, e.g. '--=Hello' + if [[ ${item:0:3} == '--=' ]]; then + # empty flag, just item content, e.g. '--=Hello', --=--= buffer_left+="${item:3}" continue + elif [[ ${item:0:2} != '--' || $item == '--' ]]; then + # not a flag, just item content, e.g. 'Hello', '--' + buffer_left+="$item" + continue fi flag="${item:2}" item_content='' diff --git a/commands/fs-own b/commands/fs-own index 82782f68c..0076eb6a3 100755 --- a/commands/fs-own +++ b/commands/fs-own @@ -142,7 +142,7 @@ function fs_own() ( '--no-optional'* | '--optional'*) option_optional="$(get-flag-value --affirmative --fallback="$option_optional" -- "$item")" ;; - '--no-recursive'* | '--recursive'*) + '--no-recursive'* | '--recursive'* | '--no-recurse'* | '--recurse'* | '--no-recursion'* | '--recursion'*) option_recursive="$(get-flag-value --affirmative --fallback="$option_recursive" -- "$item")" ;; '--permissions='*) option_permissions="${item#*=}" ;; @@ -220,6 +220,7 @@ function fs_own() ( # -f: Do not display a diagnostic message when chmod could not modify the mode for file, nor modify the exit status to reflect such (macos) # -f, --silent, --quiet: suppress most error messages (ubuntu) # -R/--recursive: self-explanatory + # -h: If the file is a symbolic link, change the mode of the link itself rather than the file that the link points to. local ch_args=() if [[ $option_recursive == 'yes' ]]; then if is-mac || is-alpine; then @@ -369,6 +370,7 @@ function fs_own() ( if [[ ${#sudo_as_user_args[@]} -ne 0 ]]; then cmd+=("${sudo_as_user_args[@]}") fi + # removing readable perms on a directory, while recursing, will cause permission denied failure cmd+=(chmod "${ch_args[@]}" "$option_permissions" "${paths[@]}") "${cmd[@]}" # eval fi diff --git a/commands/fs-rm b/commands/fs-rm index f46b72340..afb8cb69b 100755 --- a/commands/fs-rm +++ b/commands/fs-rm @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# @todo write tests, use fs-trim for reference +# @todo write tests, use fs-trim and is-readable for reference, make sure to test unreadable dirs, files, and unreadable nested contents of a readable dir function fs_rm() ( source "$DOROTHY/sources/bash.bash" @@ -17,24 +17,48 @@ function fs_rm() ( fs-rm [...options] [--] ... OPTIONS: + --reason= + The reason for the removal. + --quiet If specified, only output errors or when user intervention is required. + --no-confirm + If specified, skip confirmations. + + --no-confirm-if-empty + If specified, skip confirmations if the file or directory is empty. + --optional If specified, doesn't fail if no s were provided. - --no-confirm - If specified, skip confirms. - --sudo If specified, use sudo when removing the files. - --trash - If specified, move the file to trash instead of deleting it immediately. - --user= --group= If specified run the removal commands as this and . + + --readable + Make the and its content readable. + + --trim + Trim redundant content and re-evaluate. + Requires to be readable. + + --trash + Remove the by moving it to trash, if the system supports it. + Does not require the and its content to be readable. + Cannot be used with --sudo, --user=, --group=. + + --delete + Remove the by deleting it immediately, without recovery. + If the is a directory, then the and its content must be readable. + + QUIRKS: + If --no-confirm is not provided, or if an error or complication is encountered, the user will be prompted for which action to take. + Delete will fail if it encounters a directory that is unreadable. + Removing a parent directory is forbidden, a the shell would crash. EOF if [[ $# -ne 0 ]]; then echo-error "$@" @@ -43,7 +67,7 @@ function fs_rm() ( } # process - local item option_quiet='no' option_inputs=() option_optional='no' option_confirm='yes' option_sudo='no' option_trash='' option_user='' option_group='' + local item option_quiet='no' option_inputs=() option_reason='' option_optional='no' option_confirm='' option_confirm_if_empty='' option_sudo='no' option_user='' option_group='' option_readable='' option_trim='' option_trash='' option_delete='' option_preferences=() while [[ $# -ne 0 ]]; do item="$1" shift @@ -58,18 +82,43 @@ function fs_rm() ( '--no-optional'* | '--optional'*) option_optional="$(get-flag-value --affirmative --fallback="$option_optional" -- "$item")" ;; + '--no-confirm-if-empty'* | '--confirm-if-empty'*) # must be before --no-confirm as otherwise --no-confirm* will match it + option_confirm_if_empty="$(get-flag-value --affirmative --fallback="$option_confirm_if_empty" -- "$item")" + ;; '--no-confirm'* | '--confirm'*) option_confirm="$(get-flag-value --affirmative --fallback="$option_confirm" -- "$item")" ;; '--no-sudo'* | '--sudo'*) option_sudo="$(get-flag-value --affirmative --fallback="$option_sudo" -- "$item")" ;; + '--no-readable'* | '--readable'*) + option_readable="$(get-flag-value --affirmative --fallback="$option_readable" -- "$item")" + if [[ $option_readable == 'yes' ]]; then + option_preferences+=('readable') + fi + ;; + '--no-trim'* | '--trim'*) + option_trim="$(get-flag-value --affirmative --fallback="$option_trim" -- "$item")" + if [[ $option_trim == 'yes' ]]; then + option_preferences+=('trim') + fi + ;; '--no-trash'* | '--trash'*) option_trash="$(get-flag-value --affirmative --fallback="$option_trash" -- "$item")" + if [[ $option_trash == 'yes' ]]; then + option_preferences+=('trash') + fi + ;; + '--no-delete'* | '--delete'*) + option_delete="$(get-flag-value --affirmative --fallback="$option_delete" -- "$item")" + if [[ $option_delete == 'yes' ]]; then + option_preferences+=('delete') + fi ;; '--user='*) option_user="${item#*=}" ;; '--group='*) option_group="${item#*=}" ;; '--path='*) option_inputs+=("${item#*=}") ;; + '--reason='*) option_reason="${item#*=}" ;; '--') option_inputs+=("$@") shift $# @@ -89,65 +138,70 @@ function fs_rm() ( fi fi + # adjust reason + local styled_reason='' + if [[ -n $option_reason ]]; then + styled_reason="$( + echo-style --notice1="$option_reason" + )"$'\n' + fi + # ===================================== # Dependencies - local trash_and_delete_options=() trash_or_delete_default + # prep menu + local \ + readable_options=('readable' 'Make contents readable, then reevaluate') \ + trim_options=('trim' 'Trim redundant contents, then reevaluate') \ + trash_options=('trash' 'Move to trash') \ + delete_options=('delete' 'Delete immediately, without recovery') \ + again_options=('again' "I've done manual changes, reevaluate") \ + abort_options=('abort' 'Keep it, and abort the requested removal') + + # adjust options with warnings if [[ $option_sudo == 'yes' || -n $option_user || -n $option_group ]]; then if [[ $option_trash == 'yes' ]]; then echo-style --dim='Moving to trash is not supported for sudo, falling back to immediate deletion for: ' --code="${option_inputs[*]}" >/dev/stderr + option_trash='no' fi - option_trash='no' fi + + # adjust options with warnings, and feature detection if [[ $option_trash != 'no' ]]; then - setup-util-trash --quiet --optional --no-fallback - fi - if __command_missing -- trash; then - if [[ $option_trash == 'yes' ]]; then - echo-style --dim='Moving to trash is not available, falling back to immediate deletion for: ' --code="${option_inputs[*]}" >/dev/stderr + if [[ -z $option_trash && $option_delete != 'yes' ]] && __command_missing -- trash; then + setup-util-trash --quiet --optional --no-fallback fi - option_trash='no' - fi - if [[ $option_trash == 'no' ]]; then - trash_or_delete_default='delete' - trash_and_delete_options+=( - delete 'Delete it' - ) - else - trash_and_delete_options+=( - trash 'Move it to trash' - delete 'Delete it' - ) - if [[ $option_trash == 'yes' ]]; then - trash_or_delete_default='trash' - else - trash_or_delete_default=$'trash\ndelete' + if __command_missing -- trash; then + if [[ $option_trash == 'yes' ]]; then + echo-style --dim='Moving to trash is not available, falling back to immediate deletion for: ' --code="${option_inputs[*]}" >/dev/stderr + option_trash='no' + fi fi fi + # prep defaults if none + # do not do readable or trash or trim, as if we don't have permissions, we want to fail to let the user aware + # do not do trim, as it is redundant if we are empty + if [[ ${#option_preferences[@]} -eq 0 ]]; then + option_preferences=('delete') + fi + # ===================================== # Action function __wrap { sudo-helper --no-wrap="$option_quiet" --quiet="$option_quiet" --inherit --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$@" } - function __rm { - if [[ $option_trash == 'yes' ]]; then - __wrap trash "$path" || : - else - __wrap rm -rf "$path" || : - fi - } local CONCLUSION selves mapfile -t selves < <(fs-parents --self --root -- .) function do_rm { - local input="$1" title='' body choice + local input="$1" path title='' body choices choice is defaults default_args preferences=("${option_preferences[@]}") temp had_failure='' is_readable is_empty can_readable can_trim can_trash can_delete default_for_noconfirm # is the input already removed? if is-missing --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$input"; then CONCLUSION="$( - echo-style --green='was previously removed.' + echo-style --success='previously removed.' )" return 0 fi @@ -156,104 +210,257 @@ function fs_rm() ( path="$(fs-absolute --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$input")" if is-needle --needle="$path" -- "${selves[@]}"; then CONCLUSION="$( - echo-style --red='contains the CWD, denied.' + echo-style --error='contains the CWD, denied.' )" echo-style --error1='Denied removing a lineage of the current working directory, change the working directory to elsewhere and try again: ' --code-error1="$path" >/dev/stderr return 1 fi # it is remaining, so prompt on what to do - choice='trim' # this is outside the loop to inherit the last preference within the loop while :; do - # is the path is empty or a broken symlink, skip any confirm and remove it - if is-not-symlink --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - if is-empty-file --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path" || is-empty-directory --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - __rm "$path" - if is-present --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - CONCLUSION="$( - echo-style --red='is empty, however it failed to remove.' - )" - return 66 # ENOTEMPTY 66 Directory not empty + CONCLUSION='' + is='' + choices=() + defaults=() + default_args=() + is_readable='' + is_directory='' + is_empty='' + can_readable='' + can_trim='' + can_trash='' + can_delete='' + default_for_noconfirm='' + + # if not quiet, dump the progress + if [[ -n $CONCLUSION && $option_quiet == 'no' ]]; then + echo-style --code="$path" " $CONCLUSION" >/dev/stderr + fi + + # if readable, then we can detect emptiness + if ! is-readable --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + is_readable='no' + is='non-readable' + else + is_readable='yes' + if is-broken-symlink --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + is_directory='no' + is='broken symlink' + elif is-not-symlink --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + if is-empty-file --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + is_empty='yes' + is_directory='no' + is='empty file' + elif is-empty-directory --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + is_empty='yes' + is_directory='yes' + is='empty directory' + else + is='non-empty' fi - CONCLUSION="$( - echo-style --green='was empty, it was removed.' - )" - return 0 + else + is='symlink' + fi + fi + if [[ -z $is_directory ]]; then + if is-directory --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + is_directory='yes' + if [[ -z $is ]]; then + is='directory' + else + is+=' directory' + fi + else + is_directory='no' + # don't bother with commentary on files + fi + fi + + # adjust options + if [[ $is_readable == 'no' ]]; then + can_readable='yes' + # no trim + # can trash + if [[ $option_trash != 'no' ]]; then + can_trash='yes' + fi + # no delete if directory + if ! [[ $option_delete == 'no' || $is_directory == 'yes' ]]; then + can_delete='yes' + fi + else + if [[ $option_readable == 'yes' || $had_failure == 'yes' ]]; then + can_readable='yes' + fi + if [[ $option_trim != 'no' ]]; then + can_trim='yes' + fi + if [[ $option_trash != 'no' ]]; then + can_trash='yes' + fi + if [[ $option_delete != 'no' ]]; then + can_delete='yes' + fi + fi + + # adjust menu choices and defaults + if [[ $can_readable == 'yes' ]]; then + choices+=("${readable_options[@]}") + fi + if [[ $can_delete == 'yes' ]]; then + choices+=("${delete_options[@]}") + fi + if [[ $can_trash == 'yes' ]]; then + choices+=("${trash_options[@]}") + fi + if [[ $can_trim == 'yes' ]]; then + choices+=("${trim_options[@]}") + fi + choices+=( + "${again_options[@]}" + "${abort_options[@]}" + ) + if [[ ${#preferences[@]} -ne 0 ]]; then + for item in "${preferences[@]}"; do + case "$item" in + 'readable') + if [[ $can_readable == 'yes' ]]; then + defaults+=('readable') + fi + ;; + 'trim') + if [[ $can_trim == 'yes' && $had_failure != 'yes' ]]; then + defaults+=('trim') + fi + ;; + 'trash') + if [[ $can_trash == 'yes' && $had_failure != 'yes' ]]; then + defaults+=('trash') + fi + ;; + 'delete') + if [[ $can_delete == 'yes' && $had_failure != 'yes' ]]; then + defaults+=('delete') + fi + ;; + esac + done + fi + if [[ ${#defaults[@]} -eq 0 ]]; then + if [[ $option_confirm != 'no' && $had_failure == 'yes' ]]; then + defaults+=('readable' 'trash') + fi + fi + if [[ ${#defaults[@]} -ne 0 ]]; then + if [[ $option_confirm == 'no' ]]; then + default_for_noconfirm="${defaults[0]}" + elif [[ $is_empty == 'yes' && $option_confirm_if_empty == 'no' ]]; then + default_for_noconfirm='delete' fi - elif is-broken-symlink --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - __rm "$path" - if is-present --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - CONCLUSION="$( - echo-style --red='is a broken-symlink, however it failed to remove.' + for item in "${defaults[@]}"; do + default_args+=("--default-exact=$item") + done + fi + # else allow the prompt + + # prompt + if [[ -n $default_for_noconfirm ]]; then + choice="$default_for_noconfirm" + else + if [[ -n $is ]]; then + title="$( + echo-style --="$styled_reason" --notice1="How to remove $is " --code-notice1="$path" )" - return 66 # ENOTEMPTY 66 Directory not empty + else + title="$( + echo-style --="$styled_reason" --notice1='How to remove ' --code-notice1="$path" + )" + fi + if [[ $is_readable == 'no' && $is_directory == 'yes' ]]; then + # cannot ls/eza if directory is unreadable + body='' + else + body="$(echo-style --reset)$(fs-structure --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path")" fi - CONCLUSION="$( - echo-style --green='was a broken-symlink, it was removed.' + choice="$( + choose --required "$title" "$body" --truncate-body "${default_args[@]}" --label -- "${choices[@]}" )" - return 0 fi - # confirm - if [[ -z $title ]]; then - title="$( - echo-style --notice1='The path is non-empty and queued for removal, what should be done?' --newline \ - --code-notice1="$path" - )" + # remove the predetermined choice from the preferences from next times defaults + # this is complicated as readable may have been provided to solve a nested readable issue, but not available yet due to is-readable showing it is readable, which is then changed to maybe on failure, allowing readable to become available for the default: this only applies to --confirm + if [[ ${#defaults[@]} -ne 0 && $choice == "${defaults[0]}" ]]; then + temp=() + for item in "${preferences[@]}"; do + if [[ $item != "$choice" ]]; then + temp+=("$item") + fi + done + preferences=("${temp[@]}") fi - body="$(echo-style --reset)$(fs-structure --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path")" - choice="$( - choose "$title" "$body" --truncate-body --confirm-default="$option_confirm" --defaults-exact="$choice" --label -- \ - trim 'Trim its redundant content and re-evaluate' \ - "${trash_and_delete_options[@]}" \ - abort 'Keep it, and abort the requested removal' - )" # handle - if [[ $choice == 'trim' ]]; then - fs-trim --confirm="$option_confirm" --no-all="$option_confirm" --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path" - if is-missing --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - CONCLUSION="$( - echo-style --green='was removed by trimming.' - )" - return 0 + # @todo maybe at one point store the output of the failures to show them in the next menu + # however, the ideal would be to have --inline for choose + had_failure='' + case "$choice" in + 'delete') + __wrap rm -rf "$path" || { + had_failure='yes' + } + ;; + 'trash') + __wrap trash "$path" || { + had_failure='yes' + } + ;; + 'trim') + fs-trim --quiet="$option_quiet" --confirm="$option_confirm" --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path" || { + had_failure='yes' + } + ;; + 'readable') + fs-own --quiet="$option_quiet" --permissions='+r' --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path" + ;; + 'abort') + if [[ -n $is ]]; then + CONCLUSION="$is " fi - choice="$trash_or_delete_default" - elif [[ $choice == 'abort' || -z $choice ]]; then - CONCLUSION="$( - echo-style --yellow='was kept.' + CONCLUSION+="$( + echo-style --error='failed to remove' )" + echo-style --code="$path" " $CONCLUSION" >/dev/stderr return 66 # ENOTEMPTY 66 Directory not empty - elif [[ $choice == 'trash' ]]; then - __wrap trash "$path" || : - break - elif [[ $choice == 'delete' ]]; then - __wrap rm -rf "$path" || : + ;; + 'again' | '' | *) + continue + ;; + esac + + # check after trash/delete + if is-missing --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then + if [[ -n $is ]]; then + CONCLUSION="$is " + fi + CONCLUSION+="$( + echo-style --success='removed' + )" break fi done - - # check after trash/delete - if is-present --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - CONCLUSION="$( - echo-style --red='is non-empty, it failed to remove.' - )" - return 66 # ENOTEMPTY 66 Directory not empty - fi - CONCLUSION="$( - echo-style --green='was non-empty, it was manually removed.' - )" return 0 } function act { local input="$1" title rm_status - CONCLUSION='' if [[ $option_quiet == 'yes' ]]; then do_rm "$input" return else title='fs-rm' + if [[ $option_trim == 'yes' ]]; then + title+=' --trim' + fi if [[ $option_trash == 'yes' ]]; then title+=' --trash' fi @@ -267,12 +474,12 @@ function fs_rm() ( title+=" --group=$option_group" fi title+=" $(echo-escape-command -- "$input")" - echo-style --h2="$title" + echo-style --h2="$title" >/dev/stderr eval_capture --statusvar=rm_status -- do_rm "$input" if [[ $rm_status -eq 0 ]]; then - echo-style --g2="$title" " $CONCLUSION" + echo-style --g2="$title" " $CONCLUSION" >/dev/stderr else - echo-style --e2="$title" " $CONCLUSION" + echo-style --e2="$title" " $CONCLUSION" >/dev/stderr return "$rm_status" fi fi diff --git a/commands/fs-trim b/commands/fs-trim index 7e96303ea..0fbfbc70f 100755 --- a/commands/fs-trim +++ b/commands/fs-trim @@ -16,7 +16,7 @@ function fs_trim_test() ( local root dir_target dir_symlink file_in_dir_target file_target file_symlink file_in_dir_symlink root="$(fs-temp --directory='fs-trim-test')" # ensure anything left over from prior runs is removed - fs-rm --no-confirm -- "$root" + fs-rm --quiet --no-confirm -- "$root" __mkdirp "$root" # create a structure dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" @@ -114,7 +114,7 @@ function fs_trim() ( function help { cat <<-EOF >/dev/stderr ABOUT: - Trim s of empty files and directories, including itself it also became empty. + Trim the s of commonly redundant files and directories. USAGE: fs-trim [...options] [--] ... @@ -289,10 +289,7 @@ function fs_trim() ( function do_find { local path="$1" find_modifications find_modifications="$(fs-temp --directory='fs-trim' --file)" - while :; do - if is-not-directory --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; then - break - fi + while is-directory --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- "$path"; do : >"$find_modifications" # clear if [[ $option_cache == 'yes' ]]; then __wrap find "$path" \( "${cache_find[@]}" \) -delete -print >>"$find_modifications" diff --git a/commands/get-font b/commands/get-font index 72fe07804..01f81506c 100755 --- a/commands/get-font +++ b/commands/get-font @@ -90,7 +90,7 @@ function get_font() ( else __mkdirp "${dirs[0]}" mv -v "$HOME/.fonts/"* "${dirs[0]}" >/dev/stderr - fs-rm --no-confirm --quiet -- "$HOME/.fonts" + fs-rm --quiet --no-confirm -- "$HOME/.fonts" fi else __mkdirp "${dirs[0]}" diff --git a/commands/gocryptfs-helper b/commands/gocryptfs-helper index c207cd6ee..63e7415d9 100755 --- a/commands/gocryptfs-helper +++ b/commands/gocryptfs-helper @@ -290,7 +290,7 @@ function gocryptfs_helper() ( { echo-style --error="Something already existed at [$new_vault]..." echo-style --notice='Leaving for you to figure out.' - eval-helper --no-quiet --wrap -- ls -la "$new_vault" + fs-structure -- "$new_vault" } >/dev/stderr return 1 fi @@ -326,7 +326,7 @@ function gocryptfs_helper() ( function on_gocryptfs_finish { mount-helper --unmount --target="$old_plain" || : mount-helper --unmount --target="$new_plain" || : - fs-rm --quiet -- "$temp_dir" || : + fs-rm --quiet --no-confirm-if-empty -- "$temp_dir" || : } trap on_gocryptfs_finish EXIT @@ -346,7 +346,7 @@ function gocryptfs_helper() ( echo-style \ --error="Something already existed at [$new_vault] which was not a known vault structure..." --newline \ --notice='Leaving for you to figure out.' - eval-helper --no-quiet --wrap -- ls -la "$new_vault" + fs-structure -- "$new_vault" } >/dev/stderr return 1 fi diff --git a/commands/is-broken-symlink b/commands/is-broken-symlink index 4f01fa6c1..cc91b04db 100755 --- a/commands/is-broken-symlink +++ b/commands/is-broken-symlink @@ -3,37 +3,48 @@ function is_broken_symlink_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-broken-symlink -- eval-tester --name='empty args' --status=22 \ -- is-broken-symlink -- '' '' - eval-tester --name='missing' --status=9 \ + eval-tester --name='missing' --status=2 \ -- is-broken-symlink -- "$DOROTHY/this-does-not-exist" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-broken-symlink' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-broken-symlink' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-broken-symlink' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-broken-symlink' --file='file_symlink' --no-touch)" + eval-tester --name='dir' --status=17 \ + -- is-broken-symlink -- "$DOROTHY" + + eval-tester --name='file' --status=17 \ + -- is-broken-symlink -- "$DOROTHY/README.md" + + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-broken-symlink-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - eval-tester --name='symlink dir' --status=1 \ + # test working symlinks + eval-tester --name='symlink dir' --status=17 \ -- is-broken-symlink -- "$dir_symlink" - eval-tester --name='symlink file' --status=1 \ + eval-tester --name='symlink file' --status=17 \ -- is-broken-symlink -- "$file_symlink" - eval-tester --name='symlink dir then dir' --status=1 \ + eval-tester --name='symlink dir then dir' --status=17 \ -- is-broken-symlink -- "$dir_symlink" "$DOROTHY" - eval-tester --name='symlink file then file' --status=1 \ + eval-tester --name='symlink file then file' --status=17 \ -- is-broken-symlink -- "$file_symlink" "$DOROTHY/README.md" - eval-tester --name='symlink file then missing' --status=1 \ + eval-tester --name='symlink file then missing' --status=17 \ -- is-broken-symlink -- "$file_symlink" "$DOROTHY/this-does-not-exist" # test broken symlinks @@ -45,13 +56,13 @@ function is_broken_symlink_test() ( eval-tester --name='broken symlink file' \ -- is-broken-symlink -- "$file_symlink" - eval-tester --name='broken symlink dir then dir' --status=9 \ + eval-tester --name='broken symlink dir then dir' --status=79 \ -- is-broken-symlink -- "$dir_symlink" "$DOROTHY" - eval-tester --name='broken symlink file then file' --status=9 \ + eval-tester --name='broken symlink file then file' --status=79 \ -- is-broken-symlink -- "$file_symlink" "$DOROTHY/README.md" - eval-tester --name='broken symlink file then missing' --status=9 \ + eval-tester --name='broken symlink file then missing' --status=2 \ -- is-broken-symlink -- "$file_symlink" "$DOROTHY/this-does-not-exist" echo-style --g1="TEST: $0" @@ -67,6 +78,7 @@ function is_broken_symlink() ( cat <<-EOF >/dev/stderr ABOUT: Check if all s are a broken symlink. + Companion to [is-symlink], [is-not-symlink]. USAGE: is-broken-symlink [...options] [--] ... @@ -80,8 +92,9 @@ function is_broken_symlink() ( RETURNS: [0] if all s were a broken symlink - [1] if any s were a symlink that was not broken symlink - [9] if any s were not a symlink + [2] if a was not found + [17] if a was an unbroken symlink + [79] if a was not a symlink [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-broken-symlink.bash b/commands/is-broken-symlink.bash index c5fbc70d1..34f9f7bab 100755 --- a/commands/is-broken-symlink.bash +++ b/commands/is-broken-symlink.bash @@ -10,12 +10,19 @@ while [[ $# -ne 0 ]]; do if [[ -z $1 ]]; then exit 22 # EINVAL 22 Invalid argument fi - if [[ ! -L $1 ]]; then - # not a symlink - exit 9 # EBADF 9 Bad file descriptor - fi - if [[ -e $1 ]]; then - exit 1 + if [[ -L $1 ]]; then + # does exist: is a symlink + if [[ -e $1 ]]; then + # does exist: is a working symlink + exit 17 # EEXIST 17 File exists + fi + # does exist: is a broken symlink + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + else + # does exist: but not a symlink + exit 79 # EFTYPE 79 Inappropriate file type or format fi shift done diff --git a/commands/is-directory b/commands/is-directory index 9e6a14896..dace37e03 100755 --- a/commands/is-directory +++ b/commands/is-directory @@ -4,57 +4,79 @@ function is_directory_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-directory -- eval-tester --name='empty args' --status=22 \ -- is-directory -- '' '' - eval-tester --name='missing' --status=1 \ + eval-tester --name='missing' --status=2 \ -- is-directory -- "$DOROTHY/this-does-not-exist" eval-tester --name='directory' \ -- is-directory -- "$DOROTHY" - eval-tester --name='file' --status=1 \ + eval-tester --name='file' --status=20 \ -- is-directory -- "$DOROTHY/README.md" - eval-tester --name='file then dir' --status=1 \ + eval-tester --name='file then dir' --status=20 \ -- is-directory -- "$DOROTHY/README.md" "$DOROTHY" - eval-tester --name='dir then file' --status=1 \ + eval-tester --name='dir then file' --status=20 \ -- is-directory -- "$DOROTHY" "$DOROTHY/README.md" - eval-tester --name='dir then file then invalid' --status=1 \ + eval-tester --name='dir then file then invalid' --status=20 \ -- is-directory -- "$DOROTHY" "$DOROTHY/README.md" '' eval-tester --name='dir then invalid then file' --status=22 \ -- is-directory -- "$DOROTHY" '' "$DOROTHY/README.md" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-directory' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-directory' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-directory' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-directory' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-directory-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + # test working symlinks eval-tester --name='symlink dir' \ -- is-directory -- "$dir_symlink" - eval-tester --name='symlink file' --status=1 \ + eval-tester --name='symlink file' --status=20 \ -- is-directory -- "$file_symlink" + eval-tester --name='symlink dir then dir' \ + -- is-directory -- "$dir_symlink" "$DOROTHY" + + eval-tester --name='symlink dir then file' --status=20 \ + -- is-directory -- "$dir_symlink" "$DOROTHY/README.md" + + eval-tester --name='symlink dir then missing' --status=2 \ + -- is-directory -- "$dir_symlink" "$DOROTHY/this-does-not-exist" + # test broken symlinks fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - eval-tester --name='broken symlink dir' --status=1 \ + eval-tester --name='broken symlink dir' --status=9 \ -- is-directory -- "$dir_symlink" - eval-tester --name='broken symlink file' --status=1 \ + eval-tester --name='broken symlink file' --status=9 \ -- is-directory -- "$file_symlink" + eval-tester --name='broken symlink dir then dir' --status=9 \ + -- is-directory -- "$dir_symlink" "$DOROTHY" + + eval-tester --name='broken symlink dir then file' --status=9 \ + -- is-directory -- "$dir_symlink" "$DOROTHY/README.md" + + eval-tester --name='broken symlink dir then missing' --status=9 \ + -- is-directory -- "$dir_symlink" "$DOROTHY/this-does-not-exist" + echo-style --g1="TEST: $0" return 0 ) @@ -81,8 +103,10 @@ function is_directory() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s are a directory, or an unbroken symlink to a directory - [1] if any s are neither a directory, nor an unbroken symlink to a directory + [0] if all s were a directory, or an unbroken symlink to a directory + [2] if a was not found + [9] if a was a broken symlink + [20] if a existed, but was not a directory nor an unbroken symlink to a directory [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-directory.bash b/commands/is-directory.bash index ce013d181..0a026ed1a 100755 --- a/commands/is-directory.bash +++ b/commands/is-directory.bash @@ -10,9 +10,21 @@ while [[ $# -ne 0 ]]; do if [[ -z $1 ]]; then exit 22 # EINVAL 22 Invalid argument fi + # just -e is faulty, as -e fails on broken symlinks + if [[ -L $1 ]]; then + if [[ ! -e $1 ]]; then + # does exist: is a broken symlink + exit 9 # EBADF 9 Bad file descriptor + fi + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi if [[ ! -d $1 ]]; then - exit 1 + # does exist: not a symlink to a directory, nor a directory + exit 20 # ENOTDIR 20 Not a directory fi + # does exist: is a symlink to a directory, or a directory shift done exit 0 diff --git a/commands/is-empty-directory b/commands/is-empty-directory index b0d666ef0..e4483152b 100755 --- a/commands/is-empty-directory +++ b/commands/is-empty-directory @@ -4,16 +4,27 @@ function is_empty_directory_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" - local dir - dir="$(fs-temp --directory='is-empty-directory' --directory --touch)" - + # prep + local root dir dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-empty-directory-test')" + dir="$(fs-temp --root="$root" --directory='dir')" + fs-rm --quiet --no-confirm -- "$dir" "$root" # remove dir first, as it may not be readable from last run, and fs-rm has workarounds for that + __mkdirp "$dir" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" + symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet + symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-empty-directory -- eval-tester --name='empty args' --status=22 \ -- is-empty-directory -- '' '' - eval-tester --name='missing' --status=1 \ + eval-tester --name='missing' --status=2 \ -- is-empty-directory -- "$DOROTHY/this-does-not-exist" eval-tester --name='empty dir is empty' \ @@ -22,24 +33,63 @@ function is_empty_directory_test() ( eval-tester --name='empty dirs are empty' \ -- is-empty-directory -- "$dir" "$dir" - eval-tester --name='non-empty dir is not empty' --status=1 \ + eval-tester --name='non-empty dir is not empty' --status=66 \ -- is-empty-directory -- "$DOROTHY" - eval-tester --name='file is special failure' --status=20 \ + eval-tester --name='file' --status=20 \ -- is-empty-directory -- "$DOROTHY/README.md" - eval-tester --name='file then non-empty dir is special failure' --status=20 \ + eval-tester --name='file then non-empty dir' --status=20 \ -- is-empty-directory -- "$DOROTHY/README.md" "$DOROTHY" - eval-tester --name='non-empty dir then file standard failure' --status=1 \ + eval-tester --name='non-empty dir then file' --status=66 \ -- is-empty-directory -- "$DOROTHY" "$DOROTHY/README.md" - eval-tester --name='empty then file then non-empty dir is special failure' --status=20 \ + eval-tester --name='empty dir then file then non-empty dir' --status=20 \ -- is-empty-directory -- "$dir" "$DOROTHY/README.md" "$DOROTHY" - eval-tester --name='empty then non-empty dir then file standard failure' --status=1 \ + eval-tester --name='empty dir then non-empty dir then file' --status=66 \ -- is-empty-directory -- "$dir" "$DOROTHY" "$DOROTHY/README.md" + # make dir not readable, so detection fails + fs-own --quiet --no-recurse --permissions='-r' -- "$dir" + eval-tester --name='non-readable empty dir fails' --status=13 \ + -- is-empty-directory -- "$dir" + + # test working symlinks + eval-tester --name='symlink dir' \ + -- is-empty-directory -- "$dir_symlink" + + eval-tester --name='symlink file' --status=20 \ + -- is-empty-directory -- "$file_symlink" + + eval-tester --name='symlink dir then non-empty dir' --status=66 \ + -- is-empty-directory -- "$dir_symlink" "$DOROTHY" + + eval-tester --name='symlink dir then file' --status=20 \ + -- is-empty-directory -- "$dir_symlink" "$DOROTHY/README.md" + + eval-tester --name='symlink dir then missing' --status=2 \ + -- is-empty-directory -- "$dir_symlink" "$DOROTHY/this-does-not-exist" + + # test broken symlinks + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + + eval-tester --name='broken symlink dir' --status=9 \ + -- is-empty-directory -- "$dir_symlink" + + eval-tester --name='broken symlink file' --status=9 \ + -- is-empty-directory -- "$file_symlink" + + eval-tester --name='broken symlink dir then non-empty dir' --status=9 \ + -- is-empty-directory -- "$dir_symlink" "$DOROTHY" + + eval-tester --name='broken symlink dir then file' --status=9 \ + -- is-empty-directory -- "$dir_symlink" "$DOROTHY/README.md" + + eval-tester --name='broken symlink dir then missing' --status=9 \ + -- is-empty-directory -- "$dir_symlink" "$DOROTHY/this-does-not-exist" + echo-style --g1="TEST: $0" return 0 ) @@ -65,9 +115,12 @@ function is_empty_directory() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s are empty - [1] if any s are not empty - [20] if any s is not a directory. + [0] if all s were an empty directory + [2] if a was not found + [9] if a was a broken symlink + [20] if a existed, but was not a directory nor an unbroken symlink to a directory + [13] if a existed and was a directory, but was not readable, so unable to determine if empty or not + [66] if a was a directory, or an unbroken symlink to a directory, but was not empty [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-empty-directory.bash b/commands/is-empty-directory.bash index b54ebca08..a9a9a0cf3 100755 --- a/commands/is-empty-directory.bash +++ b/commands/is-empty-directory.bash @@ -10,12 +10,29 @@ while [[ $# -ne 0 ]]; do if [[ -z $1 ]]; then exit 22 # EINVAL 22 Invalid argument fi + # just -e is faulty, as -e fails on broken symlinks + if [[ -L $1 ]]; then + if [[ ! -e $1 ]]; then + # does exist: is a broken symlink + exit 9 # EBADF 9 Bad file descriptor + fi + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi if [[ ! -d $1 ]]; then + # does exist: not a symlink to a directory, nor a directory exit 20 # ENOTDIR 20 Not a directory fi + if [[ ! -r $1 ]]; then + # does exist: not readable however, so no ability to check contents, as would get: ls: $path: Permission denied + exit 13 # EACCES 13 Permission denied + fi if [[ -n "$(ls -A "$1")" ]]; then - exit 1 + # does exist: is a symlink to a non-empty directory, or a non-empty directory + exit 66 # ENOTEMPTY 66 Directory not empty fi + # does exist: is a symlink to an empty directory, or an empty directory shift done exit 0 diff --git a/commands/is-empty-file b/commands/is-empty-file index 0f44f4faf..b20e49870 100755 --- a/commands/is-empty-file +++ b/commands/is-empty-file @@ -4,31 +4,84 @@ function is_empty_file_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" - local dir file - dir="$(fs-temp --directory='is-empty-file' --directory --touch)" - file="$(fs-temp --directory='is-empty-file' --file --touch)" - + # prep + local root dir file dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-empty-file-test')" + fs-rm --quiet --no-confirm -- "$root" + dir="$(fs-temp --root="$root" --directory --touch)" + file="$(fs-temp --root="$root" --file --touch)" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" + symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet + symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-empty-file -- eval-tester --name='empty args' --status=22 \ -- is-empty-file -- '' '' - eval-tester --name='missing' --status=9 \ + eval-tester --name='missing' --status=2 \ -- is-empty-file -- "$DOROTHY/this-does-not-exist" - eval-tester --name='empty dirs' --status=9 \ + eval-tester --name='empty dirs' --status=79 \ -- is-empty-file -- "$dir" "$dir" eval-tester --name='empty files' \ -- is-empty-file -- "$file" "$file" - eval-tester --name='non-empty dir' --status=9 \ + eval-tester --name='non-empty dir' --status=79 \ -- is-empty-file -- "$DOROTHY" - eval-tester --name='non-empty file' --status=1 \ + eval-tester --name='non-empty file' --status=27 \ -- is-empty-file -- "$DOROTHY/README.md" + # test working symlinks + eval-tester --name='symlink empty dir' --status=79 \ + -- is-empty-file -- "$dir_symlink" + + eval-tester --name='symlink empty file' \ + -- is-empty-file -- "$file_symlink" + + eval-tester --name='symlink empty file then non-empty dir' --status=79 \ + -- is-empty-file -- "$file_symlink" "$DOROTHY" + + eval-tester --name='symlink empty file then non-empty file' --status=27 \ + -- is-empty-file -- "$file_symlink" "$DOROTHY/README.md" + + eval-tester --name='symlink empty file then missing' --status=2 \ + -- is-empty-file -- "$file_symlink" "$DOROTHY/this-does-not-exist" + + # test non-empty symlinks + __print_line >"$file_target" + + eval-tester --name='symlink non-empty file' --status=27 \ + -- is-empty-file -- "$file_symlink" + + eval-tester --name='empty file then symlink non-empty file' --status=27 \ + -- is-empty-file -- "$file" "$file_symlink" + + # test broken symlinks + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + + eval-tester --name='broken symlink dir' --status=9 \ + -- is-empty-file -- "$dir_symlink" + + eval-tester --name='broken symlink file' --status=9 \ + -- is-empty-file -- "$file_symlink" + + eval-tester --name='broken symlink file then non-empty dir' --status=9 \ + -- is-empty-file -- "$file_symlink" "$DOROTHY" + + eval-tester --name='broken symlink file then file' --status=9 \ + -- is-empty-file -- "$file_symlink" "$DOROTHY/README.md" + + eval-tester --name='broken symlink file then missing' --status=9 \ + -- is-empty-file -- "$file_symlink" "$DOROTHY/this-does-not-exist" + echo-style --g1="TEST: $0" return 0 ) @@ -42,6 +95,7 @@ function is_empty_file() ( cat <<-EOF >/dev/stderr ABOUT: Checks if a is an empty file, aka a file without content, aka a file with zero-length content. + Companion to [is-nonempty-file]. USAGE: is-empty-file [...options] [--] ... @@ -54,10 +108,13 @@ function is_empty_file() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s are empty file - [1] if any s are not an empty file. - [9] if any s are not a file. - [22] if empty arguments are provided. + [0] if all s were a zero-length file + [2] if a was not found + [9] if a was a broken symlink + [13] if a existed and was a file, but was not readable, so unable to determine if empty or not + [20] if a existed, but was not a file nor an unbroken symlink to a file + [27] if a was a file, or an unbroken symlink to a file, but was not empty + [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then echo-error "$@" diff --git a/commands/is-empty-file.bash b/commands/is-empty-file.bash index 8605ce148..3c7d6f50f 100755 --- a/commands/is-empty-file.bash +++ b/commands/is-empty-file.bash @@ -10,14 +10,29 @@ while [[ $# -ne 0 ]]; do if [[ -z $1 ]]; then exit 22 # EINVAL 22 Invalid argument fi + # just -e is faulty, as -e fails on broken symlinks + if [[ -L $1 ]]; then + if [[ ! -e $1 ]]; then + # does exist: is a broken symlink + exit 9 # EBADF 9 Bad file descriptor + fi + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi if [[ ! -f $1 ]]; then - # not a file nor symlink to a file - exit 9 # EBADF 9 Bad file descriptor + # does exist: not a symlink to a file, nor a file + exit 79 # EFTYPE 79 Inappropriate file type or format + fi + if [[ ! -r $1 ]]; then + # does exist: not readable however, so no ability to check contents, as would get: ls: $path: Permission denied + exit 13 # EACCES 13 Permission denied fi if [[ -s $1 ]]; then - # not empty - exit 1 + # does exist: is a symlink to a non-empty file, or a non-empty file + exit 27 # EFBIG 27 File too large fi + # does exist: is a symlink to an empty file, or an empty file shift done exit 0 diff --git a/commands/is-executable b/commands/is-executable index 5e5f06cc3..dc10433e5 100755 --- a/commands/is-executable +++ b/commands/is-executable @@ -3,6 +3,8 @@ function is_executable_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-executable -- @@ -15,46 +17,78 @@ function is_executable_test() ( eval-tester --name='directory' \ -- is-executable -- "$DOROTHY" - eval-tester --name='file' \ + eval-tester --name='file' --status=93 \ + -- is-executable -- "$DOROTHY/README.md" + + eval-tester --name='command' \ -- is-executable -- "$DOROTHY/commands/dorothy" - eval-tester --name='file then dir' \ + eval-tester --name='command then dir' \ -- is-executable -- "$DOROTHY/commands/dorothy" "$DOROTHY" - eval-tester --name='dir then file' \ + eval-tester --name='dir then command' \ -- is-executable -- "$DOROTHY" "$DOROTHY/commands/dorothy" - eval-tester --name='dir then file then missing then invalid' --status=2 \ + eval-tester --name='dir then command then missing then invalid' --status=2 \ -- is-executable -- "$DOROTHY" "$DOROTHY/commands/dorothy" "$DOROTHY/this-does-not-exist" '' - eval-tester --name='dir then invalid then missing then file' --status=22 \ + eval-tester --name='dir then command then file then missing then invalid' --status=93 \ + -- is-executable -- "$DOROTHY" "$DOROTHY/commands/dorothy" "$DOROTHY/README.md" "$DOROTHY/this-does-not-exist" '' + + eval-tester --name='dir then invalid then missing then command' --status=22 \ -- is-executable -- "$DOROTHY" '' "$DOROTHY/this-does-not-exist" "$DOROTHY/commands/dorothy" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-executable' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-executable' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-executable' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-executable' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-executable-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - eval-tester --name='symlink dir' \ + # test working symlinks + eval-tester --name='target dir' \ + -- is-executable -- "$dir_target" + eval-tester --name='target file' --status=93 \ + -- is-executable -- "$file_target" + eval-tester --name='symlink dir with executable target' \ -- is-executable -- "$dir_symlink" - - eval-tester --name='symlink file' --status=1 \ + eval-tester --name='symlink file with executable target' --status=93 \ -- is-executable -- "$file_symlink" - fs-own --quiet --permissions='+x' -- "$file_target" - eval-tester --name='symlink file' \ + + # test no longer executable + fs-own --quiet --permissions='-x' -- "$dir_target" "$file_target" + eval-tester --name='non-executable target dir' --status=93 \ + -- is-executable -- "$dir_target" + eval-tester --name='non-executable target file' --status=93 \ + -- is-executable -- "$file_target" + eval-tester --name='non-executable symlink dir with non-executable target' --status=93 \ + -- is-executable -- "$dir_symlink" + eval-tester --name='non-executable symlink file with non-executable target' --status=93 \ -- is-executable -- "$file_symlink" # test broken symlinks fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - - eval-tester --name='broken symlink dir' --status=1 \ + eval-tester --name='broken symlink dir with previously non-executable target (permissions discarded apparently)' --status=93 \ -- is-executable -- "$dir_symlink" + eval-tester --name='broken symlink file with previously non-executable target (permissions discarded apparently)' --status=93 \ + -- is-executable -- "$file_symlink" - eval-tester --name='broken symlink file' --status=1 \ + # recreate targets, make them executable + __mkdirp "$dir_target" + touch "$file_target" + fs-own --quiet --permissions='+x' -- "$dir_target" "$file_target" + eval-tester --name='symlink dir with executable target' \ + -- is-executable -- "$dir_symlink" + eval-tester --name='symlink file with executable target' \ + -- is-executable -- "$file_symlink" + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + eval-tester --name='broken symlink dir with previously executable target (permissions discarded apparently)' --status=93 \ + -- is-executable -- "$dir_symlink" + eval-tester --name='broken symlink file with previously executable target (permissions discarded apparently)' --status=93 \ -- is-executable -- "$file_symlink" echo-style --g1="TEST: $0" @@ -84,8 +118,8 @@ function is_executable() ( RETURNS: [0] if all s are executable - [1] if any s are not executable - [2] if any s do not exist (even as a broken symlink) + [2] if a was not found + [93] if a was not executable [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-executable.bash b/commands/is-executable.bash index f14bd73e1..d159bf970 100755 --- a/commands/is-executable.bash +++ b/commands/is-executable.bash @@ -12,11 +12,14 @@ while [[ $# -ne 0 ]]; do fi # just -e is faulty, as -e fails on broken symlinks if ! [[ -e $1 || -L $1 ]]; then + # doesn't exist: not a symlink, file, nor directory exit 2 # ENOENT 2 No such file or directory fi if [[ ! -x $1 ]]; then - exit 1 + # does exist: is not executable + exit 93 # ENOATTR 93 Attribute not found fi + # does exist: is executable shift done exit 0 diff --git a/commands/is-file b/commands/is-file index 9bd612b53..805feab32 100755 --- a/commands/is-file +++ b/commands/is-file @@ -4,43 +4,44 @@ function is_file_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-file -- eval-tester --name='empty args' --status=22 \ -- is-file -- '' '' - eval-tester --name='missing' --status=1 \ + eval-tester --name='missing' --status=2 \ -- is-file -- "$DOROTHY/this-does-not-exist" - eval-tester --name='directory' --status=1 \ - -- is-file -- "$DOROTHY" + eval-tester --name='directory' --status=79 \ + -- is-file -- "$DOROTHY" "$DOROTHY" eval-tester --name='file' \ - -- is-file -- "$DOROTHY/README.md" + -- is-file -- "$DOROTHY/README.md" "$DOROTHY/README.md" - eval-tester --name='file then dir' --status=1 \ + eval-tester --name='file then dir' --status=79 \ -- is-file -- "$DOROTHY/README.md" "$DOROTHY" - eval-tester --name='dir then file' --status=1 \ - -- is-file -- "$DOROTHY" "$DOROTHY/README.md" - - eval-tester --name='file then dir then invalid' --status=1 \ - -- is-file -- "$DOROTHY/README.md" "$DOROTHY" '' - - eval-tester --name='dir then invalid then dir' --status=22 \ + eval-tester --name='file then invalid then dir' --status=22 \ -- is-file -- "$DOROTHY/README.md" '' "$DOROTHY" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-file' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-file' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-file' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-file' --file='file_symlink' --no-touch)" + eval-tester --name='file then missing then invalid then dir' --status=2 \ + -- is-file -- "$DOROTHY/README.md" "$DOROTHY/this-does-not-exist" '' "$DOROTHY" + + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-file-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - eval-tester --name='symlink dir' --status=1 \ + # test working symlinks + eval-tester --name='symlink dir' --status=79 \ -- is-file -- "$dir_symlink" eval-tester --name='symlink file' \ @@ -49,10 +50,10 @@ function is_file_test() ( # test broken symlinks fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - eval-tester --name='broken symlink dir' --status=1 \ + eval-tester --name='broken symlink dir' --status=9 \ -- is-file -- "$dir_symlink" - eval-tester --name='broken symlink file' --status=1 \ + eval-tester --name='broken symlink file' --status=9 \ -- is-file -- "$file_symlink" echo-style --g1="TEST: $0" @@ -81,8 +82,10 @@ function is_file() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s are a file, or an unbroken symlink to a file - [1] if any s are neither a file, nor an unbroken symlink to a file + [0] if all s were a file + [2] if a was not found + [9] if a was a broken symlink + [20] if a existed, but was not a file nor an unbroken symlink to a file [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-file.bash b/commands/is-file.bash index b7b649386..4268bb734 100755 --- a/commands/is-file.bash +++ b/commands/is-file.bash @@ -10,9 +10,21 @@ while [[ $# -ne 0 ]]; do if [[ -z $1 ]]; then exit 22 # EINVAL 22 Invalid argument fi + # just -e is faulty, as -e fails on broken symlinks + if [[ -L $1 ]]; then + if [[ ! -e $1 ]]; then + # does exist: is a broken symlink + exit 9 # EBADF 9 Bad file descriptor + fi + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi if [[ ! -f $1 ]]; then - exit 1 + # does exist: not a symlink to a file, nor a file + exit 79 # EFTYPE 79 Inappropriate file type or format fi + # does exist: is a symlink to a file, or a file shift done exit 0 diff --git a/commands/is-missing b/commands/is-missing index eecd522c2..d6b3ff0ff 100755 --- a/commands/is-missing +++ b/commands/is-missing @@ -3,55 +3,60 @@ function is_missing_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-missing -- eval-tester --name='empty args' --status=22 \ -- is-missing -- '' '' - eval-tester --name='directory' --status=1 \ + eval-tester --name='directory' --status=17 \ -- is-missing -- "$DOROTHY" eval-tester --name='missing' \ -- is-missing -- "$DOROTHY/this-does-not-exist" - eval-tester --name='file' --status=1 \ + eval-tester --name='file' --status=17 \ -- is-missing -- "$DOROTHY/README.md" - eval-tester --name='file then dir' --status=1 \ + eval-tester --name='file then dir' --status=17 \ -- is-missing -- "$DOROTHY/README.md" "$DOROTHY" - eval-tester --name='missing then file' --status=1 \ + eval-tester --name='missing then file' --status=17 \ -- is-missing -- "$DOROTHY/this-does-not-exist" "$DOROTHY/README.md" - eval-tester --name='missing then file then invalid' --status=1 \ + eval-tester --name='missing then file then invalid' --status=17 \ -- is-missing -- "$DOROTHY/this-does-not-exist" "$DOROTHY/README.md" '' eval-tester --name='missing then invalid then file' --status=22 \ -- is-missing -- "$DOROTHY/this-does-not-exist" '' "$DOROTHY/README.md" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-missing' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-missing' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-missing' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-missing' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-missing-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - eval-tester --name='symlink dir' --status=1 \ + # test working symlinks + eval-tester --name='symlink dir' --status=17 \ -- is-missing -- "$dir_symlink" - eval-tester --name='symlink file' --status=1 \ + eval-tester --name='symlink file' --status=17 \ -- is-missing -- "$file_symlink" # test broken symlinks fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - eval-tester --name='broken symlink dir' --status=1 \ + eval-tester --name='broken symlink dir' --status=17 \ -- is-missing -- "$dir_symlink" - eval-tester --name='broken symlink file' --status=1 \ + eval-tester --name='broken symlink file' --status=17 \ -- is-missing -- "$file_symlink" echo-style --g1="TEST: $0" @@ -81,7 +86,7 @@ function is_missing() ( RETURNS: [0] if all s are missing (not even a broken symlink) - [1] if any s are not missing + [17] if a was not missing [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-missing.bash b/commands/is-missing.bash index 4e175427a..cd0e1ffb2 100755 --- a/commands/is-missing.bash +++ b/commands/is-missing.bash @@ -12,8 +12,10 @@ while [[ $# -ne 0 ]]; do fi # just -e is faulty, as -e fails on broken symlinks if [[ -e $1 || -L $1 ]]; then - exit 1 + # does exist: is a symlink, file, or directory + exit 17 # EEXIST 17 File exists fi + # doesn't exist: not a symlink, file, nor directory shift done exit 0 diff --git a/commands/is-nonempty-file b/commands/is-nonempty-file new file mode 100755 index 000000000..e5bb59e2e --- /dev/null +++ b/commands/is-nonempty-file @@ -0,0 +1,165 @@ +#!/usr/bin/env bash + +function is_nonempty_file_test() ( + source "$DOROTHY/sources/bash.bash" + echo-style --h1="TEST: $0" + + # prep + local root dir file dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-nonempty-file-test')" + fs-rm --quiet --no-confirm -- "$root" + dir="$(fs-temp --root="$root" --directory --touch)" + file="$(fs-temp --root="$root" --file --touch)" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" + symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet + symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + + # test standard paths + eval-tester --name='no args' --status=22 --ignore-stderr \ + -- is-nonempty-file -- + + eval-tester --name='empty args' --status=22 \ + -- is-nonempty-file -- '' '' + + eval-tester --name='missing' --status=2 \ + -- is-nonempty-file -- "$DOROTHY/this-does-not-exist" + + eval-tester --name='empty dirs' --status=79 \ + -- is-nonempty-file -- "$dir" "$dir" + + eval-tester --name='empty files' --status=66 \ + -- is-nonempty-file -- "$file" "$file" + + eval-tester --name='non-empty dir' --status=79 \ + -- is-nonempty-file -- "$DOROTHY" + + eval-tester --name='non-empty file' \ + -- is-nonempty-file -- "$DOROTHY/README.md" + + # test working symlinks + eval-tester --name='symlink empty dir' --status=79 \ + -- is-nonempty-file -- "$dir_symlink" + + eval-tester --name='symlink empty file' --status=66 \ + -- is-nonempty-file -- "$file_symlink" + + # test non-empty symlinks + __print_line >"$file_target" + + eval-tester --name='symlink non-empty file' \ + -- is-nonempty-file -- "$file_symlink" + + eval-tester --name='symlink non-empty file then non-empty file' \ + -- is-nonempty-file -- "$file_symlink" "$DOROTHY/README.md" + + eval-tester --name='symlink non-empty file then empty file' --status=66 \ + -- is-nonempty-file -- "$file_symlink" "$file" + + eval-tester --name='symlink non-empty file then non-empty dir' --status=79 \ + -- is-nonempty-file -- "$file_symlink" "$DOROTHY" + + eval-tester --name='symlink non-empty file then missing' --status=2 \ + -- is-nonempty-file -- "$file_symlink" "$DOROTHY/this-does-not-exist" + + # test broken symlinks + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + + eval-tester --name='broken symlink dir' --status=9 \ + -- is-nonempty-file -- "$dir_symlink" + + eval-tester --name='broken symlink file' --status=9 \ + -- is-nonempty-file -- "$file_symlink" + + eval-tester --name='broken symlink file then non-empty dir' --status=9 \ + -- is-nonempty-file -- "$file_symlink" "$DOROTHY" + + eval-tester --name='broken symlink file then file' --status=9 \ + -- is-nonempty-file -- "$file_symlink" "$DOROTHY/README.md" + + eval-tester --name='broken symlink file then missing' --status=9 \ + -- is-nonempty-file -- "$file_symlink" "$DOROTHY/this-does-not-exist" + + echo-style --g1="TEST: $0" + return 0 +) +function is_nonempty_file() ( + source "$DOROTHY/sources/bash.bash" + + # ===================================== + # Arguments + + function help { + cat <<-EOF >/dev/stderr + ABOUT: + Checks if a is a file that has contents, aka a file that is not zero-length. + Companion to [is-empty-file]. + + USAGE: + is-nonempty-file [...options] [--] ... + + OPTIONS: + --sudo + If specified, use sudo on filesystem interactions. + --user= + --group= + If specified use this user and/or group for filesystem interactions. + + RETURNS: + [0] if all s were a non-zero-length file + [2] if a was not found + [9] if a was a broken symlink + [20] if a existed, but was not a file nor an unbroken symlink to a file + [66] if a was a file, or an unbroken symlink to a file, but was empty + [22] if empty arguments are provided + EOF + if [[ $# -ne 0 ]]; then + echo-error "$@" + fi + return 22 # EINVAL 22 Invalid argument + } + + # process + local item option_inputs=() option_sudo='no' option_user='' option_group='' + while [[ $# -ne 0 ]]; do + item="$1" + shift + case "$item" in + '--help' | '-h') help ;; + '--no-sudo'* | '--sudo'*) + option_sudo="$(get-flag-value --affirmative --fallback="$option_sudo" -- "$item")" + ;; + '--user='*) option_user="${item#*=}" ;; + '--group='*) option_group="${item#*=}" ;; + '--') + option_inputs+=("$@") + shift "$#" + break + ;; + '--'*) help "An unrecognised flag was provided: $item" ;; + *) option_inputs+=("$item") ;; + esac + done + + # check + if [[ ${#option_inputs[@]} -eq 0 ]]; then + help "No s provided." + fi + + # ===================================== + # Action + + sudo-helper --inherit --sudo="$option_sudo" --user="$option_user" --group="$option_group" -- is-nonempty-file.bash -- "${option_inputs[@]}" + return +) + +# fire if invoked standalone +if [[ $0 == "${BASH_SOURCE[0]}" ]]; then + if [[ $* == '--test' ]]; then + is_nonempty_file_test + else + is_nonempty_file "$@" + fi +fi diff --git a/commands/is-nonempty-file.bash b/commands/is-nonempty-file.bash new file mode 100755 index 000000000..47cee5713 --- /dev/null +++ b/commands/is-nonempty-file.bash @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +if [[ $1 == '--' ]]; then + shift +fi +if [[ $# -eq 0 ]]; then + exit 22 # EINVAL 22 Invalid argument +fi +while [[ $# -ne 0 ]]; do + if [[ -z $1 ]]; then + exit 22 # EINVAL 22 Invalid argument + fi + # just -e is faulty, as -e fails on broken symlinks + if [[ -L $1 ]]; then + if [[ ! -e $1 ]]; then + # does exist: is a broken symlink + exit 9 # EBADF 9 Bad file descriptor + fi + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi + if [[ ! -f $1 ]]; then + # does exist: not a symlink to a file, nor a file + exit 79 # EFTYPE 79 Inappropriate file type or format + fi + if [[ ! -s $1 ]]; then + # does exist: is a symlink to an empty file, or an empty file + exit 66 # ENOTEMPTY 66 Directory not empty + fi + # does exist: is a symlink to a non-empty file, or a non-empty file + shift +done +exit 0 diff --git a/commands/is-not-directory b/commands/is-not-directory index eeba6bd46..bb5226788 100755 --- a/commands/is-not-directory +++ b/commands/is-not-directory @@ -1,5 +1,88 @@ #!/usr/bin/env bash +function is_not_directory_test() ( + source "$DOROTHY/sources/bash.bash" + echo-style --h1="TEST: $0" + + # test standard paths + eval-tester --name='no args' --status=22 --ignore-stderr \ + -- is-not-directory -- + + eval-tester --name='empty args' --status=22 \ + -- is-not-directory -- '' '' + + eval-tester --name='missing' --status=2 \ + -- is-not-directory -- "$DOROTHY/this-does-not-exist" + + eval-tester --name='directory' --status=21 \ + -- is-not-directory -- "$DOROTHY" + + eval-tester --name='file' \ + -- is-not-directory -- "$DOROTHY/README.md" + + eval-tester --name='file then dir' --status=21 \ + -- is-not-directory -- "$DOROTHY/README.md" "$DOROTHY" + + eval-tester --name='dir then file' --status=21 \ + -- is-not-directory -- "$DOROTHY" "$DOROTHY/README.md" + + eval-tester --name='dir then file then invalid' --status=21 \ + -- is-not-directory -- "$DOROTHY" "$DOROTHY/README.md" '' + + eval-tester --name='dir then invalid then file' --status=21 \ + -- is-not-directory -- "$DOROTHY" '' "$DOROTHY/README.md" + + eval-tester --name='file then invalid then dir' --status=22 \ + -- is-not-directory -- "$DOROTHY/README.md" '' "$DOROTHY" + + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-not-directory-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" + symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet + symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + + # test working symlinks + eval-tester --name='symlink dir' --status=21 \ + -- is-not-directory -- "$dir_symlink" + + eval-tester --name='symlink file' \ + -- is-not-directory -- "$file_symlink" + + eval-tester --name='symlink dir then dir' --status=21 \ + -- is-not-directory -- "$dir_symlink" "$DOROTHY" + + eval-tester --name='symlink file then file' \ + -- is-not-directory -- "$file_symlink" "$DOROTHY/README.md" + + eval-tester --name='symlink file then missing' --status=2 \ + -- is-not-directory -- "$file_symlink" "$DOROTHY/this-does-not-exist" + + # test broken symlinks + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + + eval-tester --name='broken symlink dir' --status=9 \ + -- is-not-directory -- "$dir_symlink" + + eval-tester --name='broken symlink file' --status=9 \ + -- is-not-directory -- "$file_symlink" + + eval-tester --name='broken symlink dir then dir' --status=9 \ + -- is-not-directory -- "$dir_symlink" "$DOROTHY" + + eval-tester --name='broken symlink dir then file' --status=9 \ + -- is-not-directory -- "$dir_symlink" "$DOROTHY/README.md" + + eval-tester --name='broken symlink dir then missing' --status=9 \ + -- is-not-directory -- "$dir_symlink" "$DOROTHY/this-does-not-exist" + + echo-style --g1="TEST: $0" + return 0 +) function is_not_directory() ( source "$DOROTHY/sources/bash.bash" @@ -10,9 +93,8 @@ function is_not_directory() ( cat <<-EOF >/dev/stderr ABOUT: Check if all s are neither a directory, nor an unbroken symlink to a directory. - Companion to [is-directory]. - If you want if any are not directory, use [! is-directory -- ...]. - The advantage of using this over [! is-directory -- ...] is that will pass for invalid paths and arguments. + Companion to [is-directory], [is-empty-directory]. + Use this over [! is-directory -- ...], as that will pass for invalid arguments. USAGE: is-not-directory [...options] [--] ... @@ -25,8 +107,10 @@ function is_not_directory() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s were neither a directory, nor an unbroken symlink to a directory - [1] if any s were a directory, or an unbroken symlink to a directory + [0] if all s existed, and were neither a directory, nor an unbroken symlink to a directory + [2] if a was not found + [9] if a was a broken symlink + [21] if a was a directory, or an unbroken symlink to a directory [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then @@ -71,5 +155,9 @@ function is_not_directory() ( # fire if invoked standalone if [[ $0 == "${BASH_SOURCE[0]}" ]]; then - is_not_directory "$@" + if [[ $* == '--test' ]]; then + is_not_directory_test + else + is_not_directory "$@" + fi fi diff --git a/commands/is-not-directory.bash b/commands/is-not-directory.bash index d80f63786..ed6b916cb 100755 --- a/commands/is-not-directory.bash +++ b/commands/is-not-directory.bash @@ -10,9 +10,21 @@ while [[ $# -ne 0 ]]; do if [[ -z $1 ]]; then exit 22 # EINVAL 22 Invalid argument fi + # just -e is faulty, as -e fails on broken symlinks + if [[ -L $1 ]]; then + if [[ ! -e $1 ]]; then + # does exist: is a broken symlink + exit 9 # EBADF 9 Bad file descriptor + fi + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi if [[ -d $1 ]]; then - exit 1 + # does exist: is a symlink to a directory, or a directory + exit 21 # EISDIR 21 Is a directory fi + # does exist: is a symlink to a non-directory, or a non-directory shift done exit 0 diff --git a/commands/is-not-symlink b/commands/is-not-symlink index 890a0c4fd..61dbe9817 100755 --- a/commands/is-not-symlink +++ b/commands/is-not-symlink @@ -3,56 +3,66 @@ function is_not_symlink_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-not-symlink -- eval-tester --name='empty args' --status=22 \ -- is-not-symlink -- '' '' - eval-tester --name='missing' --status=1 \ + eval-tester --name='missing' --status=2 \ -- is-not-symlink -- "$DOROTHY/this-does-not-exist" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-not-symlink' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-not-symlink' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-not-symlink' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-not-symlink' --file='file_symlink' --no-touch)" + eval-tester --name='directory' \ + -- is-not-symlink -- "$DOROTHY" + + eval-tester --name='file' \ + -- is-not-symlink -- "$DOROTHY/README.md" + + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-not-symlink-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - # #2todo - eval-tester --name='symlink dir' \ + # test working symlinks + eval-tester --name='symlink dir' --status=79 \ -- is-not-symlink -- "$dir_symlink" - eval-tester --name='symlink file' \ + eval-tester --name='symlink file' --status=79 \ -- is-not-symlink -- "$file_symlink" - eval-tester --name='symlink dir then dir' --status=1 \ + eval-tester --name='symlink dir then dir' --status=79 \ -- is-not-symlink -- "$dir_symlink" "$DOROTHY" - eval-tester --name='symlink file then file' --status=1 \ + eval-tester --name='symlink file then file' --status=79 \ -- is-not-symlink -- "$file_symlink" "$DOROTHY/README.md" - eval-tester --name='symlink file then missing' --status=1 \ + eval-tester --name='symlink file then missing' --status=79 \ -- is-not-symlink -- "$file_symlink" "$DOROTHY/this-does-not-exist" # test broken symlinks fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - eval-tester --name='broken symlink dir' \ + eval-tester --name='broken symlink dir' --status=79 \ -- is-not-symlink -- "$dir_symlink" - eval-tester --name='broken symlink file' \ + eval-tester --name='broken symlink file' --status=79 \ -- is-not-symlink -- "$file_symlink" - eval-tester --name='broken symlink dir then dir' --status=1 \ + eval-tester --name='broken symlink dir then dir' --status=79 \ -- is-not-symlink -- "$dir_symlink" "$DOROTHY" - eval-tester --name='broken symlink file then file' --status=1 \ + eval-tester --name='broken symlink file then file' --status=79 \ -- is-not-symlink -- "$file_symlink" "$DOROTHY/README.md" - eval-tester --name='broken symlink file then missing' --status=1 \ + eval-tester --name='broken symlink file then missing' --status=79 \ -- is-not-symlink -- "$file_symlink" "$DOROTHY/this-does-not-exist" echo-style --g1="TEST: $0" @@ -68,6 +78,7 @@ function is_not_symlink() ( cat <<-EOF >/dev/stderr ABOUT: Check if all s are not a symlink, broken or otherwise. + Companion to [is-broken-symlink], [is-symlink]. USAGE: is-not-symlink [...options] [--] ... @@ -80,8 +91,9 @@ function is_not_symlink() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s were not a symlink - [1] if any s were a symlink + [0] if all s existed and were not a symlink + [2] if a was not found + [79] if a was a symlink (broken or otherwise) [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-not-symlink.bash b/commands/is-not-symlink.bash index 8ed999483..ccbebc019 100755 --- a/commands/is-not-symlink.bash +++ b/commands/is-not-symlink.bash @@ -11,8 +11,13 @@ while [[ $# -ne 0 ]]; do exit 22 # EINVAL 22 Invalid argument fi if [[ -L $1 ]]; then - exit 1 + # does exist: is a symlink + exit 79 # EFTYPE 79 Inappropriate file type or format + elif [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory fi + # does exist: not a symlink shift done exit 0 diff --git a/commands/is-present b/commands/is-present index afc536199..5e3c06c8d 100755 --- a/commands/is-present +++ b/commands/is-present @@ -3,13 +3,15 @@ function is_present_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-present -- eval-tester --name='empty args' --status=22 \ -- is-present -- '' '' - eval-tester --name='missing' --status=1 \ + eval-tester --name='missing' --status=2 \ -- is-present -- "$DOROTHY/this-does-not-exist" eval-tester --name='directory' \ @@ -30,15 +32,18 @@ function is_present_test() ( eval-tester --name='dir then invalid then file' --status=22 \ -- is-present -- "$DOROTHY" '' "$DOROTHY/README.md" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-present' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-present' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-present' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-present' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-present-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + # test working symlinks eval-tester --name='symlink dir' \ -- is-present -- "$dir_symlink" @@ -82,7 +87,7 @@ function is_present() ( RETURNS: [0] if all s were present (even if it is a broken symlink). - [1] if any s were not present + [2] if a was not present [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-present.bash b/commands/is-present.bash index bab63034f..97257d372 100755 --- a/commands/is-present.bash +++ b/commands/is-present.bash @@ -12,7 +12,7 @@ while [[ $# -ne 0 ]]; do fi # just -e is faulty, as -e fails on broken symlinks if ! [[ -e $1 || -L $1 ]]; then - exit 1 + exit 2 # ENOENT 2 No such file or directory fi shift done diff --git a/commands/is-readable b/commands/is-readable index f08d7d1c6..ece8e587e 100755 --- a/commands/is-readable +++ b/commands/is-readable @@ -3,6 +3,8 @@ function is_readable_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-readable -- @@ -30,33 +32,59 @@ function is_readable_test() ( eval-tester --name='dir then invalid then missing then file' --status=22 \ -- is-readable -- "$DOROTHY" '' "$DOROTHY/this-does-not-exist" "$DOROTHY/README.md" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-readable' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-readable' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-readable' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-readable' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-readable-test')" + dir_target="$(fs-temp --root="$root" --directory='dir_target')" + file_target="$(fs-temp --root="$root" --file='file_target')" + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" "$root" # remove dir_target, file_target first as they may not be readable from last run, and fs-rm has workarounds for that + __mkdirp "$dir_target" + touch "$file_target" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - eval-tester --name='symlink dir' \ + # test working symlinks + eval-tester --name='target dir' \ + -- is-readable -- "$dir_target" + eval-tester --name='target file' \ + -- is-readable -- "$file_target" + eval-tester --name='symlink dir with readable target' \ -- is-readable -- "$dir_symlink" - - eval-tester --name='symlink file' \ + eval-tester --name='symlink file with readable target' \ -- is-readable -- "$file_symlink" # test no longer readable - fs-own --quiet --permissions='-r' -- "$file_target" - eval-tester --name='no longer readable' --status=1 \ + fs-own --quiet --permissions='-r' --no-recursion -- "$dir_target" "$file_target" + eval-tester --name='non-readable target dir' --status=93 \ + -- is-readable -- "$dir_target" + eval-tester --name='non-readable target file' --status=93 \ -- is-readable -- "$file_target" + eval-tester --name='non-readable symlink dir with non-readable target' --status=93 \ + -- is-readable -- "$dir_symlink" + eval-tester --name='non-readable symlink file with non-readable target' --status=93 \ + -- is-readable -- "$file_symlink" # test broken symlinks - fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - - eval-tester --name='broken symlink dir' --status=1 \ + fs-rm --quiet --no-confirm --readable --delete -- "$dir_target" "$file_target" + eval-tester --name='broken symlink dir with previously non-readable target (permissions discarded apparently)' --status=93 \ -- is-readable -- "$dir_symlink" + eval-tester --name='broken symlink file with previously non-readable target (permissions discarded apparently)' --status=93 \ + -- is-readable -- "$file_symlink" - eval-tester --name='broken symlink file' --status=1 \ + # recreate targets, make them readable + __mkdirp "$dir_target" + touch "$file_target" + fs-own --quiet --permissions='+r' -- "$dir_target" "$file_target" + eval-tester --name='symlink dir with readable target' \ + -- is-readable -- "$dir_symlink" + eval-tester --name='symlink file with readable target' \ + -- is-readable -- "$file_symlink" + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + eval-tester --name='broken symlink dir with previously readable target (permissions discarded apparently)' --status=93 \ + -- is-readable -- "$dir_symlink" + eval-tester --name='broken symlink file with previously readable target (permissions discarded apparently)' --status=93 \ -- is-readable -- "$file_symlink" echo-style --g1="TEST: $0" @@ -85,8 +113,8 @@ function is_readable() ( RETURNS: [0] if all s are readable - [1] if any s are not readable - [2] if any s do not exist (even as a broken symlink) + [2] if a was not found + [93] if a was not readable [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-readable.bash b/commands/is-readable.bash index 83f78efec..b91b427de 100755 --- a/commands/is-readable.bash +++ b/commands/is-readable.bash @@ -12,11 +12,14 @@ while [[ $# -ne 0 ]]; do fi # just -e is faulty, as -e fails on broken symlinks if ! [[ -e $1 || -L $1 ]]; then + # doesn't exist: not a symlink, file, nor directory exit 2 # ENOENT 2 No such file or directory fi if [[ ! -r $1 ]]; then - exit 1 + # does exist: is not readable + exit 93 # ENOATTR 93 Attribute not found fi + # does exist: is readable shift done exit 0 diff --git a/commands/is-symlink b/commands/is-symlink index 51ee9b9a1..b84171296 100755 --- a/commands/is-symlink +++ b/commands/is-symlink @@ -3,37 +3,42 @@ function is_symlink_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-symlink -- eval-tester --name='empty args' --status=22 \ -- is-symlink -- '' '' - eval-tester --name='missing' --status=1 \ + eval-tester --name='missing' --status=2 \ -- is-symlink -- "$DOROTHY/this-does-not-exist" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-symlink' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-symlink' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-symlink' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-symlink' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-symlink-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet + # test working symlinks eval-tester --name='symlink dir' \ -- is-symlink -- "$dir_symlink" eval-tester --name='symlink file' \ -- is-symlink -- "$file_symlink" - eval-tester --name='symlink dir then dir' --status=1 \ + eval-tester --name='symlink dir then dir' --status=79 \ -- is-symlink -- "$dir_symlink" "$DOROTHY" - eval-tester --name='symlink file then file' --status=1 \ + eval-tester --name='symlink file then file' --status=79 \ -- is-symlink -- "$file_symlink" "$DOROTHY/README.md" - eval-tester --name='symlink file then missing' --status=1 \ + eval-tester --name='symlink file then missing' --status=2 \ -- is-symlink -- "$file_symlink" "$DOROTHY/this-does-not-exist" # test broken symlinks @@ -45,13 +50,13 @@ function is_symlink_test() ( eval-tester --name='broken symlink file' \ -- is-symlink -- "$file_symlink" - eval-tester --name='broken symlink dir then dir' --status=1 \ + eval-tester --name='broken symlink dir then dir' --status=79 \ -- is-symlink -- "$dir_symlink" "$DOROTHY" - eval-tester --name='broken symlink file then file' --status=1 \ + eval-tester --name='broken symlink file then file' --status=79 \ -- is-symlink -- "$file_symlink" "$DOROTHY/README.md" - eval-tester --name='broken symlink file then missing' --status=1 \ + eval-tester --name='broken symlink file then missing' --status=2 \ -- is-symlink -- "$file_symlink" "$DOROTHY/this-does-not-exist" echo-style --g1="TEST: $0" @@ -67,6 +72,7 @@ function is_symlink() ( cat <<-EOF >/dev/stderr ABOUT: Check if all s are a symlink, broken or otherwise. + Companion to [is-broken-symlink], [is-not-symlink]. USAGE: is-symlink [...options] [--] ... @@ -79,8 +85,9 @@ function is_symlink() ( If specified use this user and/or group for filesystem interactions. RETURNS: - [0] if all s were a symlink - [1] if any s were not a symlink + [0] if all s were a symlink (broken or otherwise) + [2] if a was not found + [79] if a was not a symlink (broken or otherwise) [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-symlink.bash b/commands/is-symlink.bash index 5468e6d31..ddad6a60b 100755 --- a/commands/is-symlink.bash +++ b/commands/is-symlink.bash @@ -11,8 +11,15 @@ while [[ $# -ne 0 ]]; do exit 22 # EINVAL 22 Invalid argument fi if [[ ! -L $1 ]]; then - exit 1 + # not a symlink + if [[ ! -e $1 ]]; then + # doesn't exist: not a symlink, file, nor directory + exit 2 # ENOENT 2 No such file or directory + fi + # does exist: but not a symlink + exit 79 # EFTYPE 79 Inappropriate file type or format fi + # does exist: symlink (broken or otherwise) shift done exit 0 diff --git a/commands/is-writable b/commands/is-writable index b368bc516..e03883d46 100755 --- a/commands/is-writable +++ b/commands/is-writable @@ -3,6 +3,8 @@ function is_writable_test() ( source "$DOROTHY/sources/bash.bash" echo-style --h1="TEST: $0" + + # test standard paths eval-tester --name='no args' --status=22 --ignore-stderr \ -- is-writable -- @@ -30,33 +32,57 @@ function is_writable_test() ( eval-tester --name='dir then invalid then missing then file' --status=22 \ -- is-writable -- "$DOROTHY" '' "$DOROTHY/this-does-not-exist" "$DOROTHY/README.md" - # test working symlinks - local dir_target dir_symlink file_target file_symlink - dir_target="$(fs-temp --directory='is-writable' --directory='dir_target' --touch)" - file_target="$(fs-temp --directory='is-writable' --file='file_target' --touch)" - dir_symlink="$(fs-temp --directory='is-writable' --directory='dir_symlink' --no-touch)" - file_symlink="$(fs-temp --directory='is-writable' --file='file_symlink' --no-touch)" + # prep + local root dir_target dir_symlink file_target file_symlink + root="$(fs-temp --directory='is-writable-test')" + fs-rm --quiet --no-confirm -- "$root" + dir_target="$(fs-temp --root="$root" --directory='dir_target' --touch)" + file_target="$(fs-temp --root="$root" --file='file_target' --touch)" + dir_symlink="$(fs-temp --root="$root" --directory='dir_symlink' --no-touch)" + file_symlink="$(fs-temp --root="$root" --file='file_symlink' --no-touch)" symlink-helper --existing="$dir_target" --symlink="$dir_symlink" --quiet symlink-helper --existing="$file_target" --symlink="$file_symlink" --quiet - eval-tester --name='symlink dir' \ + # test working symlinks + eval-tester --name='target dir' \ + -- is-writable -- "$dir_target" + eval-tester --name='target file' \ + -- is-writable -- "$file_target" + eval-tester --name='symlink dir with writable target' \ -- is-writable -- "$dir_symlink" - - eval-tester --name='symlink file' \ + eval-tester --name='symlink file with writable target' \ -- is-writable -- "$file_symlink" # test no longer writable - fs-own --quiet --permissions='-w' -- "$file_target" - eval-tester --name='no longer writable' --status=1 \ + fs-own --quiet --permissions='-w' -- "$dir_target" "$file_target" + eval-tester --name='non-writable target dir' --status=93 \ + -- is-writable -- "$dir_target" + eval-tester --name='non-writable target file' --status=93 \ -- is-writable -- "$file_target" + eval-tester --name='non-writable symlink dir with non-writable target' --status=93 \ + -- is-writable -- "$dir_symlink" + eval-tester --name='non-writable symlink file with non-writable target' --status=93 \ + -- is-writable -- "$file_symlink" # test broken symlinks fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" - - eval-tester --name='broken symlink dir' --status=1 \ + eval-tester --name='broken symlink dir with previously non-writable target (permissions discarded apparently)' --status=93 \ -- is-writable -- "$dir_symlink" + eval-tester --name='broken symlink file with previously non-writable target (permissions discarded apparently)' --status=93 \ + -- is-writable -- "$file_symlink" - eval-tester --name='broken symlink file' --status=1 \ + # recreate targets, make them writable + __mkdirp "$dir_target" + touch "$file_target" + fs-own --quiet --permissions='+w' -- "$dir_target" "$file_target" + eval-tester --name='symlink dir with writable target' \ + -- is-writable -- "$dir_symlink" + eval-tester --name='symlink file with writable target' \ + -- is-writable -- "$file_symlink" + fs-rm --quiet --no-confirm -- "$dir_target" "$file_target" + eval-tester --name='broken symlink dir with previously writable target (permissions discarded apparently)' --status=93 \ + -- is-writable -- "$dir_symlink" + eval-tester --name='broken symlink file with previously writable target (permissions discarded apparently)' --status=93 \ -- is-writable -- "$file_symlink" echo-style --g1="TEST: $0" @@ -85,8 +111,8 @@ function is_writable() ( RETURNS: [0] if all s are writable - [1] if any s are not writable - [2] if any s do not exist (even as a broken symlink) + [2] if a was not found + [93] if a was not writable [22] if empty arguments are provided EOF if [[ $# -ne 0 ]]; then diff --git a/commands/is-writable.bash b/commands/is-writable.bash index 0d8a14fff..d1fe97f39 100755 --- a/commands/is-writable.bash +++ b/commands/is-writable.bash @@ -12,11 +12,14 @@ while [[ $# -ne 0 ]]; do fi # just -e is faulty, as -e fails on broken symlinks if ! [[ -e $1 || -L $1 ]]; then + # doesn't exist: not a symlink, file, nor directory exit 2 # ENOENT 2 No such file or directory fi if [[ ! -w $1 ]]; then - exit 1 + # does exist: is not writable + exit 93 # ENOATTR 93 Attribute not found fi + # does exist: is writable shift done exit 0 diff --git a/commands/mount-helper b/commands/mount-helper index d3ed68bd8..d773280ea 100755 --- a/commands/mount-helper +++ b/commands/mount-helper @@ -1116,8 +1116,7 @@ function mount_helper() ( fi if [[ -n $option_target ]]; then rm_root_cmd=( - fs-rm - --sudo + fs-rm --sudo --no-confirm-if-empty -- "$option_target" ) @@ -1125,8 +1124,7 @@ function mount_helper() ( mounting_cmd+=("${root_cmd[@]}") mkdir_cmd+=("${root_cmd[@]}") rm_cmd+=( - fs-rm - --sudo + fs-rm --sudo --no-confirm-if-empty -- "$option_target" ) @@ -1134,7 +1132,7 @@ function mount_helper() ( mounting_cmd+=("${owner_cmd[@]}") mkdir_cmd+=("${owner_cmd[@]}") - rm_cmd+=('fs-rm') + rm_cmd+=(fs-rm --no-confirm-if-empty) if [[ -n $option_user ]]; then rm_cmd+=("--user=$option_user") fi @@ -1147,7 +1145,7 @@ function mount_helper() ( ) else rm_cmd+=( - fs-rm + fs-rm --no-confirm-if-empty -- "$option_target" ) diff --git a/commands/setup-util b/commands/setup-util index cf130ca87..8311d2c6e 100755 --- a/commands/setup-util +++ b/commands/setup-util @@ -3543,7 +3543,7 @@ function setup_util() ( ) if [[ ${#filtered_paths[@]} -eq 0 ]]; then echo-error "No files matched the build filter [$build_glob] within [$download_directory]." - ls -la "$download_directory" >/dev/stderr + fs-structure -- "$download_directory" >/dev/stderr return 1 elif [[ ${#filtered_paths[@]} -eq 1 ]]; then # move the single file @@ -3552,7 +3552,7 @@ function setup_util() ( done else echo-error "Multiple files matched the build filter [$build_glob] within [$download_directory]." - ls -la "$download_directory" >/dev/stderr + fs-structure -- "$download_directory" >/dev/stderr return 1 fi fi diff --git a/commands/setup-util-firefox b/commands/setup-util-firefox new file mode 100755 index 000000000..e7ff03408 --- /dev/null +++ b/commands/setup-util-firefox @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# https://repology.org/project/firefox/versions + +function setup_util_firefox() ( + source "$DOROTHY/sources/bash.bash" + + local options=( + --name='Firefox' + --app='Firefox' + "$@" + APK='firefox' # ALPINE + APT='firefox' # UBUNTU + AUR='firefox' # ARCH + CASK='firefox' # MACOS + CHOCOLATEY='firefox' # WINDOWS + EMERGE='www-client/firefox' # GENTOO + NIX='nixpkgs.firefox' # NIX + RPM='firefox' # FEDORA + URPMI='firefox' # MAGEIA + XBPS='firefox' # VOID + ZYPPER='firefox' # SUSE + ) + setup-util "${options[@]}" +) + +# fire if invoked standalone +if [[ $0 == "${BASH_SOURCE[0]}" ]]; then + setup_util_firefox "$@" +fi diff --git a/commands/ssh-helper b/commands/ssh-helper index 593f16de7..8b077004d 100755 --- a/commands/ssh-helper +++ b/commands/ssh-helper @@ -289,7 +289,7 @@ function ssh_helper() ( # check if [[ -z $name ]]; then - help "Missing in [ssh-helper new ]." + help 'Missing in [ssh-helper new ].' fi # prepare @@ -299,8 +299,7 @@ function ssh_helper() ( # remove the old keys local paths=("$path"*) if [[ ${#paths[@]} -ne 0 ]]; then - echo-style --notice='Keys already exist with that filename...' - fs-rm --confirm -- "${paths[@]}" + fs-rm --reason='Cannot create a new key as an old key already exists with that filename.' --confirm -- "${paths[@]}" fi # make the new key