Skip to content

Commit

Permalink
improvements to clearing, and resolve setup-util(-bash) edge case
Browse files Browse the repository at this point in the history
- brew-installed: support --quiet argument, return exit status 46 if brew is not installed
- echo-quote: support --double and --single arguments
- echo-revolving-door: remove some outdated implementation comments
- fs-rm: note when trash is not available
- setup-util:
    - output to /dev/tty if supported and appropriate
    - if uninstalling/upgrading the currently executing bash, output a warning
    - redo the clearing functionality to implement modern conventions and support a wide range of edge cases, such as when installer outputs more than LINES, or when clear is not available, or when the installer needs interaction
    - don't remove non-xdg-bins when upgrading as that causes issues with homebrew if the homebrew-intalled bin was removed but homebrew still thinks it is installed
- setup-util-bash: detections and workarounds if trying to uninstall/upgrade the currently executing bash
- add an `echo-revolving-screen` command that unfortunately doesn't work properly and I don't know why
- styles.bash: change clear screen to use `\e[J` instead of `\e[2J` for reasoning documented inside the file
  • Loading branch information
balupton committed Oct 17, 2024
1 parent c3d2a34 commit 1678be2
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 109 deletions.
102 changes: 102 additions & 0 deletions commands.beta/echo-revolving-screen
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env bash

function echo_revolving_screen() (
source "$DOROTHY/sources/bash.bash"

# =====================================
# Arguments

function help {
cat <<-EOF >/dev/stderr
ABOUT:
Continously clear the output of a command, showing only the latest output, then clearing it upon completion.
USAGE:
(echo-lines -- 1 2; sleep 2; echo-lines -- 3 4; sleep 2) | echo-revolving-door [...options]
# outputs 2, then waits, then outputs 4, then waits, then clears
OPTIONS:
--columns=<columns>
The number of columns to display. If not provided, the terminal's columns will be used. If the terminal's columns cannot be determined, or if <= 0, then the full line will be displayed.
EOF
if test "$#" -ne 0; then
echo-error "$@"
fi
return 22 # EINVAL 22 Invalid argument
}

# process
local item option_lines='' option_columns=''
while test "$#" -ne 0; do
item="$1"
shift
case "$item" in
'--help' | '-h') help ;;
'--lines='*) option_lines="${item#*=}" ;;
'--columns='*) option_columns="${item#*=}" ;;
'--'*) help "An unrecognised flag was provided: $item" ;;
*) help "An unrecognised argument was provided: $item" ;;
esac
done

# determine columns
if test -z "$option_lines" -o -z "$option_columns"; then
local terminal_size=()
mapfile -t terminal_size < <(get-terminal-lines-and-columns || :)
if test "${#terminal_size[@]}" -eq 2; then
if test -z "$option_lines"; then
option_lines="${terminal_size[0]}"
fi
if test -z "$option_columns"; then
option_columns="${terminal_size[1]}"
fi
fi
fi

# =====================================
# Action

if test -z "$option_lines" -o -z "$option_columns" || test "$option_lines" -le 0 -o "$option_columns" -le 0; then
cat
else
local terminal_device_file clear_screen
terminal_device_file="$(get-terminal-device-file)"
clear_screen=$'\e[H\e[J'

local input total_lines=0 status wrapped lines=0
while IFS='' read -r input || test -n "$input"; do
eval_capture --statusvar=status --outputvar=wrapped \
-- gfold --width="$option_columns" <<<"$input"
#-- echo-wrap --width="$option_columns" -- "$input"
if test "$status" -ne 0; then
echo-error "$wrapped"
return "$status"
fi
# count lines
lines="$(echo-count-lines -- "$wrapped")"
total_lines="$((total_lines + lines))"
if test "$total_lines" -ge "$option_lines"; then
if test "$lines" -ge "$option_lines"; then
# this line is very long and wraps across all lines of our screen, this is an edge case
# use a coreutil to trim the excess lines
__print_lines "${clear_screen}${wrapped}" | head -n "$option_lines"
else
# clear the screen
__print_lines "${clear_screen}${wrapped}" >"$terminal_device_file"
fi
total_lines="$lines"
else
# print the wrapped output
__print_lines "$wrapped" >"$terminal_device_file"
fi
done
# we are now done, so clear wrapped
# echo-clear-lines --count="$lines" >"$terminal_device_file"
__print_string "${clear_screen}" >"$terminal_device_file"
fi
)

