fix and updates

This commit is contained in:
2026-05-29 11:32:58 +08:00
parent 7c2f21d8ec
commit bed888c3c3
+56 -50
View File
@@ -16,23 +16,6 @@ MAGENTA=$(tput_cmd setaf 5)
BLUE=$(tput_cmd setaf 4) BLUE=$(tput_cmd setaf 4)
ERASE_LINE=$(tput_cmd el || printf '\033[K') ERASE_LINE=$(tput_cmd el || printf '\033[K')
# map a filename to a display colour based on extension
file_colour() {
_fc_name="$1"
_fc_ext="${_fc_name##*.}"
_fc_ext=$(printf '%s' "$_fc_ext" | tr '[:upper:]' '[:lower:]')
# executable?
[ -x "${CWD}/${_fc_name}" ] && { printf '%s' "${GREEN}${BOLD}" ; return; }
# node device?
[ -c "${CWD}/${_fc_name}" ] && { printf '%s' "${MAGENTA}${BOLD}"; return; }
# block device?
[ -b "${CWD}/${_fc_name}" ] && { printf '%s' "${MAGENTA}${BOLD}"; return; }
# readable?
[ -r "${CWD}/${_fc_name}" ] || { printf '%s' "${RED}${BOLD}" ; return; }
# writable?
[ -w "${CWD}/${_fc_name}" ] || { printf '%s' "${RED}${BOLD}" ; return; }
}
goto() { printf '\033[%d;%dH' "$1" "$2"; } goto() { printf '\033[%d;%dH' "$1" "$2"; }
hide_cursor() { printf '\033[?25l'; } hide_cursor() { printf '\033[?25l'; }
show_cursor() { printf '\033[?25h'; } show_cursor() { printf '\033[?25h'; }
@@ -174,8 +157,8 @@ $_line"; fi ;;
# sort files using ls into a tmp file # sort files using ls into a tmp file
if [ -n "$_files" ]; then if [ -n "$_files" ]; then
case "$SORT_MODE" in case "$SORT_MODE" in
size) ls -1Sp "$CWD" 2>/dev/null | grep -v '/' > /tmp/_fm_sorted ;; size) ls -1Sp "$CWD" 2>/dev/null | grep -v '/' > /tmp/_fm_sorted_$$ ;;
date) ls -1tp "$CWD" 2>/dev/null | grep -v '/' > /tmp/_fm_sorted ;; date) ls -1tp "$CWD" 2>/dev/null | grep -v '/' > /tmp/_fm_sorted_$$ ;;
esac esac
# only keep files that were already in our list (respects hidden filter) # only keep files that were already in our list (respects hidden filter)
_sorted="" _sorted=""
@@ -190,7 +173,7 @@ ${_n}
else _sorted="$_sorted else _sorted="$_sorted
$_n"; fi ;; $_n"; fi ;;
esac esac
done < /tmp/_fm_sorted done < /tmp/_fm_sorted_$$
_files="$_sorted" _files="$_sorted"
fi fi
@@ -265,16 +248,16 @@ $_line"; fi
done done
fi fi
# write to tmp file for O(1) line access in get_entry # write to tmp file for O(1) line access in get_entry
printf '%s\n' "$ENTRIES" > /tmp/_sfm_list printf '%s\n' "$ENTRIES" > /tmp/_sfm_list_$$
} }
get_entry() { get_entry() {
sed -n "$(($1 + 1))p" /tmp/_sfm_list sed -n "$(($1 + 1))p" /tmp/_sfm_list_$$
} }
# find index of entry by name (0-based), returns -1 if not found # find index of entry by name (0-based), returns -1 if not found
find_entry() { find_entry() {
_fe_n=$(grep -n "^${1}$" /tmp/_sfm_list 2>/dev/null | head -1 | cut -d: -f1) _fe_n=$(grep -nFx "$1" /tmp/_sfm_list_$$ 2>/dev/null | head -1 | cut -d: -f1)
if [ -n "$_fe_n" ]; then if [ -n "$_fe_n" ]; then
printf '%d' $((_fe_n - 1)) printf '%d' $((_fe_n - 1))
else else
@@ -284,12 +267,7 @@ find_entry() {
# is entry name in SELECTED list? # is entry name in SELECTED list?
is_selected() { is_selected() {
case " printf '%s\n' "$SELECTED" | grep -Fxq "$1"
${SELECTED}
" in *"
$1
"*) return 0 ;; esac
return 1
} }
count_selected() { count_selected() {
@@ -1033,7 +1011,7 @@ do_find() {
_query="$READ_LINE" _query="$READ_LINE"
# run find and collect results into a tmp file # run find and collect results into a tmp file
_tmp=/tmp/_sfm_find _tmp=/tmp/_sfm_find_$$
find "$CWD" -name "*${_query}*" 2>/dev/null | sort > "$_tmp" find "$CWD" -name "*${_query}*" 2>/dev/null | sort > "$_tmp"
_total=$(wc -l < "$_tmp" | tr -d '[:space:]') _total=$(wc -l < "$_tmp" | tr -d '[:space:]')
@@ -1258,7 +1236,8 @@ do_delete() {
[ "$_next" = "$_rest" ] && _next="" [ "$_next" = "$_rest" ] && _next=""
_rest="$_next" _rest="$_next"
[ -z "$_line" ] && continue [ -z "$_line" ] && continue
_t="${CWD}/${_line%/}" _name="${_line%@}"; _name="${_name%/}"
_t="${CWD}/${_name}"
if [ -d "$_t" ]; then rm -rf "$_t"; else rm -f "$_t"; fi if [ -d "$_t" ]; then rm -rf "$_t"; else rm -f "$_t"; fi
done done
SELECTED="" SELECTED=""
@@ -1271,7 +1250,8 @@ do_delete() {
else else
# --- single delete --- # --- single delete ---
entry=$(get_entry "$SEL") entry=$(get_entry "$SEL")
target="${CWD}/${entry%/}" _name="${entry%@}"; _name="${_name%/}"
target="${CWD}/${_name}"
restore_term; show_cursor restore_term; show_cursor
goto "$(term_rows)" 1 goto "$(term_rows)" 1
printf '%s%s Delete "%s"? [y/N] %s' "${ERASE_LINE}" "${RED}${BOLD}" "$entry" "${RESET}" printf '%s%s Delete "%s"? [y/N] %s' "${ERASE_LINE}" "${RED}${BOLD}" "$entry" "${RESET}"
@@ -1311,7 +1291,8 @@ do_rename() {
goto "$(term_rows)" 1 goto "$(term_rows)" 1
printf '%s%s Rename "%s" to (esc=cancel): %s' "${ERASE_LINE}" "${YELLOW}${BOLD}" "$entry" "${RESET}" printf '%s%s Rename "%s" to (esc=cancel): %s' "${ERASE_LINE}" "${YELLOW}${BOLD}" "$entry" "${RESET}"
if read_line && [ -n "$READ_LINE" ] && [ "$READ_LINE" != "$entry" ]; then if read_line && [ -n "$READ_LINE" ] && [ "$READ_LINE" != "$entry" ]; then
mv "${CWD}/${entry%/}" "${CWD}/${READ_LINE}" _name="${entry%@}"; _name="${_name%/}"
mv "${CWD}/${_name}" "${CWD}/${READ_LINE}"
load_entries load_entries
else else
NEED_FULL_REDRAW=1; draw NEED_FULL_REDRAW=1; draw
@@ -1358,7 +1339,8 @@ do_chmod_nox() {
do_info() { do_info() {
entry=$(get_entry "$SEL") entry=$(get_entry "$SEL")
[ -z "$entry" ] && return [ -z "$entry" ] && return
target="${CWD}/${entry%/}" _name="${entry%@}"; _name="${_name%/}"
target="${CWD}/${_name}"
# permissions + type # permissions + type
_perm=$(ls -ld "$target" 2>/dev/null | awk '{print $1}') _perm=$(ls -ld "$target" 2>/dev/null | awk '{print $1}')
# size (human readable via du, fallback to ls) # size (human readable via du, fallback to ls)
@@ -1396,9 +1378,10 @@ do_sort() {
do_trash() { do_trash() {
entry=$(get_entry "$SEL") entry=$(get_entry "$SEL")
[ -z "$entry" ] && return [ -z "$entry" ] && return
target="${CWD}/${entry%/}" _name="${entry%@}"; _name="${_name%/}"
target="${CWD}/${_name}"
_ts=$(date '+%Y%m%d_%H%M%S' 2>/dev/null || date '+%s') _ts=$(date '+%Y%m%d_%H%M%S' 2>/dev/null || date '+%s')
_dest="${TRASH_DIR}/${_ts}_${entry%/}" _dest="${TRASH_DIR}/${_ts}_${_name}"
if mv "$target" "$_dest"; then if mv "$target" "$_dest"; then
INFO_MSG="trashed: ${entry}" INFO_MSG="trashed: ${entry}"
[ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2)) [ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2))
@@ -1421,7 +1404,8 @@ do_open_trash() {
do_copy_path() { do_copy_path() {
entry=$(get_entry "$SEL") entry=$(get_entry "$SEL")
[ -z "$entry" ] && return [ -z "$entry" ] && return
_path="${CWD}/${entry%/}" _name="${entry%@}"; _name="${_name%/}"
_path="${CWD}/${_name}"
if command -v wl-copy >/dev/null 2>&1; then if command -v wl-copy >/dev/null 2>&1; then
printf '%s' "$_path" | wl-copy printf '%s' "$_path" | wl-copy
elif command -v xclip >/dev/null 2>&1; then elif command -v xclip >/dev/null 2>&1; then
@@ -1479,6 +1463,8 @@ do_bookmark_jump() {
_hl=$(printf '%*s' "$_iw" '' | tr ' ' '-') _hl=$(printf '%*s' "$_iw" '' | tr ' ' '-')
_bsel=1 _bsel=1
_bvis=$((_ph - 3))
_boff=1
while true; do while true; do
# draw picker inline # draw picker inline
goto "$_py" "$_px" goto "$_py" "$_px"
@@ -1488,11 +1474,23 @@ do_bookmark_jump() {
printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_t" "$_p" "${BOLD}${CYAN}" "${RESET}" printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_t" "$_p" "${BOLD}${CYAN}" "${RESET}"
goto "$((_py+2))" "$_px" goto "$((_py+2))" "$_px"
printf '%s|%s|%s' "${BOLD}${CYAN}" "$_hl" "${RESET}" printf '%s|%s|%s' "${BOLD}${CYAN}" "$_hl" "${RESET}"
# clear visible rows first
_bri=0
while [ "$_bri" -lt "$_bvis" ]; do
goto "$((_py + 3 + _bri))" "$_px"
_bep=$(printf '%*s' "$_iw" '')
printf '%s|%s|%s' "${BOLD}${CYAN}" "$_bep" "${RESET}"
_bri=$((_bri + 1))
done
# draw visible bookmarks
_bi=0 _bi=0
while IFS= read -r _bm; do while IFS= read -r _bm; do
[ -z "$_bm" ] && continue [ -z "$_bm" ] && continue
_bi=$((_bi+1)) _bi=$((_bi+1))
goto "$((_py+2+_bi))" "$_px" [ "$_bi" -lt "$_boff" ] && continue
[ "$_bi" -ge $((_boff + _bvis)) ] && continue
_drow=$((_py + 2 + _bi - _boff + 1))
goto "$_drow" "$_px"
_bt="${_bi} ${_bm}" _bt="${_bi} ${_bm}"
if [ "${#_bt}" -gt "$_iw" ]; then _bt=$(printf '%s' "$_bt" | cut -c1-$((_iw-1))); _bt="${_bt}~"; fi if [ "${#_bt}" -gt "$_iw" ]; then _bt=$(printf '%s' "$_bt" | cut -c1-$((_iw-1))); _bt="${_bt}~"; fi
_bpl=$((_iw - ${#_bt})); _bp=$(printf '%*s' "$_bpl" '') _bpl=$((_iw - ${#_bt})); _bp=$(printf '%*s' "$_bpl" '')
@@ -1506,14 +1504,18 @@ do_bookmark_jump() {
printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}" printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}"
IFS= read -r -n1 _bk 2>/dev/null || IFS= read -r _bk IFS= read -r -n1 _bk 2>/dev/null || IFS= read -r _bk
case "$_bk" in case "$_bk" in
j) _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc ;; j) _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc
k) _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1 ;; [ "$_bsel" -ge $((_boff + _bvis)) ] && _boff=$((_bsel - _bvis + 1)) ;;
k) _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1
[ "$_bsel" -lt "$_boff" ] && _boff=$_bsel ;;
"$(printf '\033')") "$(printf '\033')")
# use timeout to distinguish bare esc from arrow sequences # use timeout to distinguish bare esc from arrow sequences
IFS= read -r -n2 -t 0.1 _seq 2>/dev/null || _seq="" IFS= read -r -n2 -t 0.1 _seq 2>/dev/null || _seq=""
case "$_seq" in case "$_seq" in
'[A') _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1 ;; '[A') _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1
'[B') _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc ;; [ "$_bsel" -lt "$_boff" ] && _boff=$_bsel ;;
'[B') _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc
[ "$_bsel" -ge $((_boff + _bvis)) ] && _boff=$((_bsel - _bvis + 1)) ;;
'[C') # right — open '[C') # right — open
_chosen=$(awk -v n="$_bsel" 'NR==n&&NF{print;exit}' "$BOOKMARK_FILE") _chosen=$(awk -v n="$_bsel" 'NR==n&&NF{print;exit}' "$BOOKMARK_FILE")
if [ -n "$_chosen" ] && [ -d "$_chosen" ]; then if [ -n "$_chosen" ] && [ -d "$_chosen" ]; then
@@ -1589,7 +1591,8 @@ do_yank() {
if [ "$_c" -gt 0 ]; then if [ "$_c" -gt 0 ]; then
CLIPBOARD=$(printf '%s\n' "$SELECTED" | while IFS= read -r _e; do CLIPBOARD=$(printf '%s\n' "$SELECTED" | while IFS= read -r _e; do
[ -z "$_e" ] && continue [ -z "$_e" ] && continue
printf '%s\n' "${CWD}/${_e%/}" _yname="${_e%@}"; _yname="${_yname%/}"
printf '%s\n' "${CWD}/${_yname}"
done) done)
CLIP_MODE="copy" CLIP_MODE="copy"
INFO_MSG="yanked ${_c} items" INFO_MSG="yanked ${_c} items"
@@ -1597,7 +1600,8 @@ do_yank() {
else else
entry=$(get_entry "$SEL") entry=$(get_entry "$SEL")
[ -z "$entry" ] && return [ -z "$entry" ] && return
CLIPBOARD="${CWD}/${entry%/}" _yname="${entry%@}"; _yname="${_yname%/}"
CLIPBOARD="${CWD}/${_yname}"
CLIP_MODE="copy" CLIP_MODE="copy"
INFO_MSG="yanked: ${entry}" INFO_MSG="yanked: ${entry}"
fi fi
@@ -1609,7 +1613,8 @@ do_cut() {
if [ "$_c" -gt 0 ]; then if [ "$_c" -gt 0 ]; then
CLIPBOARD=$(printf '%s\n' "$SELECTED" | while IFS= read -r _e; do CLIPBOARD=$(printf '%s\n' "$SELECTED" | while IFS= read -r _e; do
[ -z "$_e" ] && continue [ -z "$_e" ] && continue
printf '%s\n' "${CWD}/${_e%/}" _cname="${_e%@}"; _cname="${_cname%/}"
printf '%s\n' "${CWD}/${_cname}"
done) done)
CLIP_MODE="cut" CLIP_MODE="cut"
INFO_MSG="cut ${_c} items" INFO_MSG="cut ${_c} items"
@@ -1617,7 +1622,8 @@ do_cut() {
else else
entry=$(get_entry "$SEL") entry=$(get_entry "$SEL")
[ -z "$entry" ] && return [ -z "$entry" ] && return
CLIPBOARD="${CWD}/${entry%/}" _cname="${entry%@}"; _cname="${_cname%/}"
CLIPBOARD="${CWD}/${_cname}"
CLIP_MODE="cut" CLIP_MODE="cut"
INFO_MSG="cut: ${entry}" INFO_MSG="cut: ${entry}"
fi fi
@@ -1630,7 +1636,6 @@ do_paste() {
NEED_FULL_REDRAW=1 NEED_FULL_REDRAW=1
return return
fi fi
_ok=0; _fail=0
printf '%s\n' "$CLIPBOARD" | while IFS= read -r _src; do printf '%s\n' "$CLIPBOARD" | while IFS= read -r _src; do
[ -z "$_src" ] && continue [ -z "$_src" ] && continue
_name="${_src##*/}" _name="${_src##*/}"
@@ -1641,8 +1646,8 @@ do_paste() {
_dest="${CWD}/${_base}_copy${_ext}" _dest="${CWD}/${_base}_copy${_ext}"
fi fi
case "$CLIP_MODE" in case "$CLIP_MODE" in
copy) cp -r "$_src" "$_dest" && _ok=$((_ok+1)) || _fail=$((_fail+1)) ;; copy) cp -r "$_src" "$_dest" ;;
cut) mv "$_src" "$_dest" && _ok=$((_ok+1)) || _fail=$((_fail+1)) ;; cut) mv "$_src" "$_dest" ;;
esac esac
done done
CLIPBOARD=""; CLIP_MODE="" CLIPBOARD=""; CLIP_MODE=""
@@ -1651,7 +1656,7 @@ do_paste() {
} }
# --- main --- # --- main ---
trap 'restore_term; show_cursor; printf "\033[2J\033[H"; rm -f /tmp/_sfm_list /tmp/_sfm_find; exit 0' INT TERM EXIT trap 'restore_term; show_cursor; printf "\033[2J\033[H"; rm -f /tmp/_sfm_list_$$ /tmp/_sfm_find_$$; exit 0' INT QUIT TERM EXIT
setup_term setup_term
hide_cursor hide_cursor
@@ -1709,6 +1714,7 @@ while true; do
m) do_mkdir ;; m) do_mkdir ;;
n) do_newfile ;; n) do_newfile ;;
q|Q) break ;; q|Q) break ;;
'!') do_shell ;;
esac esac
done done