mirror of
https://github.com/tw93/Mole.git
synced 2026-02-11 12:59:16 +00:00
refactor: centralize subcommand help handlers
This commit is contained in:
100
bin/clean.sh
100
bin/clean.sh
@@ -69,10 +69,10 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
fi
|
||||
|
||||
case "$line" in
|
||||
/ | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /etc | /etc/* | /var/db | /var/db/*)
|
||||
WHITELIST_WARNINGS+=("Protected system path: $line")
|
||||
continue
|
||||
;;
|
||||
/ | /System | /System/* | /bin | /bin/* | /sbin | /sbin/* | /usr/bin | /usr/bin/* | /usr/sbin | /usr/sbin/* | /etc | /etc/* | /var/db | /var/db/*)
|
||||
WHITELIST_WARNINGS+=("Protected system path: $line")
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
duplicate="false"
|
||||
@@ -86,7 +86,7 @@ if [[ -f "$HOME/.config/mole/whitelist" ]]; then
|
||||
fi
|
||||
[[ "$duplicate" == "true" ]] && continue
|
||||
WHITELIST_PATTERNS+=("$line")
|
||||
done < "$HOME/.config/mole/whitelist"
|
||||
done <"$HOME/.config/mole/whitelist"
|
||||
else
|
||||
WHITELIST_PATTERNS=("${DEFAULT_WHITELIST_PATTERNS[@]}")
|
||||
fi
|
||||
@@ -140,7 +140,7 @@ cleanup() {
|
||||
fi
|
||||
CLEANUP_DONE=true
|
||||
|
||||
stop_inline_spinner 2> /dev/null || true
|
||||
stop_inline_spinner 2>/dev/null || true
|
||||
|
||||
cleanup_temp_files
|
||||
|
||||
@@ -162,8 +162,8 @@ start_section() {
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
ensure_user_file "$EXPORT_LIST_FILE"
|
||||
echo "" >> "$EXPORT_LIST_FILE"
|
||||
echo "=== $1 ===" >> "$EXPORT_LIST_FILE"
|
||||
echo "" >>"$EXPORT_LIST_FILE"
|
||||
echo "=== $1 ===" >>"$EXPORT_LIST_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ normalize_paths_for_cleanup() {
|
||||
done
|
||||
fi
|
||||
[[ "$is_child" == "true" ]] || result_paths+=("$path")
|
||||
done <<< "$sorted_paths"
|
||||
done <<<"$sorted_paths"
|
||||
|
||||
if [[ ${#result_paths[@]} -gt 0 ]]; then
|
||||
printf '%s\n' "${result_paths[@]}"
|
||||
@@ -228,9 +228,9 @@ get_cleanup_path_size_kb() {
|
||||
local path="$1"
|
||||
|
||||
if [[ -f "$path" && ! -L "$path" ]]; then
|
||||
if command -v stat > /dev/null 2>&1; then
|
||||
if command -v stat >/dev/null 2>&1; then
|
||||
local bytes
|
||||
bytes=$(stat -f%z "$path" 2> /dev/null || echo "0")
|
||||
bytes=$(stat -f%z "$path" 2>/dev/null || echo "0")
|
||||
if [[ "$bytes" =~ ^[0-9]+$ && "$bytes" -gt 0 ]]; then
|
||||
echo $(((bytes + 1023) / 1024))
|
||||
return 0
|
||||
@@ -239,9 +239,9 @@ get_cleanup_path_size_kb() {
|
||||
fi
|
||||
|
||||
if [[ -L "$path" ]]; then
|
||||
if command -v stat > /dev/null 2>&1; then
|
||||
if command -v stat >/dev/null 2>&1; then
|
||||
local bytes
|
||||
bytes=$(stat -f%z "$path" 2> /dev/null || echo "0")
|
||||
bytes=$(stat -f%z "$path" 2>/dev/null || echo "0")
|
||||
if [[ "$bytes" =~ ^[0-9]+$ && "$bytes" -gt 0 ]]; then
|
||||
echo $(((bytes + 1023) / 1024))
|
||||
else
|
||||
@@ -461,9 +461,9 @@ safe_clean() {
|
||||
[[ ! "$size" =~ ^[0-9]+$ ]] && size=0
|
||||
|
||||
if [[ "$size" -gt 0 ]]; then
|
||||
echo "$size 1" > "$temp_dir/result_${idx}"
|
||||
echo "$size 1" >"$temp_dir/result_${idx}"
|
||||
else
|
||||
echo "0 0" > "$temp_dir/result_${idx}"
|
||||
echo "0 0" >"$temp_dir/result_${idx}"
|
||||
fi
|
||||
|
||||
((idx++))
|
||||
@@ -488,17 +488,17 @@ safe_clean() {
|
||||
[[ ! "$size" =~ ^[0-9]+$ ]] && size=0
|
||||
local tmp_file="$temp_dir/result_${idx}.$$"
|
||||
if [[ "$size" -gt 0 ]]; then
|
||||
echo "$size 1" > "$tmp_file"
|
||||
echo "$size 1" >"$tmp_file"
|
||||
else
|
||||
echo "0 0" > "$tmp_file"
|
||||
echo "0 0" >"$tmp_file"
|
||||
fi
|
||||
mv "$tmp_file" "$temp_dir/result_${idx}" 2> /dev/null || true
|
||||
mv "$tmp_file" "$temp_dir/result_${idx}" 2>/dev/null || true
|
||||
) &
|
||||
pids+=($!)
|
||||
((idx++))
|
||||
|
||||
if ((${#pids[@]} >= MOLE_MAX_PARALLEL_JOBS)); then
|
||||
wait "${pids[0]}" 2> /dev/null || true
|
||||
wait "${pids[0]}" 2>/dev/null || true
|
||||
pids=("${pids[@]:1}")
|
||||
((completed++))
|
||||
|
||||
@@ -511,7 +511,7 @@ safe_clean() {
|
||||
|
||||
if [[ ${#pids[@]} -gt 0 ]]; then
|
||||
for pid in "${pids[@]}"; do
|
||||
wait "$pid" 2> /dev/null || true
|
||||
wait "$pid" 2>/dev/null || true
|
||||
((completed++))
|
||||
|
||||
if [[ "$show_spinner" == "true" && -t 1 ]]; then
|
||||
@@ -532,7 +532,7 @@ safe_clean() {
|
||||
for path in "${existing_paths[@]}"; do
|
||||
local result_file="$temp_dir/result_${idx}"
|
||||
if [[ -f "$result_file" ]]; then
|
||||
read -r size count < "$result_file" 2> /dev/null || true
|
||||
read -r size count <"$result_file" 2>/dev/null || true
|
||||
local removed=0
|
||||
if [[ "$DRY_RUN" != "true" ]]; then
|
||||
if safe_remove "$path" true; then
|
||||
@@ -631,9 +631,9 @@ safe_clean() {
|
||||
local size=0
|
||||
|
||||
if [[ -n "${temp_dir:-}" && -f "$temp_dir/result_${idx}" ]]; then
|
||||
read -r size count < "$temp_dir/result_${idx}" 2> /dev/null || true
|
||||
read -r size count <"$temp_dir/result_${idx}" 2>/dev/null || true
|
||||
else
|
||||
size=$(get_cleanup_path_size_kb "$path" 2> /dev/null || echo "0")
|
||||
size=$(get_cleanup_path_size_kb "$path" 2>/dev/null || echo "0")
|
||||
fi
|
||||
|
||||
[[ "$size" == "0" || -z "$size" ]] && {
|
||||
@@ -641,7 +641,7 @@ safe_clean() {
|
||||
continue
|
||||
}
|
||||
|
||||
echo "$(dirname "$path")|$size|$path" >> "$paths_temp"
|
||||
echo "$(dirname "$path")|$size|$path" >>"$paths_temp"
|
||||
((idx++))
|
||||
done
|
||||
fi
|
||||
@@ -672,9 +672,9 @@ safe_clean() {
|
||||
' | while IFS='|' read -r display_path total_size child_count; do
|
||||
local size_human=$(bytes_to_human "$((total_size * 1024))")
|
||||
if [[ $child_count -gt 1 ]]; then
|
||||
echo "$display_path # $size_human, $child_count items" >> "$EXPORT_LIST_FILE"
|
||||
echo "$display_path # $size_human, $child_count items" >>"$EXPORT_LIST_FILE"
|
||||
else
|
||||
echo "$display_path # $size_human" >> "$EXPORT_LIST_FILE"
|
||||
echo "$display_path # $size_human" >>"$EXPORT_LIST_FILE"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -714,7 +714,7 @@ start_cleanup() {
|
||||
SYSTEM_CLEAN=false
|
||||
|
||||
ensure_user_file "$EXPORT_LIST_FILE"
|
||||
cat > "$EXPORT_LIST_FILE" << EOF
|
||||
cat >"$EXPORT_LIST_FILE" <<EOF
|
||||
# Mole Cleanup Preview - $(date '+%Y-%m-%d %H:%M:%S')
|
||||
#
|
||||
# How to protect files:
|
||||
@@ -730,7 +730,7 @@ EOF
|
||||
fi
|
||||
|
||||
if [[ -t 0 ]]; then
|
||||
if sudo -n true 2> /dev/null; then
|
||||
if sudo -n true 2>/dev/null; then
|
||||
SYSTEM_CLEAN=true
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Admin access already available"
|
||||
echo ""
|
||||
@@ -770,7 +770,7 @@ EOF
|
||||
else
|
||||
echo ""
|
||||
echo "Running in non-interactive mode"
|
||||
if sudo -n true 2> /dev/null; then
|
||||
if sudo -n true 2>/dev/null; then
|
||||
SYSTEM_CLEAN=true
|
||||
echo " ${ICON_LIST} System-level cleanup enabled, sudo session active"
|
||||
else
|
||||
@@ -1022,7 +1022,7 @@ perform_cleanup() {
|
||||
echo "# Potential cleanup: ${freed_gb}GB"
|
||||
echo "# Items: $files_cleaned"
|
||||
echo "# Categories: $total_items"
|
||||
} >> "$EXPORT_LIST_FILE"
|
||||
} >>"$EXPORT_LIST_FILE"
|
||||
|
||||
summary_details+=("Detailed file list: ${GRAY}$EXPORT_LIST_FILE${NC}")
|
||||
summary_details+=("Use ${GRAY}mo clean --whitelist${NC} to add protection rules")
|
||||
@@ -1078,30 +1078,22 @@ perform_cleanup() {
|
||||
main() {
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
"--help" | "-h")
|
||||
echo "Usage: mo clean [OPTIONS]"
|
||||
echo ""
|
||||
echo "Clean up disk space by removing caches, logs, and temporary files."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --dry-run, -n Preview cleanup without making changes"
|
||||
echo " --whitelist Manage protected paths"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--dry-run" | "-n")
|
||||
DRY_RUN=true
|
||||
export MOLE_DRY_RUN=1
|
||||
;;
|
||||
"--whitelist")
|
||||
source "$SCRIPT_DIR/../lib/manage/whitelist.sh"
|
||||
manage_whitelist "clean"
|
||||
exit 0
|
||||
;;
|
||||
"--help" | "-h")
|
||||
show_clean_help
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--dry-run" | "-n")
|
||||
DRY_RUN=true
|
||||
export MOLE_DRY_RUN=1
|
||||
;;
|
||||
"--whitelist")
|
||||
source "$SCRIPT_DIR/../lib/manage/whitelist.sh"
|
||||
manage_whitelist "clean"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
266
bin/installer.sh
266
bin/installer.sh
@@ -47,9 +47,9 @@ readonly MAX_ZIP_ENTRIES=50
|
||||
ZIP_LIST_CMD=()
|
||||
IN_ALT_SCREEN=0
|
||||
|
||||
if command -v zipinfo > /dev/null 2>&1; then
|
||||
if command -v zipinfo >/dev/null 2>&1; then
|
||||
ZIP_LIST_CMD=(zipinfo -1)
|
||||
elif command -v unzip > /dev/null 2>&1; then
|
||||
elif command -v unzip >/dev/null 2>&1; then
|
||||
ZIP_LIST_CMD=(unzip -Z -1)
|
||||
fi
|
||||
|
||||
@@ -62,7 +62,7 @@ is_installer_zip() {
|
||||
|
||||
[[ ${#ZIP_LIST_CMD[@]} -gt 0 ]] || return 1
|
||||
|
||||
if ! "${ZIP_LIST_CMD[@]}" "$zip" 2> /dev/null |
|
||||
if ! "${ZIP_LIST_CMD[@]}" "$zip" 2>/dev/null |
|
||||
head -n "$cap" |
|
||||
awk '
|
||||
/\.(app|pkg|dmg|xip)(\/|$)/ { found=1; exit 0 }
|
||||
@@ -79,15 +79,15 @@ handle_candidate_file() {
|
||||
|
||||
[[ -L "$file" ]] && return 0 # Skip symlinks explicitly
|
||||
case "$file" in
|
||||
*.dmg | *.pkg | *.mpkg | *.iso | *.xip)
|
||||
*.dmg | *.pkg | *.mpkg | *.iso | *.xip)
|
||||
echo "$file"
|
||||
;;
|
||||
*.zip)
|
||||
[[ -r "$file" ]] || return 0
|
||||
if is_installer_zip "$file" 2>/dev/null; then
|
||||
echo "$file"
|
||||
;;
|
||||
*.zip)
|
||||
[[ -r "$file" ]] || return 0
|
||||
if is_installer_zip "$file" 2> /dev/null; then
|
||||
echo "$file"
|
||||
fi
|
||||
;;
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -99,13 +99,13 @@ scan_installers_in_path() {
|
||||
|
||||
local file
|
||||
|
||||
if command -v fd > /dev/null 2>&1; then
|
||||
if command -v fd >/dev/null 2>&1; then
|
||||
while IFS= read -r file; do
|
||||
handle_candidate_file "$file"
|
||||
done < <(
|
||||
fd --no-ignore --hidden --type f --max-depth "$max_depth" \
|
||||
-e dmg -e pkg -e mpkg -e iso -e xip -e zip \
|
||||
. "$path" 2> /dev/null || true
|
||||
. "$path" 2>/dev/null || true
|
||||
)
|
||||
else
|
||||
while IFS= read -r file; do
|
||||
@@ -114,7 +114,7 @@ scan_installers_in_path() {
|
||||
find "$path" -maxdepth "$max_depth" -type f \
|
||||
\( -name '*.dmg' -o -name '*.pkg' -o -name '*.mpkg' \
|
||||
-o -name '*.iso' -o -name '*.xip' -o -name '*.zip' \) \
|
||||
2> /dev/null || true
|
||||
2>/dev/null || true
|
||||
)
|
||||
fi
|
||||
}
|
||||
@@ -142,23 +142,23 @@ get_source_display() {
|
||||
|
||||
# Match against known paths and return friendly names
|
||||
case "$dir_path" in
|
||||
"$HOME/Downloads"*) echo "Downloads" ;;
|
||||
"$HOME/Desktop"*) echo "Desktop" ;;
|
||||
"$HOME/Documents"*) echo "Documents" ;;
|
||||
"$HOME/Public"*) echo "Public" ;;
|
||||
"$HOME/Library/Downloads"*) echo "Library" ;;
|
||||
"/Users/Shared"*) echo "Shared" ;;
|
||||
"$HOME/Library/Caches/Homebrew"*) echo "Homebrew" ;;
|
||||
"$HOME/Library/Mobile Documents/com~apple~CloudDocs/Downloads"*) echo "iCloud" ;;
|
||||
"$HOME/Library/Containers/com.apple.mail"*) echo "Mail" ;;
|
||||
*"Telegram Desktop"*) echo "Telegram" ;;
|
||||
*) echo "${dir_path##*/}" ;;
|
||||
"$HOME/Downloads"*) echo "Downloads" ;;
|
||||
"$HOME/Desktop"*) echo "Desktop" ;;
|
||||
"$HOME/Documents"*) echo "Documents" ;;
|
||||
"$HOME/Public"*) echo "Public" ;;
|
||||
"$HOME/Library/Downloads"*) echo "Library" ;;
|
||||
"/Users/Shared"*) echo "Shared" ;;
|
||||
"$HOME/Library/Caches/Homebrew"*) echo "Homebrew" ;;
|
||||
"$HOME/Library/Mobile Documents/com~apple~CloudDocs/Downloads"*) echo "iCloud" ;;
|
||||
"$HOME/Library/Containers/com.apple.mail"*) echo "Mail" ;;
|
||||
*"Telegram Desktop"*) echo "Telegram" ;;
|
||||
*) echo "${dir_path##*/}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_terminal_width() {
|
||||
if [[ $TERMINAL_WIDTH -le 0 ]]; then
|
||||
TERMINAL_WIDTH=$(tput cols 2> /dev/null || echo 80)
|
||||
TERMINAL_WIDTH=$(tput cols 2>/dev/null || echo 80)
|
||||
fi
|
||||
echo "$TERMINAL_WIDTH"
|
||||
}
|
||||
@@ -291,11 +291,11 @@ select_installers() {
|
||||
_get_items_per_page() {
|
||||
local term_height=24
|
||||
if [[ -t 0 ]] || [[ -t 2 ]]; then
|
||||
term_height=$(stty size < /dev/tty 2> /dev/null | awk '{print $1}')
|
||||
term_height=$(stty size </dev/tty 2>/dev/null | awk '{print $1}')
|
||||
fi
|
||||
if [[ -z "$term_height" || $term_height -le 0 ]]; then
|
||||
if command -v tput > /dev/null 2>&1; then
|
||||
term_height=$(tput lines 2> /dev/null || echo "24")
|
||||
if command -v tput >/dev/null 2>&1; then
|
||||
term_height=$(tput lines 2>/dev/null || echo "24")
|
||||
else
|
||||
term_height=24
|
||||
fi
|
||||
@@ -322,8 +322,8 @@ select_installers() {
|
||||
done
|
||||
|
||||
local original_stty=""
|
||||
if [[ -t 0 ]] && command -v stty > /dev/null 2>&1; then
|
||||
original_stty=$(stty -g 2> /dev/null || echo "")
|
||||
if [[ -t 0 ]] && command -v stty >/dev/null 2>&1; then
|
||||
original_stty=$(stty -g 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
restore_terminal() {
|
||||
@@ -334,7 +334,7 @@ select_installers() {
|
||||
fi
|
||||
show_cursor
|
||||
if [[ -n "${original_stty:-}" ]]; then
|
||||
stty "${original_stty}" 2> /dev/null || stty sane 2> /dev/null || true
|
||||
stty "${original_stty}" 2>/dev/null || stty sane 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ select_installers() {
|
||||
|
||||
trap restore_terminal EXIT
|
||||
trap handle_interrupt INT TERM
|
||||
stty -echo -icanon intr ^C 2> /dev/null || true
|
||||
stty -echo -icanon intr ^C 2>/dev/null || true
|
||||
hide_cursor
|
||||
if [[ -t 1 ]]; then
|
||||
printf "\033[2J\033[H" >&2
|
||||
@@ -429,75 +429,75 @@ select_installers() {
|
||||
|
||||
IFS= read -r -s -n1 key || key=""
|
||||
case "$key" in
|
||||
$'\x1b')
|
||||
IFS= read -r -s -n1 -t 1 key2 || key2=""
|
||||
if [[ "$key2" == "[" ]]; then
|
||||
IFS= read -r -s -n1 -t 1 key3 || key3=""
|
||||
case "$key3" in
|
||||
A) # Up arrow
|
||||
if [[ $cursor_pos -gt 0 ]]; then
|
||||
((cursor_pos--))
|
||||
elif [[ $top_index -gt 0 ]]; then
|
||||
((top_index--))
|
||||
fi
|
||||
;;
|
||||
B) # Down arrow
|
||||
local absolute_index=$((top_index + cursor_pos))
|
||||
local last_index=$((total_items - 1))
|
||||
if [[ $absolute_index -lt $last_index ]]; then
|
||||
local visible_count=$((total_items - top_index))
|
||||
[[ $visible_count -gt $items_per_page ]] && visible_count=$items_per_page
|
||||
if [[ $cursor_pos -lt $((visible_count - 1)) ]]; then
|
||||
((cursor_pos++))
|
||||
elif [[ $((top_index + visible_count)) -lt $total_items ]]; then
|
||||
((top_index++))
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# ESC alone
|
||||
restore_terminal
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
" ") # Space - toggle current item
|
||||
local idx=$((top_index + cursor_pos))
|
||||
if [[ ${selected[idx]} == true ]]; then
|
||||
selected[idx]=false
|
||||
else
|
||||
selected[idx]=true
|
||||
fi
|
||||
;;
|
||||
"a" | "A") # Select all
|
||||
for ((i = 0; i < total_items; i++)); do
|
||||
selected[i]=true
|
||||
done
|
||||
;;
|
||||
"i" | "I") # Invert selection
|
||||
for ((i = 0; i < total_items; i++)); do
|
||||
if [[ ${selected[i]} == true ]]; then
|
||||
selected[i]=false
|
||||
else
|
||||
selected[i]=true
|
||||
$'\x1b')
|
||||
IFS= read -r -s -n1 -t 1 key2 || key2=""
|
||||
if [[ "$key2" == "[" ]]; then
|
||||
IFS= read -r -s -n1 -t 1 key3 || key3=""
|
||||
case "$key3" in
|
||||
A) # Up arrow
|
||||
if [[ $cursor_pos -gt 0 ]]; then
|
||||
((cursor_pos--))
|
||||
elif [[ $top_index -gt 0 ]]; then
|
||||
((top_index--))
|
||||
fi
|
||||
done
|
||||
;;
|
||||
"q" | "Q" | $'\x03') # Quit or Ctrl-C
|
||||
;;
|
||||
B) # Down arrow
|
||||
local absolute_index=$((top_index + cursor_pos))
|
||||
local last_index=$((total_items - 1))
|
||||
if [[ $absolute_index -lt $last_index ]]; then
|
||||
local visible_count=$((total_items - top_index))
|
||||
[[ $visible_count -gt $items_per_page ]] && visible_count=$items_per_page
|
||||
if [[ $cursor_pos -lt $((visible_count - 1)) ]]; then
|
||||
((cursor_pos++))
|
||||
elif [[ $((top_index + visible_count)) -lt $total_items ]]; then
|
||||
((top_index++))
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# ESC alone
|
||||
restore_terminal
|
||||
return 1
|
||||
;;
|
||||
"" | $'\n' | $'\r') # Enter - confirm
|
||||
MOLE_SELECTION_RESULT=""
|
||||
for ((i = 0; i < total_items; i++)); do
|
||||
if [[ ${selected[i]} == true ]]; then
|
||||
[[ -n "$MOLE_SELECTION_RESULT" ]] && MOLE_SELECTION_RESULT+=","
|
||||
MOLE_SELECTION_RESULT+="$i"
|
||||
fi
|
||||
done
|
||||
restore_terminal
|
||||
return 0
|
||||
;;
|
||||
fi
|
||||
;;
|
||||
" ") # Space - toggle current item
|
||||
local idx=$((top_index + cursor_pos))
|
||||
if [[ ${selected[idx]} == true ]]; then
|
||||
selected[idx]=false
|
||||
else
|
||||
selected[idx]=true
|
||||
fi
|
||||
;;
|
||||
"a" | "A") # Select all
|
||||
for ((i = 0; i < total_items; i++)); do
|
||||
selected[i]=true
|
||||
done
|
||||
;;
|
||||
"i" | "I") # Invert selection
|
||||
for ((i = 0; i < total_items; i++)); do
|
||||
if [[ ${selected[i]} == true ]]; then
|
||||
selected[i]=false
|
||||
else
|
||||
selected[i]=true
|
||||
fi
|
||||
done
|
||||
;;
|
||||
"q" | "Q" | $'\x03') # Quit or Ctrl-C
|
||||
restore_terminal
|
||||
return 1
|
||||
;;
|
||||
"" | $'\n' | $'\r') # Enter - confirm
|
||||
MOLE_SELECTION_RESULT=""
|
||||
for ((i = 0; i < total_items; i++)); do
|
||||
if [[ ${selected[i]} == true ]]; then
|
||||
[[ -n "$MOLE_SELECTION_RESULT" ]] && MOLE_SELECTION_RESULT+=","
|
||||
MOLE_SELECTION_RESULT+="$i"
|
||||
fi
|
||||
done
|
||||
restore_terminal
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
@@ -522,7 +522,7 @@ show_installer_menu() {
|
||||
delete_selected_installers() {
|
||||
# Parse selection indices
|
||||
local -a selected_indices=()
|
||||
[[ -n "$MOLE_SELECTION_RESULT" ]] && IFS=',' read -ra selected_indices <<< "$MOLE_SELECTION_RESULT"
|
||||
[[ -n "$MOLE_SELECTION_RESULT" ]] && IFS=',' read -ra selected_indices <<<"$MOLE_SELECTION_RESULT"
|
||||
|
||||
if [[ ${#selected_indices[@]} -eq 0 ]]; then
|
||||
return 1
|
||||
@@ -556,16 +556,16 @@ delete_selected_installers() {
|
||||
|
||||
IFS= read -r -s -n1 confirm || confirm=""
|
||||
case "$confirm" in
|
||||
$'\e' | q | Q)
|
||||
return 1
|
||||
;;
|
||||
"" | $'\n' | $'\r')
|
||||
printf "\r\033[K" # Clear prompt line
|
||||
echo "" # Single line break
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
$'\e' | q | Q)
|
||||
return 1
|
||||
;;
|
||||
"" | $'\n' | $'\r')
|
||||
printf "\r\033[K" # Clear prompt line
|
||||
echo "" # Single line break
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Delete each selected installer with spinner
|
||||
@@ -668,23 +668,17 @@ show_summary() {
|
||||
main() {
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
"--help" | "-h")
|
||||
echo "Usage: mo installer [OPTIONS]"
|
||||
echo ""
|
||||
echo "Find and remove installer files (.dmg, .pkg, .iso, .xip, .zip)."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $arg"
|
||||
exit 1
|
||||
;;
|
||||
"--help" | "-h")
|
||||
show_installer_help
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $arg"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -694,15 +688,15 @@ main() {
|
||||
show_cursor
|
||||
|
||||
case $exit_code in
|
||||
0)
|
||||
show_summary
|
||||
;;
|
||||
1)
|
||||
printf '\n'
|
||||
;;
|
||||
2)
|
||||
# Already handled by collect_installers
|
||||
;;
|
||||
0)
|
||||
show_summary
|
||||
;;
|
||||
1)
|
||||
printf '\n'
|
||||
;;
|
||||
2)
|
||||
# Already handled by collect_installers
|
||||
;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
|
||||
100
bin/optimize.sh
100
bin/optimize.sh
@@ -139,12 +139,12 @@ show_optimization_summary() {
|
||||
show_system_health() {
|
||||
local health_json="$1"
|
||||
|
||||
local mem_used=$(echo "$health_json" | jq -r '.memory_used_gb // 0' 2> /dev/null || echo "0")
|
||||
local mem_total=$(echo "$health_json" | jq -r '.memory_total_gb // 0' 2> /dev/null || echo "0")
|
||||
local disk_used=$(echo "$health_json" | jq -r '.disk_used_gb // 0' 2> /dev/null || echo "0")
|
||||
local disk_total=$(echo "$health_json" | jq -r '.disk_total_gb // 0' 2> /dev/null || echo "0")
|
||||
local disk_percent=$(echo "$health_json" | jq -r '.disk_used_percent // 0' 2> /dev/null || echo "0")
|
||||
local uptime=$(echo "$health_json" | jq -r '.uptime_days // 0' 2> /dev/null || echo "0")
|
||||
local mem_used=$(echo "$health_json" | jq -r '.memory_used_gb // 0' 2>/dev/null || echo "0")
|
||||
local mem_total=$(echo "$health_json" | jq -r '.memory_total_gb // 0' 2>/dev/null || echo "0")
|
||||
local disk_used=$(echo "$health_json" | jq -r '.disk_used_gb // 0' 2>/dev/null || echo "0")
|
||||
local disk_total=$(echo "$health_json" | jq -r '.disk_total_gb // 0' 2>/dev/null || echo "0")
|
||||
local disk_percent=$(echo "$health_json" | jq -r '.disk_used_percent // 0' 2>/dev/null || echo "0")
|
||||
local uptime=$(echo "$health_json" | jq -r '.uptime_days // 0' 2>/dev/null || echo "0")
|
||||
|
||||
mem_used=${mem_used:-0}
|
||||
mem_total=${mem_total:-0}
|
||||
@@ -159,7 +159,7 @@ show_system_health() {
|
||||
|
||||
parse_optimizations() {
|
||||
local health_json="$1"
|
||||
echo "$health_json" | jq -c '.optimizations[]' 2> /dev/null
|
||||
echo "$health_json" | jq -c '.optimizations[]' 2>/dev/null
|
||||
}
|
||||
|
||||
announce_action() {
|
||||
@@ -177,12 +177,12 @@ announce_action() {
|
||||
|
||||
touchid_configured() {
|
||||
local pam_file="/etc/pam.d/sudo"
|
||||
[[ -f "$pam_file" ]] && grep -q "pam_tid.so" "$pam_file" 2> /dev/null
|
||||
[[ -f "$pam_file" ]] && grep -q "pam_tid.so" "$pam_file" 2>/dev/null
|
||||
}
|
||||
|
||||
touchid_supported() {
|
||||
if command -v bioutil > /dev/null 2>&1; then
|
||||
if bioutil -r 2> /dev/null | grep -qi "Touch ID"; then
|
||||
if command -v bioutil >/dev/null 2>&1; then
|
||||
if bioutil -r 2>/dev/null | grep -qi "Touch ID"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
@@ -272,7 +272,7 @@ ask_for_security_fixes() {
|
||||
echo ""
|
||||
echo -e "${BLUE}SECURITY FIXES${NC}"
|
||||
for entry in "${SECURITY_FIXES[@]}"; do
|
||||
IFS='|' read -r _ label <<< "$entry"
|
||||
IFS='|' read -r _ label <<<"$entry"
|
||||
echo -e " ${ICON_LIST} $label"
|
||||
done
|
||||
echo ""
|
||||
@@ -299,7 +299,7 @@ ask_for_security_fixes() {
|
||||
}
|
||||
|
||||
apply_firewall_fix() {
|
||||
if sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on > /dev/null 2>&1; then
|
||||
if sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on >/dev/null 2>&1; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Firewall enabled"
|
||||
FIREWALL_DISABLED=false
|
||||
return 0
|
||||
@@ -309,7 +309,7 @@ apply_firewall_fix() {
|
||||
}
|
||||
|
||||
apply_gatekeeper_fix() {
|
||||
if sudo spctl --master-enable 2> /dev/null; then
|
||||
if sudo spctl --master-enable 2>/dev/null; then
|
||||
echo -e " ${GREEN}${ICON_SUCCESS}${NC} Gatekeeper enabled"
|
||||
GATEKEEPER_DISABLED=false
|
||||
return 0
|
||||
@@ -333,17 +333,17 @@ perform_security_fixes() {
|
||||
|
||||
local applied=0
|
||||
for entry in "${SECURITY_FIXES[@]}"; do
|
||||
IFS='|' read -r action _ <<< "$entry"
|
||||
IFS='|' read -r action _ <<<"$entry"
|
||||
case "$action" in
|
||||
firewall)
|
||||
apply_firewall_fix && ((applied++))
|
||||
;;
|
||||
gatekeeper)
|
||||
apply_gatekeeper_fix && ((applied++))
|
||||
;;
|
||||
touchid)
|
||||
apply_touchid_fix && ((applied++))
|
||||
;;
|
||||
firewall)
|
||||
apply_firewall_fix && ((applied++))
|
||||
;;
|
||||
gatekeeper)
|
||||
apply_gatekeeper_fix && ((applied++))
|
||||
;;
|
||||
touchid)
|
||||
apply_touchid_fix && ((applied++))
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -354,7 +354,7 @@ perform_security_fixes() {
|
||||
}
|
||||
|
||||
cleanup_all() {
|
||||
stop_inline_spinner 2> /dev/null || true
|
||||
stop_inline_spinner 2>/dev/null || true
|
||||
stop_sudo_session
|
||||
cleanup_temp_files
|
||||
# Log session end
|
||||
@@ -373,28 +373,20 @@ main() {
|
||||
local health_json
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
"--help" | "-h")
|
||||
echo "Usage: mo optimize [OPTIONS]"
|
||||
echo ""
|
||||
echo "Check and maintain system health, apply optimizations."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --dry-run Preview optimization without making changes"
|
||||
echo " --whitelist Manage protected items"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--dry-run")
|
||||
export MOLE_DRY_RUN=1
|
||||
;;
|
||||
"--whitelist")
|
||||
manage_whitelist "optimize"
|
||||
exit 0
|
||||
;;
|
||||
"--help" | "-h")
|
||||
show_optimize_help
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--dry-run")
|
||||
export MOLE_DRY_RUN=1
|
||||
;;
|
||||
"--whitelist")
|
||||
manage_whitelist "optimize"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -413,13 +405,13 @@ main() {
|
||||
echo -e "${YELLOW}${ICON_DRY_RUN} DRY RUN MODE${NC}, No files will be modified\n"
|
||||
fi
|
||||
|
||||
if ! command -v jq > /dev/null 2>&1; then
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW}${ICON_ERROR}${NC} Missing dependency: jq"
|
||||
echo -e "${GRAY}Install with: ${GREEN}brew install jq${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v bc > /dev/null 2>&1; then
|
||||
if ! command -v bc >/dev/null 2>&1; then
|
||||
echo -e "${YELLOW}${ICON_ERROR}${NC} Missing dependency: bc"
|
||||
echo -e "${GRAY}Install with: ${GREEN}brew install bc${NC}"
|
||||
exit 1
|
||||
@@ -429,7 +421,7 @@ main() {
|
||||
start_inline_spinner "Collecting system info..."
|
||||
fi
|
||||
|
||||
if ! health_json=$(generate_health_json 2> /dev/null); then
|
||||
if ! health_json=$(generate_health_json 2>/dev/null); then
|
||||
if [[ -t 1 ]]; then
|
||||
stop_inline_spinner
|
||||
fi
|
||||
@@ -438,7 +430,7 @@ main() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! echo "$health_json" | jq empty 2> /dev/null; then
|
||||
if ! echo "$health_json" | jq empty 2>/dev/null; then
|
||||
if [[ -t 1 ]]; then
|
||||
stop_inline_spinner
|
||||
fi
|
||||
@@ -470,7 +462,7 @@ main() {
|
||||
local -a confirm_items=()
|
||||
local opts_file
|
||||
opts_file=$(mktemp_file)
|
||||
parse_optimizations "$health_json" > "$opts_file"
|
||||
parse_optimizations "$health_json" >"$opts_file"
|
||||
|
||||
while IFS= read -r opt_json; do
|
||||
[[ -z "$opt_json" ]] && continue
|
||||
@@ -488,7 +480,7 @@ main() {
|
||||
else
|
||||
confirm_items+=("$item")
|
||||
fi
|
||||
done < "$opts_file"
|
||||
done <"$opts_file"
|
||||
|
||||
echo ""
|
||||
if [[ "${MOLE_DRY_RUN:-0}" != "1" ]]; then
|
||||
@@ -498,7 +490,7 @@ main() {
|
||||
export FIRST_ACTION=true
|
||||
if [[ ${#safe_items[@]} -gt 0 ]]; then
|
||||
for item in "${safe_items[@]}"; do
|
||||
IFS='|' read -r name desc action path <<< "$item"
|
||||
IFS='|' read -r name desc action path <<<"$item"
|
||||
announce_action "$name" "$desc" "safe"
|
||||
execute_optimization "$action" "$path"
|
||||
done
|
||||
@@ -506,7 +498,7 @@ main() {
|
||||
|
||||
if [[ ${#confirm_items[@]} -gt 0 ]]; then
|
||||
for item in "${confirm_items[@]}"; do
|
||||
IFS='|' read -r name desc action path <<< "$item"
|
||||
IFS='|' read -r name desc action path <<<"$item"
|
||||
announce_action "$name" "$desc" "confirm"
|
||||
execute_optimization "$action" "$path"
|
||||
done
|
||||
|
||||
135
bin/touchid.sh
135
bin/touchid.sh
@@ -26,21 +26,21 @@ readonly PAM_TID_LINE="auth sufficient pam_tid.so"
|
||||
is_touchid_configured() {
|
||||
# Check sudo_local first
|
||||
if [[ -f "$PAM_SUDO_LOCAL_FILE" ]]; then
|
||||
grep -q "pam_tid.so" "$PAM_SUDO_LOCAL_FILE" 2> /dev/null && return 0
|
||||
grep -q "pam_tid.so" "$PAM_SUDO_LOCAL_FILE" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
# Fallback to standard sudo file
|
||||
if [[ ! -f "$PAM_SUDO_FILE" ]]; then
|
||||
return 1
|
||||
fi
|
||||
grep -q "pam_tid.so" "$PAM_SUDO_FILE" 2> /dev/null
|
||||
grep -q "pam_tid.so" "$PAM_SUDO_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
# Check if system supports Touch ID
|
||||
supports_touchid() {
|
||||
# Check if bioutil exists and has Touch ID capability
|
||||
if command -v bioutil &> /dev/null; then
|
||||
bioutil -r 2> /dev/null | grep -q "Touch ID" && return 0
|
||||
if command -v bioutil &>/dev/null; then
|
||||
bioutil -r 2>/dev/null | grep -q "Touch ID" && return 0
|
||||
fi
|
||||
|
||||
# Fallback: check if running on Apple Silicon or modern Intel Mac
|
||||
@@ -52,7 +52,7 @@ supports_touchid() {
|
||||
|
||||
# For Intel Macs, check if it's 2018 or later (approximation)
|
||||
local model_year
|
||||
model_year=$(system_profiler SPHardwareDataType 2> /dev/null | grep "Model Identifier" | grep -o "[0-9]\{4\}" | head -1)
|
||||
model_year=$(system_profiler SPHardwareDataType 2>/dev/null | grep "Model Identifier" | grep -o "[0-9]\{4\}" | head -1)
|
||||
if [[ -n "$model_year" ]] && [[ "$model_year" -ge 2018 ]]; then
|
||||
return 0
|
||||
fi
|
||||
@@ -93,8 +93,8 @@ enable_touchid() {
|
||||
if grep -q "pam_tid.so" "$PAM_SUDO_FILE"; then
|
||||
# Clean up legacy config
|
||||
temp_file=$(create_temp_file)
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" > "$temp_file"
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2> /dev/null; then
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" >"$temp_file"
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2>/dev/null; then
|
||||
echo -e "${GREEN}${ICON_SUCCESS} Cleanup legacy configuration${NC}"
|
||||
fi
|
||||
fi
|
||||
@@ -113,8 +113,8 @@ enable_touchid() {
|
||||
local write_success=false
|
||||
if [[ ! -f "$PAM_SUDO_LOCAL_FILE" ]]; then
|
||||
# Create the file
|
||||
echo "# sudo_local: local customizations for sudo" | sudo tee "$PAM_SUDO_LOCAL_FILE" > /dev/null
|
||||
echo "$PAM_TID_LINE" | sudo tee -a "$PAM_SUDO_LOCAL_FILE" > /dev/null
|
||||
echo "# sudo_local: local customizations for sudo" | sudo tee "$PAM_SUDO_LOCAL_FILE" >/dev/null
|
||||
echo "$PAM_TID_LINE" | sudo tee -a "$PAM_SUDO_LOCAL_FILE" >/dev/null
|
||||
sudo chmod 444 "$PAM_SUDO_LOCAL_FILE"
|
||||
sudo chown root:wheel "$PAM_SUDO_LOCAL_FILE"
|
||||
write_success=true
|
||||
@@ -123,7 +123,7 @@ enable_touchid() {
|
||||
if ! grep -q "pam_tid.so" "$PAM_SUDO_LOCAL_FILE"; then
|
||||
temp_file=$(create_temp_file)
|
||||
cp "$PAM_SUDO_LOCAL_FILE" "$temp_file"
|
||||
echo "$PAM_TID_LINE" >> "$temp_file"
|
||||
echo "$PAM_TID_LINE" >>"$temp_file"
|
||||
sudo mv "$temp_file" "$PAM_SUDO_LOCAL_FILE"
|
||||
sudo chmod 444 "$PAM_SUDO_LOCAL_FILE"
|
||||
sudo chown root:wheel "$PAM_SUDO_LOCAL_FILE"
|
||||
@@ -137,7 +137,7 @@ enable_touchid() {
|
||||
# If we migrated from legacy, clean it up now
|
||||
if $is_legacy_configured; then
|
||||
temp_file=$(create_temp_file)
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" > "$temp_file"
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" >"$temp_file"
|
||||
sudo mv "$temp_file" "$PAM_SUDO_FILE"
|
||||
log_success "Touch ID migrated to sudo_local"
|
||||
else
|
||||
@@ -160,7 +160,7 @@ enable_touchid() {
|
||||
|
||||
# Create backup only if it doesn't exist to preserve original state
|
||||
if [[ ! -f "${PAM_SUDO_FILE}.mole-backup" ]]; then
|
||||
if ! sudo cp "$PAM_SUDO_FILE" "${PAM_SUDO_FILE}.mole-backup" 2> /dev/null; then
|
||||
if ! sudo cp "$PAM_SUDO_FILE" "${PAM_SUDO_FILE}.mole-backup" 2>/dev/null; then
|
||||
log_error "Failed to create backup"
|
||||
return 1
|
||||
fi
|
||||
@@ -178,7 +178,7 @@ enable_touchid() {
|
||||
inserted = 1
|
||||
}
|
||||
{ print }
|
||||
' "$PAM_SUDO_FILE" > "$temp_file"
|
||||
' "$PAM_SUDO_FILE" >"$temp_file"
|
||||
|
||||
# Verify content change
|
||||
if cmp -s "$PAM_SUDO_FILE" "$temp_file"; then
|
||||
@@ -187,7 +187,7 @@ enable_touchid() {
|
||||
fi
|
||||
|
||||
# Apply the changes
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2> /dev/null; then
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2>/dev/null; then
|
||||
log_success "Touch ID enabled, try: sudo ls"
|
||||
return 0
|
||||
else
|
||||
@@ -210,13 +210,13 @@ disable_touchid() {
|
||||
if [[ -f "$PAM_SUDO_LOCAL_FILE" ]] && grep -q "pam_tid.so" "$PAM_SUDO_LOCAL_FILE"; then
|
||||
# Remove from sudo_local
|
||||
temp_file=$(create_temp_file)
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_LOCAL_FILE" > "$temp_file"
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_LOCAL_FILE" >"$temp_file"
|
||||
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_LOCAL_FILE" 2> /dev/null; then
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_LOCAL_FILE" 2>/dev/null; then
|
||||
# Since we modified sudo_local, we should also check if it's in sudo file (legacy cleanup)
|
||||
if grep -q "pam_tid.so" "$PAM_SUDO_FILE"; then
|
||||
temp_file=$(create_temp_file)
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" > "$temp_file"
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" >"$temp_file"
|
||||
sudo mv "$temp_file" "$PAM_SUDO_FILE"
|
||||
fi
|
||||
echo -e "${GREEN}${ICON_SUCCESS} Touch ID disabled, removed from sudo_local${NC}"
|
||||
@@ -232,7 +232,7 @@ disable_touchid() {
|
||||
if grep -q "pam_tid.so" "$PAM_SUDO_FILE"; then
|
||||
# Create backup only if it doesn't exist
|
||||
if [[ ! -f "${PAM_SUDO_FILE}.mole-backup" ]]; then
|
||||
if ! sudo cp "$PAM_SUDO_FILE" "${PAM_SUDO_FILE}.mole-backup" 2> /dev/null; then
|
||||
if ! sudo cp "$PAM_SUDO_FILE" "${PAM_SUDO_FILE}.mole-backup" 2>/dev/null; then
|
||||
log_error "Failed to create backup"
|
||||
return 1
|
||||
fi
|
||||
@@ -240,9 +240,9 @@ disable_touchid() {
|
||||
|
||||
# Remove pam_tid.so line
|
||||
temp_file=$(create_temp_file)
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" > "$temp_file"
|
||||
grep -v "pam_tid.so" "$PAM_SUDO_FILE" >"$temp_file"
|
||||
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2> /dev/null; then
|
||||
if sudo mv "$temp_file" "$PAM_SUDO_FILE" 2>/dev/null; then
|
||||
echo -e "${GREEN}${ICON_SUCCESS} Touch ID disabled${NC}"
|
||||
echo ""
|
||||
return 0
|
||||
@@ -268,17 +268,17 @@ show_menu() {
|
||||
echo ""
|
||||
|
||||
case "$key" in
|
||||
$'\e') # ESC
|
||||
return 0
|
||||
;;
|
||||
"" | $'\n' | $'\r') # Enter
|
||||
printf "\r\033[K" # Clear the prompt line
|
||||
disable_touchid
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
log_error "Invalid key"
|
||||
;;
|
||||
$'\e') # ESC
|
||||
return 0
|
||||
;;
|
||||
"" | $'\n' | $'\r') # Enter
|
||||
printf "\r\033[K" # Clear the prompt line
|
||||
disable_touchid
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
log_error "Invalid key"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
echo -ne "${PURPLE}☛${NC} Press ${GREEN}Enter${NC} to enable, ${GRAY}Q${NC} to quit: "
|
||||
@@ -286,17 +286,17 @@ show_menu() {
|
||||
drain_pending_input # Clean up any escape sequence remnants
|
||||
|
||||
case "$key" in
|
||||
$'\e') # ESC
|
||||
return 0
|
||||
;;
|
||||
"" | $'\n' | $'\r') # Enter
|
||||
printf "\r\033[K" # Clear the prompt line
|
||||
enable_touchid
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
log_error "Invalid key"
|
||||
;;
|
||||
$'\e') # ESC
|
||||
return 0
|
||||
;;
|
||||
"" | $'\n' | $'\r') # Enter
|
||||
printf "\r\033[K" # Clear the prompt line
|
||||
enable_touchid
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
log_error "Invalid key"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
@@ -306,38 +306,25 @@ main() {
|
||||
local command="${1:-}"
|
||||
|
||||
case "$command" in
|
||||
"--help" | "-h")
|
||||
echo "Usage: mo touchid [COMMAND]"
|
||||
echo ""
|
||||
echo "Configure Touch ID for sudo authentication."
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " enable Enable Touch ID for sudo"
|
||||
echo " disable Disable Touch ID for sudo"
|
||||
echo " status Show current Touch ID status"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "If no command is provided, an interactive menu is shown."
|
||||
exit 0
|
||||
;;
|
||||
enable)
|
||||
enable_touchid
|
||||
;;
|
||||
disable)
|
||||
disable_touchid
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
"")
|
||||
show_menu
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown command: $command"
|
||||
exit 1
|
||||
;;
|
||||
"--help" | "-h")
|
||||
show_touchid_help
|
||||
;;
|
||||
enable)
|
||||
enable_touchid
|
||||
;;
|
||||
disable)
|
||||
disable_touchid
|
||||
;;
|
||||
status)
|
||||
show_status
|
||||
;;
|
||||
"")
|
||||
show_menu
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown command: $command"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
180
bin/uninstall.sh
180
bin/uninstall.sh
@@ -79,17 +79,17 @@ uninstall_resolve_display_name() {
|
||||
if [[ -f "$app_path/Contents/Info.plist" ]]; then
|
||||
local md_display_name
|
||||
if [[ -n "$MOLE_UNINSTALL_USER_LC_ALL" ]]; then
|
||||
md_display_name=$(run_with_timeout 0.04 env LC_ALL="$MOLE_UNINSTALL_USER_LC_ALL" LANG="$MOLE_UNINSTALL_USER_LANG" mdls -name kMDItemDisplayName -raw "$app_path" 2> /dev/null || echo "")
|
||||
md_display_name=$(run_with_timeout 0.04 env LC_ALL="$MOLE_UNINSTALL_USER_LC_ALL" LANG="$MOLE_UNINSTALL_USER_LANG" mdls -name kMDItemDisplayName -raw "$app_path" 2>/dev/null || echo "")
|
||||
elif [[ -n "$MOLE_UNINSTALL_USER_LANG" ]]; then
|
||||
md_display_name=$(run_with_timeout 0.04 env LANG="$MOLE_UNINSTALL_USER_LANG" mdls -name kMDItemDisplayName -raw "$app_path" 2> /dev/null || echo "")
|
||||
md_display_name=$(run_with_timeout 0.04 env LANG="$MOLE_UNINSTALL_USER_LANG" mdls -name kMDItemDisplayName -raw "$app_path" 2>/dev/null || echo "")
|
||||
else
|
||||
md_display_name=$(run_with_timeout 0.04 mdls -name kMDItemDisplayName -raw "$app_path" 2> /dev/null || echo "")
|
||||
md_display_name=$(run_with_timeout 0.04 mdls -name kMDItemDisplayName -raw "$app_path" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
local bundle_display_name
|
||||
bundle_display_name=$(plutil -extract CFBundleDisplayName raw "$app_path/Contents/Info.plist" 2> /dev/null || echo "")
|
||||
bundle_display_name=$(plutil -extract CFBundleDisplayName raw "$app_path/Contents/Info.plist" 2>/dev/null || echo "")
|
||||
local bundle_name
|
||||
bundle_name=$(plutil -extract CFBundleName raw "$app_path/Contents/Info.plist" 2> /dev/null || echo "")
|
||||
bundle_name=$(plutil -extract CFBundleName raw "$app_path/Contents/Info.plist" 2>/dev/null || echo "")
|
||||
|
||||
if [[ "$md_display_name" == /* ]]; then
|
||||
md_display_name=""
|
||||
@@ -125,7 +125,7 @@ uninstall_acquire_metadata_lock() {
|
||||
local lock_dir="$1"
|
||||
local attempts=0
|
||||
|
||||
while ! mkdir "$lock_dir" 2> /dev/null; do
|
||||
while ! mkdir "$lock_dir" 2>/dev/null; do
|
||||
((attempts++))
|
||||
if [[ $attempts -ge 40 ]]; then
|
||||
return 1
|
||||
@@ -140,12 +140,12 @@ uninstall_acquire_metadata_lock() {
|
||||
local lock_age
|
||||
lock_age=$(($(get_epoch_seconds) - lock_mtime))
|
||||
if [[ "$lock_age" =~ ^-?[0-9]+$ && $lock_age -gt 300 ]]; then
|
||||
rmdir "$lock_dir" 2> /dev/null || true
|
||||
rmdir "$lock_dir" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 0.1 2> /dev/null || sleep 1
|
||||
sleep 0.1 2>/dev/null || sleep 1
|
||||
done
|
||||
|
||||
return 0
|
||||
@@ -153,7 +153,7 @@ uninstall_acquire_metadata_lock() {
|
||||
|
||||
uninstall_release_metadata_lock() {
|
||||
local lock_dir="$1"
|
||||
[[ -d "$lock_dir" ]] && rmdir "$lock_dir" 2> /dev/null || true
|
||||
[[ -d "$lock_dir" ]] && rmdir "$lock_dir" 2>/dev/null || true
|
||||
}
|
||||
|
||||
uninstall_collect_inline_metadata() {
|
||||
@@ -167,9 +167,9 @@ uninstall_collect_inline_metadata() {
|
||||
|
||||
local last_used_epoch=0
|
||||
local metadata_date
|
||||
metadata_date=$(run_with_timeout "$MOLE_UNINSTALL_INLINE_MDLS_TIMEOUT_SEC" mdls -name kMDItemLastUsedDate -raw "$app_path" 2> /dev/null || echo "")
|
||||
metadata_date=$(run_with_timeout "$MOLE_UNINSTALL_INLINE_MDLS_TIMEOUT_SEC" mdls -name kMDItemLastUsedDate -raw "$app_path" 2>/dev/null || echo "")
|
||||
if [[ "$metadata_date" != "(null)" && -n "$metadata_date" ]]; then
|
||||
last_used_epoch=$(date -j -f "%Y-%m-%d %H:%M:%S %z" "$metadata_date" "+%s" 2> /dev/null || echo "0")
|
||||
last_used_epoch=$(date -j -f "%Y-%m-%d %H:%M:%S %z" "$metadata_date" "+%s" 2>/dev/null || echo "0")
|
||||
fi
|
||||
|
||||
# Fallback to app mtime so first scan does not show "...".
|
||||
@@ -187,7 +187,7 @@ uninstall_collect_inline_metadata() {
|
||||
start_uninstall_metadata_refresh() {
|
||||
local refresh_file="$1"
|
||||
[[ ! -s "$refresh_file" ]] && {
|
||||
rm -f "$refresh_file" 2> /dev/null || true
|
||||
rm -f "$refresh_file" 2>/dev/null || true
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -195,15 +195,15 @@ start_uninstall_metadata_refresh() {
|
||||
_refresh_debug() {
|
||||
if [[ "${MO_DEBUG:-}" == "1" ]]; then
|
||||
local ts
|
||||
ts=$(date "+%Y-%m-%d %H:%M:%S" 2> /dev/null || echo "?")
|
||||
echo "[$ts] DEBUG: [metadata-refresh] $*" >> "${HOME}/.config/mole/mole_debug_session.log" 2> /dev/null || true
|
||||
ts=$(date "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "?")
|
||||
echo "[$ts] DEBUG: [metadata-refresh] $*" >>"${HOME}/.config/mole/mole_debug_session.log" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_user_dir "$MOLE_UNINSTALL_META_CACHE_DIR"
|
||||
ensure_user_file "$MOLE_UNINSTALL_META_CACHE_FILE"
|
||||
if [[ ! -r "$MOLE_UNINSTALL_META_CACHE_FILE" ]]; then
|
||||
if ! : > "$MOLE_UNINSTALL_META_CACHE_FILE" 2> /dev/null; then
|
||||
if ! : >"$MOLE_UNINSTALL_META_CACHE_FILE" 2>/dev/null; then
|
||||
_refresh_debug "Cannot create cache file, aborting"
|
||||
exit 0
|
||||
fi
|
||||
@@ -214,7 +214,7 @@ start_uninstall_metadata_refresh() {
|
||||
fi
|
||||
|
||||
local updates_file
|
||||
updates_file=$(mktemp 2> /dev/null) || {
|
||||
updates_file=$(mktemp 2>/dev/null) || {
|
||||
_refresh_debug "mktemp failed, aborting"
|
||||
exit 0
|
||||
}
|
||||
@@ -238,9 +238,9 @@ start_uninstall_metadata_refresh() {
|
||||
(
|
||||
local last_used_epoch=0
|
||||
local metadata_date
|
||||
metadata_date=$(run_with_timeout 0.2 mdls -name kMDItemLastUsedDate -raw "$app_path" 2> /dev/null || echo "")
|
||||
metadata_date=$(run_with_timeout 0.2 mdls -name kMDItemLastUsedDate -raw "$app_path" 2>/dev/null || echo "")
|
||||
if [[ "$metadata_date" != "(null)" && -n "$metadata_date" ]]; then
|
||||
last_used_epoch=$(date -j -f "%Y-%m-%d %H:%M:%S %z" "$metadata_date" "+%s" 2> /dev/null || echo "0")
|
||||
last_used_epoch=$(date -j -f "%Y-%m-%d %H:%M:%S %z" "$metadata_date" "+%s" 2>/dev/null || echo "0")
|
||||
fi
|
||||
|
||||
if [[ ! "$last_used_epoch" =~ ^[0-9]+$ || $last_used_epoch -le 0 ]]; then
|
||||
@@ -251,25 +251,25 @@ start_uninstall_metadata_refresh() {
|
||||
size_kb=$(get_path_size_kb "$app_path")
|
||||
[[ "$size_kb" =~ ^[0-9]+$ ]] || size_kb=0
|
||||
|
||||
printf "%s|%s|%s|%s|%s|%s|%s\n" "$app_path" "${app_mtime:-0}" "$size_kb" "${last_used_epoch:-0}" "$now_epoch" "$bundle_id" "$display_name" > "$worker_output"
|
||||
printf "%s|%s|%s|%s|%s|%s|%s\n" "$app_path" "${app_mtime:-0}" "$size_kb" "${last_used_epoch:-0}" "$now_epoch" "$bundle_id" "$display_name" >"$worker_output"
|
||||
) &
|
||||
worker_pids+=($!)
|
||||
|
||||
if ((${#worker_pids[@]} >= max_parallel)); then
|
||||
wait "${worker_pids[0]}" 2> /dev/null || true
|
||||
wait "${worker_pids[0]}" 2>/dev/null || true
|
||||
worker_pids=("${worker_pids[@]:1}")
|
||||
fi
|
||||
done < "$refresh_file"
|
||||
done <"$refresh_file"
|
||||
|
||||
local worker_pid
|
||||
for worker_pid in "${worker_pids[@]}"; do
|
||||
wait "$worker_pid" 2> /dev/null || true
|
||||
wait "$worker_pid" 2>/dev/null || true
|
||||
done
|
||||
|
||||
local worker_output
|
||||
for worker_output in "${updates_file}".*; do
|
||||
[[ -f "$worker_output" ]] || continue
|
||||
cat "$worker_output" >> "$updates_file"
|
||||
cat "$worker_output" >>"$updates_file"
|
||||
rm -f "$worker_output"
|
||||
done
|
||||
|
||||
@@ -285,7 +285,7 @@ start_uninstall_metadata_refresh() {
|
||||
fi
|
||||
|
||||
local merged_file
|
||||
merged_file=$(mktemp 2> /dev/null) || {
|
||||
merged_file=$(mktemp 2>/dev/null) || {
|
||||
_refresh_debug "mktemp for merge failed, aborting"
|
||||
uninstall_release_metadata_lock "$MOLE_UNINSTALL_META_CACHE_LOCK"
|
||||
rm -f "$updates_file"
|
||||
@@ -300,17 +300,17 @@ start_uninstall_metadata_refresh() {
|
||||
print updates[path]
|
||||
}
|
||||
}
|
||||
' "$updates_file" "$MOLE_UNINSTALL_META_CACHE_FILE" > "$merged_file"
|
||||
' "$updates_file" "$MOLE_UNINSTALL_META_CACHE_FILE" >"$merged_file"
|
||||
|
||||
mv "$merged_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2> /dev/null || {
|
||||
cp "$merged_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2> /dev/null || true
|
||||
mv "$merged_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2>/dev/null || {
|
||||
cp "$merged_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2>/dev/null || true
|
||||
rm -f "$merged_file"
|
||||
}
|
||||
|
||||
uninstall_release_metadata_lock "$MOLE_UNINSTALL_META_CACHE_LOCK"
|
||||
rm -f "$updates_file"
|
||||
rm -f "$refresh_file" 2> /dev/null || true
|
||||
) > /dev/null 2>&1 &
|
||||
rm -f "$refresh_file" 2>/dev/null || true
|
||||
) >/dev/null 2>&1 &
|
||||
|
||||
}
|
||||
|
||||
@@ -322,9 +322,9 @@ scan_applications() {
|
||||
merged_file="${temp_file}.merged"
|
||||
refresh_file="${temp_file}.refresh"
|
||||
cache_snapshot_file="${temp_file}.cache"
|
||||
: > "$scan_raw_file"
|
||||
: > "$refresh_file"
|
||||
: > "$cache_snapshot_file"
|
||||
: >"$scan_raw_file"
|
||||
: >"$refresh_file"
|
||||
: >"$cache_snapshot_file"
|
||||
|
||||
ensure_user_dir "$MOLE_UNINSTALL_META_CACHE_DIR"
|
||||
ensure_user_file "$MOLE_UNINSTALL_META_CACHE_FILE"
|
||||
@@ -332,7 +332,7 @@ scan_applications() {
|
||||
local cache_source_is_temp=false
|
||||
if [[ ! -r "$cache_source" ]]; then
|
||||
cache_source=$(create_temp_file)
|
||||
: > "$cache_source"
|
||||
: >"$cache_source"
|
||||
cache_source_is_temp=true
|
||||
fi
|
||||
|
||||
@@ -348,7 +348,7 @@ scan_applications() {
|
||||
cache_mtimes+=("${cache_mtime:-0}")
|
||||
cache_bundle_ids+=("${cache_bundle:-}")
|
||||
cache_display_names+=("${cache_display:-}")
|
||||
done < "$cache_source"
|
||||
done <"$cache_source"
|
||||
|
||||
lookup_cached_identity() {
|
||||
local target_path="$1"
|
||||
@@ -385,13 +385,13 @@ scan_applications() {
|
||||
# shellcheck disable=SC2329 # Function invoked indirectly via trap
|
||||
trap_scan_cleanup() {
|
||||
if [[ -n "$spinner_pid" ]]; then
|
||||
kill -TERM "$spinner_pid" 2> /dev/null || true
|
||||
wait "$spinner_pid" 2> /dev/null || true
|
||||
kill -TERM "$spinner_pid" 2>/dev/null || true
|
||||
wait "$spinner_pid" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -f "$spinner_shown_file" ]]; then
|
||||
printf "\r\033[K" >&2
|
||||
fi
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file" "${temp_file}.sorted" "${temp_file}.progress" "$spinner_shown_file" 2> /dev/null || true
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file" "${temp_file}.sorted" "${temp_file}.progress" "$spinner_shown_file" 2>/dev/null || true
|
||||
exit 130
|
||||
}
|
||||
trap trap_scan_cleanup INT
|
||||
@@ -440,18 +440,18 @@ scan_applications() {
|
||||
|
||||
if [[ -L "$app_path" ]]; then
|
||||
local link_target
|
||||
link_target=$(readlink "$app_path" 2> /dev/null)
|
||||
link_target=$(readlink "$app_path" 2>/dev/null)
|
||||
if [[ -n "$link_target" ]]; then
|
||||
local resolved_target="$link_target"
|
||||
if [[ "$link_target" != /* ]]; then
|
||||
local link_dir
|
||||
link_dir=$(dirname "$app_path")
|
||||
resolved_target=$(cd "$link_dir" 2> /dev/null && cd "$(dirname "$link_target")" 2> /dev/null && pwd)/$(basename "$link_target") 2> /dev/null || echo ""
|
||||
resolved_target=$(cd "$link_dir" 2>/dev/null && cd "$(dirname "$link_target")" 2>/dev/null && pwd)/$(basename "$link_target") 2>/dev/null || echo ""
|
||||
fi
|
||||
case "$resolved_target" in
|
||||
/System/* | /usr/bin/* | /usr/lib/* | /bin/* | /sbin/* | /private/etc/*)
|
||||
continue
|
||||
;;
|
||||
/System/* | /usr/bin/* | /usr/lib/* | /bin/* | /sbin/* | /private/etc/*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
@@ -461,16 +461,16 @@ scan_applications() {
|
||||
|
||||
local cached_identity cached_bundle_id cached_display_name
|
||||
cached_identity=$(lookup_cached_identity "$app_path" "$app_mtime")
|
||||
IFS='|' read -r cached_bundle_id cached_display_name <<< "$cached_identity"
|
||||
IFS='|' read -r cached_bundle_id cached_display_name <<<"$cached_identity"
|
||||
|
||||
# Store tuple for pass 2 (bundle + display resolution, then cache merge).
|
||||
app_data_tuples+=("${app_path}|${app_name}|${app_mtime}|${cached_bundle_id}|${cached_display_name}")
|
||||
done < <(command find "$app_dir" -name "*.app" -maxdepth 3 -print0 2> /dev/null)
|
||||
done < <(command find "$app_dir" -name "*.app" -maxdepth 3 -print0 2>/dev/null)
|
||||
done
|
||||
|
||||
if [[ ${#app_data_tuples[@]} -eq 0 ]]; then
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file" "${temp_file}.sorted" "${temp_file}.progress" "$spinner_shown_file" 2> /dev/null || true
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2> /dev/null || true
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file" "${temp_file}.sorted" "${temp_file}.progress" "$spinner_shown_file" 2>/dev/null || true
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2>/dev/null || true
|
||||
restore_scan_int_trap
|
||||
printf "\r\033[K" >&2
|
||||
echo "No applications found to uninstall." >&2
|
||||
@@ -492,13 +492,13 @@ scan_applications() {
|
||||
local app_data_tuple="$1"
|
||||
local output_file="$2"
|
||||
|
||||
IFS='|' read -r app_path app_name app_mtime cached_bundle_id cached_display_name <<< "$app_data_tuple"
|
||||
IFS='|' read -r app_path app_name app_mtime cached_bundle_id cached_display_name <<<"$app_data_tuple"
|
||||
|
||||
local bundle_id="${cached_bundle_id:-}"
|
||||
if [[ -z "$bundle_id" ]]; then
|
||||
bundle_id="unknown"
|
||||
if [[ -f "$app_path/Contents/Info.plist" ]]; then
|
||||
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2> /dev/null || echo "unknown")
|
||||
bundle_id=$(defaults read "$app_path/Contents/Info.plist" CFBundleIdentifier 2>/dev/null || echo "unknown")
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -515,27 +515,27 @@ scan_applications() {
|
||||
display_name="${display_name//|/-}"
|
||||
display_name="${display_name//[$'\t\r\n']/}"
|
||||
|
||||
echo "${app_path}|${display_name}|${bundle_id}|${app_mtime}" >> "$output_file"
|
||||
echo "${app_path}|${display_name}|${bundle_id}|${app_mtime}" >>"$output_file"
|
||||
}
|
||||
|
||||
local progress_file="${temp_file}.progress"
|
||||
echo "0" > "$progress_file"
|
||||
echo "0" >"$progress_file"
|
||||
|
||||
(
|
||||
# shellcheck disable=SC2329 # Function invoked indirectly via trap
|
||||
cleanup_spinner() { exit 0; }
|
||||
trap cleanup_spinner TERM INT EXIT
|
||||
sleep "$MOLE_UNINSTALL_SCAN_SPINNER_DELAY_SEC" 2> /dev/null || sleep 1
|
||||
sleep "$MOLE_UNINSTALL_SCAN_SPINNER_DELAY_SEC" 2>/dev/null || sleep 1
|
||||
[[ -f "$progress_file" ]] || exit 0
|
||||
local spinner_chars="|/-\\"
|
||||
local i=0
|
||||
: > "$spinner_shown_file"
|
||||
: >"$spinner_shown_file"
|
||||
while true; do
|
||||
local completed=$(cat "$progress_file" 2> /dev/null || echo 0)
|
||||
local completed=$(cat "$progress_file" 2>/dev/null || echo 0)
|
||||
local c="${spinner_chars:$((i % 4)):1}"
|
||||
printf "\r\033[K%s Scanning applications... %d/%d" "$c" "$completed" "$total_apps" >&2
|
||||
((i++))
|
||||
sleep 0.1 2> /dev/null || sleep 1
|
||||
sleep 0.1 2>/dev/null || sleep 1
|
||||
done
|
||||
) &
|
||||
spinner_pid=$!
|
||||
@@ -544,21 +544,21 @@ scan_applications() {
|
||||
((app_count++))
|
||||
process_app_metadata "$app_data_tuple" "$scan_raw_file" &
|
||||
pids+=($!)
|
||||
echo "$app_count" > "$progress_file"
|
||||
echo "$app_count" >"$progress_file"
|
||||
|
||||
if ((${#pids[@]} >= max_parallel)); then
|
||||
wait "${pids[0]}" 2> /dev/null
|
||||
wait "${pids[0]}" 2>/dev/null
|
||||
pids=("${pids[@]:1}")
|
||||
fi
|
||||
done
|
||||
|
||||
for pid in "${pids[@]}"; do
|
||||
wait "$pid" 2> /dev/null
|
||||
wait "$pid" 2>/dev/null
|
||||
done
|
||||
|
||||
if [[ -n "$spinner_pid" ]]; then
|
||||
kill -TERM "$spinner_pid" 2> /dev/null || true
|
||||
wait "$spinner_pid" 2> /dev/null || true
|
||||
kill -TERM "$spinner_pid" 2>/dev/null || true
|
||||
wait "$spinner_pid" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -f "$spinner_shown_file" ]]; then
|
||||
echo -ne "\r\033[K" >&2
|
||||
@@ -567,8 +567,8 @@ scan_applications() {
|
||||
|
||||
if [[ ! -s "$scan_raw_file" ]]; then
|
||||
echo "No applications found to uninstall" >&2
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file" "${temp_file}.sorted" "${temp_file}.progress" "$spinner_shown_file" 2> /dev/null || true
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2> /dev/null || true
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file" "${temp_file}.sorted" "${temp_file}.progress" "$spinner_shown_file" 2>/dev/null || true
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2>/dev/null || true
|
||||
restore_scan_int_trap
|
||||
return 1
|
||||
fi
|
||||
@@ -590,9 +590,9 @@ scan_applications() {
|
||||
{
|
||||
print $0 "|" cache_mtime[$1] "|" cache_size[$1] "|" cache_epoch[$1] "|" cache_updated[$1] "|" cache_bundle[$1] "|" cache_display[$1]
|
||||
}
|
||||
' "$cache_source" "$scan_raw_file" > "$merged_file"
|
||||
' "$cache_source" "$scan_raw_file" >"$merged_file"
|
||||
if [[ ! -s "$merged_file" && -s "$scan_raw_file" ]]; then
|
||||
awk '{print $0 "||||||"}' "$scan_raw_file" > "$merged_file"
|
||||
awk '{print $0 "||||||"}' "$scan_raw_file" >"$merged_file"
|
||||
fi
|
||||
|
||||
local current_epoch
|
||||
@@ -644,7 +644,7 @@ scan_applications() {
|
||||
if [[ $inline_metadata_count -lt $MOLE_UNINSTALL_INLINE_METADATA_LIMIT ]]; then
|
||||
local inline_metadata inline_size_kb inline_epoch inline_updated_epoch
|
||||
inline_metadata=$(uninstall_collect_inline_metadata "$app_path" "${app_mtime:-0}" "$current_epoch")
|
||||
IFS='|' read -r inline_size_kb inline_epoch inline_updated_epoch <<< "$inline_metadata"
|
||||
IFS='|' read -r inline_size_kb inline_epoch inline_updated_epoch <<<"$inline_metadata"
|
||||
((inline_metadata_count++))
|
||||
|
||||
if [[ "$inline_size_kb" =~ ^[0-9]+$ && $inline_size_kb -gt 0 ]]; then
|
||||
@@ -659,36 +659,36 @@ scan_applications() {
|
||||
cached_updated_epoch="$inline_updated_epoch"
|
||||
fi
|
||||
fi
|
||||
printf "%s|%s|%s|%s\n" "$app_path" "${app_mtime:-0}" "$bundle_id" "$display_name" >> "$refresh_file"
|
||||
printf "%s|%s|%s|%s\n" "$app_path" "${app_mtime:-0}" "$bundle_id" "$display_name" >>"$refresh_file"
|
||||
fi
|
||||
|
||||
local persist_updated_epoch=0
|
||||
if [[ "$cached_updated_epoch" =~ ^[0-9]+$ && $cached_updated_epoch -gt 0 ]]; then
|
||||
persist_updated_epoch="$cached_updated_epoch"
|
||||
fi
|
||||
printf "%s|%s|%s|%s|%s|%s|%s\n" "$app_path" "${app_mtime:-0}" "${final_size_kb:-0}" "${final_epoch:-0}" "${persist_updated_epoch:-0}" "$bundle_id" "$display_name" >> "$cache_snapshot_file"
|
||||
printf "%s|%s|%s|%s|%s|%s|%s\n" "$app_path" "${app_mtime:-0}" "${final_size_kb:-0}" "${final_epoch:-0}" "${persist_updated_epoch:-0}" "$bundle_id" "$display_name" >>"$cache_snapshot_file"
|
||||
|
||||
echo "${final_epoch}|${app_path}|${display_name}|${bundle_id}|${final_size}|${final_last_used}|${final_size_kb}" >> "$temp_file"
|
||||
done < "$merged_file"
|
||||
echo "${final_epoch}|${app_path}|${display_name}|${bundle_id}|${final_size}|${final_last_used}|${final_size_kb}" >>"$temp_file"
|
||||
done <"$merged_file"
|
||||
|
||||
if [[ -s "$cache_snapshot_file" ]]; then
|
||||
if uninstall_acquire_metadata_lock "$MOLE_UNINSTALL_META_CACHE_LOCK"; then
|
||||
mv "$cache_snapshot_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2> /dev/null || {
|
||||
cp "$cache_snapshot_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2> /dev/null || true
|
||||
mv "$cache_snapshot_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2>/dev/null || {
|
||||
cp "$cache_snapshot_file" "$MOLE_UNINSTALL_META_CACHE_FILE" 2>/dev/null || true
|
||||
rm -f "$cache_snapshot_file"
|
||||
}
|
||||
uninstall_release_metadata_lock "$MOLE_UNINSTALL_META_CACHE_LOCK"
|
||||
fi
|
||||
fi
|
||||
|
||||
sort -t'|' -k1,1n "$temp_file" > "${temp_file}.sorted" || {
|
||||
sort -t'|' -k1,1n "$temp_file" >"${temp_file}.sorted" || {
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$refresh_file" "$cache_snapshot_file"
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2> /dev/null || true
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2>/dev/null || true
|
||||
restore_scan_int_trap
|
||||
return 1
|
||||
}
|
||||
rm -f "$temp_file" "$scan_raw_file" "$merged_file" "$cache_snapshot_file"
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2> /dev/null || true
|
||||
[[ $cache_source_is_temp == true ]] && rm -f "$cache_source" 2>/dev/null || true
|
||||
|
||||
[[ $total_apps -gt 50 ]] && printf "\r\033[K" >&2
|
||||
|
||||
@@ -720,7 +720,7 @@ load_applications() {
|
||||
|
||||
apps_data+=("$epoch|$app_path|$app_name|$bundle_id|$size|$last_used|${size_kb:-0}")
|
||||
selection_state+=(false)
|
||||
done < "$apps_file"
|
||||
done <"$apps_file"
|
||||
|
||||
if [[ ${#apps_data[@]} -eq 0 ]]; then
|
||||
log_warning "No applications available for uninstallation"
|
||||
@@ -737,8 +737,8 @@ cleanup() {
|
||||
unset MOLE_ALT_SCREEN_ACTIVE
|
||||
fi
|
||||
if [[ -n "${sudo_keepalive_pid:-}" ]]; then
|
||||
kill "$sudo_keepalive_pid" 2> /dev/null || true
|
||||
wait "$sudo_keepalive_pid" 2> /dev/null || true
|
||||
kill "$sudo_keepalive_pid" 2>/dev/null || true
|
||||
wait "$sudo_keepalive_pid" 2>/dev/null || true
|
||||
sudo_keepalive_pid=""
|
||||
fi
|
||||
# Log session end
|
||||
@@ -757,19 +757,13 @@ main() {
|
||||
# Global flags
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
"--help" | "-h")
|
||||
echo "Usage: mo uninstall [OPTIONS]"
|
||||
echo ""
|
||||
echo "Interactively remove applications and their leftover files."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
"--help" | "-h")
|
||||
show_uninstall_help
|
||||
exit 0
|
||||
;;
|
||||
"--debug")
|
||||
export MO_DEBUG=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -821,7 +815,7 @@ main() {
|
||||
local max_size_width=0
|
||||
local max_last_width=0
|
||||
for selected_app in "${selected_apps[@]}"; do
|
||||
IFS='|' read -r _ _ app_name _ size last_used _ <<< "$selected_app"
|
||||
IFS='|' read -r _ _ app_name _ size last_used _ <<<"$selected_app"
|
||||
local name_width=$(get_display_width "$app_name")
|
||||
[[ $name_width -gt $max_name_display_width ]] && max_name_display_width=$name_width
|
||||
local size_display="$size"
|
||||
@@ -835,7 +829,7 @@ main() {
|
||||
((max_last_width < 5)) && max_last_width=5
|
||||
((max_name_display_width < 16)) && max_name_display_width=16
|
||||
|
||||
local term_width=$(tput cols 2> /dev/null || echo 100)
|
||||
local term_width=$(tput cols 2>/dev/null || echo 100)
|
||||
local available_for_name=$((term_width - 17 - max_size_width - max_last_width))
|
||||
|
||||
local min_name_width=24
|
||||
@@ -855,7 +849,7 @@ main() {
|
||||
max_name_display_width=0
|
||||
|
||||
for selected_app in "${selected_apps[@]}"; do
|
||||
IFS='|' read -r epoch app_path app_name bundle_id size last_used size_kb <<< "$selected_app"
|
||||
IFS='|' read -r epoch app_path app_name bundle_id size last_used size_kb <<<"$selected_app"
|
||||
|
||||
local display_name
|
||||
display_name=$(truncate_by_display_width "$app_name" "$name_trunc_limit")
|
||||
@@ -880,7 +874,7 @@ main() {
|
||||
|
||||
local index=1
|
||||
for row in "${summary_rows[@]}"; do
|
||||
IFS='|' read -r name_cell size_cell last_cell <<< "$row"
|
||||
IFS='|' read -r name_cell size_cell last_cell <<<"$row"
|
||||
local name_display_width
|
||||
name_display_width=$(get_display_width "$name_cell")
|
||||
local name_char_count=${#name_cell}
|
||||
|
||||
@@ -18,6 +18,7 @@ source "$_MOLE_CORE_DIR/log.sh"
|
||||
|
||||
source "$_MOLE_CORE_DIR/timeout.sh"
|
||||
source "$_MOLE_CORE_DIR/file_ops.sh"
|
||||
source "$_MOLE_CORE_DIR/help.sh"
|
||||
source "$_MOLE_CORE_DIR/ui.sh"
|
||||
source "$_MOLE_CORE_DIR/app_protection.sh"
|
||||
|
||||
|
||||
61
lib/core/help.sh
Normal file
61
lib/core/help.sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
show_clean_help() {
|
||||
echo "Usage: mo clean [OPTIONS]"
|
||||
echo ""
|
||||
echo "Clean up disk space by removing caches, logs, and temporary files."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --dry-run, -n Preview cleanup without making changes"
|
||||
echo " --whitelist Manage protected paths"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
}
|
||||
|
||||
show_installer_help() {
|
||||
echo "Usage: mo installer [OPTIONS]"
|
||||
echo ""
|
||||
echo "Find and remove installer files (.dmg, .pkg, .iso, .xip, .zip)."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
}
|
||||
|
||||
show_optimize_help() {
|
||||
echo "Usage: mo optimize [OPTIONS]"
|
||||
echo ""
|
||||
echo "Check and maintain system health, apply optimizations."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --dry-run Preview optimization without making changes"
|
||||
echo " --whitelist Manage protected items"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
}
|
||||
|
||||
show_touchid_help() {
|
||||
echo "Usage: mo touchid [COMMAND]"
|
||||
echo ""
|
||||
echo "Configure Touch ID for sudo authentication."
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " enable Enable Touch ID for sudo"
|
||||
echo " disable Disable Touch ID for sudo"
|
||||
echo " status Show current Touch ID status"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "If no command is provided, an interactive menu is shown."
|
||||
}
|
||||
|
||||
show_uninstall_help() {
|
||||
echo "Usage: mo uninstall [OPTIONS]"
|
||||
echo ""
|
||||
echo "Interactively remove applications and their leftover files."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --debug Show detailed operation logs"
|
||||
echo " -h, --help Show this help message"
|
||||
}
|
||||
Reference in New Issue
Block a user