# fire if invoked standalone
if test "$0" = "${BASH_SOURCE[0]}"; then
echo_revolving_screen "$@"
fi
66 changes: 44 additions & 22 deletions commands/brew-installed
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
function brew_installed() (
source "$DOROTHY/sources/bash.bash"

# compatibility
if ! is-brew; then
return 46 # EPFNOSUPPORT 46 Protocol family not supported
fi

# =====================================
# Arguments

function help {
cat <<-EOF >/dev/stderr
ABOUT:
Outputs the names of the installed packages.
Outputs the names of the homebrew installed packages.
USAGE:
brew-installed [...options] -- [...<package>]
Expand All @@ -24,18 +29,21 @@ function brew_installed() (
--cask
Output only packages that are casks.
--quiet
If provided, do not output anything, just return the exist status.
...<package>
If provided, only get details for these packages. Fail if one of them has not yet been installed.
QUIRKS:
If packages are provided, failure exit code will be returned if any are missing.
If packages are provided, failure exist status will be returned if any are missing.
To check if any are present, use:
$(echo-style --code="test -n \"\$(brew-installed -- bash something-missing &>/dev/null || :)\"")
$(echo-style --code="test -n \"\$(brew-installed -- bash something-missing || :)\"")
If you just want the exit code, use:
If you just want the exist status, use:
$(echo-style --code="brew-installed -- bash something-missing &>/dev/null")
$(echo-style --code="brew-installed --quiet -- bash something-missing")
EOF
if test "$#" -ne 0; then
echo-error "$@"
Expand All @@ -44,12 +52,12 @@ function brew_installed() (
}

# process
local item option_requested='no' packages=() brew_type brew_list_cmd brew_info_cmd deno_script_args
brew_type='' # empty, formula, cask
local item option_quiet='' option_requested='no' option_packages=() option_type brew_list_cmd brew_info_cmd deno_script_args
option_type='' # empty, formula, cask
brew_list_cmd=(
'brew'
'list'
'--versions' # --versions is necessary to limit to installed packages, as --full-name/-1 doesn't
'--versions' # --versions is necessary to limit to installed option_packages, as --full-name/-1 doesn't
)
brew_info_cmd=(
'brew'
Expand All @@ -63,13 +71,19 @@ function brew_installed() (
shift
case "$item" in
'--help' | '-h') help ;;
'--formula' | '--formulae') brew_type='formula' ;;
'--cask' | '--casks') brew_type='cask' ;;
'--no-verbose'* | '--verbose'*)
option_quiet="$(get-flag-value --non-affirmative --fallback="$option_quiet" -- "$item")"
;;
'--no-quiet'* | '--quiet'*)
option_quiet="$(get-flag-value --affirmative --fallback="$option_quiet" -- "$item")"
;;
'--formula' | '--formulae') option_type='formula' ;;
'--cask' | '--casks') option_type='cask' ;;
'--no-requested'* | '--requested'*)
option_requested="$(get-flag-value --affirmative --fallback="$option_requested")"
;;
'--')
packages+=("$@")
option_packages+=("$@")
shift $#
break
;;
Expand All @@ -79,18 +93,18 @@ function brew_installed() (
done

# add the filter
if test -n "$brew_type"; then
brew_list_cmd+=("--$brew_type")
brew_info_cmd+=("--$brew_type")
deno_script_args+=("--$brew_type")
if test -n "$option_type"; then
brew_list_cmd+=("--$option_type")
brew_info_cmd+=("--$option_type")
deno_script_args+=("--$option_type")
fi

# add the packages
if test "${#packages[@]}" -ne 0; then
brew_list_cmd+=("${packages[@]}")
if test "${#option_packages[@]}" -ne 0; then
brew_list_cmd+=("${option_packages[@]}")
brew_info_cmd+=(
'--json=v2'
"${packages[@]}"
"${option_packages[@]}"
)
else
brew_info_cmd+=(
Expand All @@ -104,7 +118,11 @@ function brew_installed() (

function do_brew_simple {
# handles only --cask and --formula, but not --requested
"${brew_list_cmd[@]}" | cut -d' ' -f1 | sort | uniq
if test "$option_quiet" = 'yes'; then
"${brew_list_cmd[@]}" &>/dev/null
else
"${brew_list_cmd[@]}" | cut -d' ' -f1 | sort | uniq
fi
}

function do_brew_advanced {
Expand All @@ -116,15 +134,19 @@ function brew_installed() (
# run
local deno_script
deno_script="$(type -P 'brew-installed.ts')"
"${brew_info_cmd[@]}" | "$deno_script" "${deno_script_args[@]}"
if test "$option_quiet" = 'yes'; then
"${brew_info_cmd[@]}" | "$deno_script" "${deno_script_args[@]}" &>/dev/null
else
"${brew_info_cmd[@]}" | "$deno_script" "${deno_script_args[@]}"
fi
}

# get names of requested packages
if test "$option_requested" = 'no'; then
do_brew_simple
elif test "${#packages[@]}" -eq 0; then
elif test "${#option_packages[@]}" -eq 0; then
do_brew_advanced
elif test "$(do_brew_advanced | echo-count-lines --no-inline --stdin)" -ne "${#packages[@]}"; then
elif test "$(do_brew_advanced | echo-count-lines --no-inline --stdin)" -ne "${#option_packages[@]}"; then
return 1
fi
)
Expand Down
35 changes: 30 additions & 5 deletions commands/echo-quote
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ function echo_quote() (
echo-lines ...<input> | echo-quote [...options]
OPTIONS:
--double
If specified, output the <input> as a double quoted string.
--single
If specified, output the <input> as a single quoted string.
$(stdinargs_options_help --)
EOF
if test "$#" -ne 0; then
Expand All @@ -24,22 +30,41 @@ function echo_quote() (
return 22 # EINVAL 22 Invalid argument
}

# process our own arguments, delegate everything else to stdinargs
local item option_quote_desired=''
while test "$#" -ne 0; do
item="$1"
shift
case "$item" in
'--help' | '-h') help ;;
'--double') option_quote_desired='double' ;;
'--single') option_quote_desired='single' ;;
# forward to stdinargs, however support mixing and matching of our options, with stdinarg options
'--')
option_args+=("$item" "$@")
shift $#
break
;;
*) option_args+=("$item") ;;
esac
done

# =====================================
# Action

# this is not the same as ${var@Q}, which handles single quotes differently
function on_input {
local item="$1"
if [[ $item != *"'"* ]]; then
if [[ $item != *"'"* ]] && test "$option_quote_desired" != 'double'; then
# does not contain single quotes
__print_lines "'$item'"
elif [[ $item != *'"'* ]]; then
elif [[ $item != *'"'* ]] && test "$option_quote_desired" != 'single'; then
# does not contain double quotes
__print_lines "\"$item\""
elif [[ $item != *"\\'"* ]]; then
elif [[ $item != *"\\'"* ]] && test "$option_quote_desired" != 'double'; then
# does not contain escaped single quotes
__print_lines "'${item//\'/\\\'}'"
elif [[ $item != *"\\\""* ]]; then
elif [[ $item != *"\\\""* ]] && test "$option_quote_desired" != 'single'; then
# does not contain escaped double quotes
__print_lines "\"${item//\"/\\\"}\""
else
Expand All @@ -51,7 +76,7 @@ function echo_quote() (
fi
}

stdinargs "$@"
stdinargs "${option_args[@]}"
)

# fire if invoked standalone
Expand Down
30 changes: 0 additions & 30 deletions commands/echo-revolving-door
Original file line number Diff line number Diff line change
Expand Up @@ -69,33 +69,3 @@ function echo_revolving_door() (
if test "$0" = "${BASH_SOURCE[0]}"; then
echo_revolving_door "$@"
fi

# if is-mac; then
# bin_gfold="$(type -P 'gfold' 2>/dev/null || :)"
# else
# # we could support these on macos, however fmt does not support -t on macos (it is something different, so we'd have to manually do that)
# bin_gfold="$(type -P 'fold' 2>/dev/null || :)"
# fi
# if test -z "$bin_gfold"; then
# use_cat='yes'
# fi

# local input last_input=''
# function clear_last_input {
# if test -z "$last_input"; then
# : # do nothing
# elif [[ $last_input =~ $'\e\[[0-9]*[AKGFJ]' ]]; then
# printf '\e[G\e[2K' # set cursor to start of line, and clear from there
# else
# local count
# count="$(__print_string "$last_input" | echo-count-lines --stdin)"
# printf "\e[${count}A\e[G\e[J" # move cursor up count, set cursor to start of line, and delete everything from there
# fi
# }
# while IFS='' read -r input || test -n "$input"; do
# clear_last_input
# input="$(__print_string "$input" | "$bin_gfold" -w "$size_columns")"
# __print_lines "$input"
# last_input="$input"
# done
# clear_last_input
1 change: 1 addition & 0 deletions commands/fs-rm
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ function fs_rm() (
if test "$option_trash" = 'yes'; then
setup-util-trash --quiet --optional --no-fallback
if __command_missing -- trash; then
echo-style --dim='Moving to trash is not available, falling back to immediate deletion for: ' --code="${option_paths[*]}" >/dev/stderr
option_trash='no'
fi
fi
Expand Down
Loading

0 comments on commit 1678be2

Please sign in to comment.