From bed888c3c3910aecaff19d63e0ea905316d23507 Mon Sep 17 00:00:00 2001 From: emmett1 Date: Fri, 29 May 2026 11:32:58 +0800 Subject: fix and updates --- sfm | 106 ++++++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/sfm b/sfm index d440464..9b181c9 100755 --- a/sfm +++ b/sfm @@ -16,23 +16,6 @@ MAGENTA=$(tput_cmd setaf 5) BLUE=$(tput_cmd setaf 4) 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"; } hide_cursor() { printf '\033[?25l'; } show_cursor() { printf '\033[?25h'; } @@ -174,8 +157,8 @@ $_line"; fi ;; # sort files using ls into a tmp file if [ -n "$_files" ]; then case "$SORT_MODE" in - size) ls -1Sp "$CWD" 2>/dev/null | grep -v '/' > /tmp/_fm_sorted ;; - date) ls -1tp "$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_$$ ;; esac # only keep files that were already in our list (respects hidden filter) _sorted="" @@ -190,7 +173,7 @@ ${_n} else _sorted="$_sorted $_n"; fi ;; esac - done < /tmp/_fm_sorted + done < /tmp/_fm_sorted_$$ _files="$_sorted" fi @@ -265,16 +248,16 @@ $_line"; fi done fi # 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() { - 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_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 printf '%d' $((_fe_n - 1)) else @@ -284,12 +267,7 @@ find_entry() { # is entry name in SELECTED list? is_selected() { - case " -${SELECTED} -" in *" -$1 -"*) return 0 ;; esac - return 1 + printf '%s\n' "$SELECTED" | grep -Fxq "$1" } count_selected() { @@ -1033,7 +1011,7 @@ do_find() { _query="$READ_LINE" # 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" _total=$(wc -l < "$_tmp" | tr -d '[:space:]') @@ -1258,7 +1236,8 @@ do_delete() { [ "$_next" = "$_rest" ] && _next="" _rest="$_next" [ -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 done SELECTED="" @@ -1271,7 +1250,8 @@ do_delete() { else # --- single delete --- entry=$(get_entry "$SEL") - target="${CWD}/${entry%/}" + _name="${entry%@}"; _name="${_name%/}" + target="${CWD}/${_name}" restore_term; show_cursor goto "$(term_rows)" 1 printf '%s%s Delete "%s"? [y/N] %s' "${ERASE_LINE}" "${RED}${BOLD}" "$entry" "${RESET}" @@ -1311,7 +1291,8 @@ do_rename() { goto "$(term_rows)" 1 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 - mv "${CWD}/${entry%/}" "${CWD}/${READ_LINE}" + _name="${entry%@}"; _name="${_name%/}" + mv "${CWD}/${_name}" "${CWD}/${READ_LINE}" load_entries else NEED_FULL_REDRAW=1; draw @@ -1358,7 +1339,8 @@ do_chmod_nox() { do_info() { entry=$(get_entry "$SEL") [ -z "$entry" ] && return - target="${CWD}/${entry%/}" + _name="${entry%@}"; _name="${_name%/}" + target="${CWD}/${_name}" # permissions + type _perm=$(ls -ld "$target" 2>/dev/null | awk '{print $1}') # size (human readable via du, fallback to ls) @@ -1396,9 +1378,10 @@ do_sort() { do_trash() { entry=$(get_entry "$SEL") [ -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') - _dest="${TRASH_DIR}/${_ts}_${entry%/}" + _dest="${TRASH_DIR}/${_ts}_${_name}" if mv "$target" "$_dest"; then INFO_MSG="trashed: ${entry}" [ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2)) @@ -1421,7 +1404,8 @@ do_open_trash() { do_copy_path() { entry=$(get_entry "$SEL") [ -z "$entry" ] && return - _path="${CWD}/${entry%/}" + _name="${entry%@}"; _name="${_name%/}" + _path="${CWD}/${_name}" if command -v wl-copy >/dev/null 2>&1; then printf '%s' "$_path" | wl-copy elif command -v xclip >/dev/null 2>&1; then @@ -1479,6 +1463,8 @@ do_bookmark_jump() { _hl=$(printf '%*s' "$_iw" '' | tr ' ' '-') _bsel=1 + _bvis=$((_ph - 3)) + _boff=1 while true; do # draw picker inline 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}" goto "$((_py+2))" "$_px" 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 while IFS= read -r _bm; do [ -z "$_bm" ] && continue _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}" if [ "${#_bt}" -gt "$_iw" ]; then _bt=$(printf '%s' "$_bt" | cut -c1-$((_iw-1))); _bt="${_bt}~"; fi _bpl=$((_iw - ${#_bt})); _bp=$(printf '%*s' "$_bpl" '') @@ -1506,14 +1504,18 @@ do_bookmark_jump() { printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}" IFS= read -r -n1 _bk 2>/dev/null || IFS= read -r _bk case "$_bk" in - j) _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc ;; - k) _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1 ;; + j) _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc + [ "$_bsel" -ge $((_boff + _bvis)) ] && _boff=$((_bsel - _bvis + 1)) ;; + k) _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1 + [ "$_bsel" -lt "$_boff" ] && _boff=$_bsel ;; "$(printf '\033')") # use timeout to distinguish bare esc from arrow sequences IFS= read -r -n2 -t 0.1 _seq 2>/dev/null || _seq="" case "$_seq" in - '[A') _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1 ;; - '[B') _bsel=$((_bsel+1)); [ "$_bsel" -gt "$_bc" ] && _bsel=$_bc ;; + '[A') _bsel=$((_bsel-1)); [ "$_bsel" -lt 1 ] && _bsel=1 + [ "$_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 _chosen=$(awk -v n="$_bsel" 'NR==n&&NF{print;exit}' "$BOOKMARK_FILE") if [ -n "$_chosen" ] && [ -d "$_chosen" ]; then @@ -1589,7 +1591,8 @@ do_yank() { if [ "$_c" -gt 0 ]; then CLIPBOARD=$(printf '%s\n' "$SELECTED" | while IFS= read -r _e; do [ -z "$_e" ] && continue - printf '%s\n' "${CWD}/${_e%/}" + _yname="${_e%@}"; _yname="${_yname%/}" + printf '%s\n' "${CWD}/${_yname}" done) CLIP_MODE="copy" INFO_MSG="yanked ${_c} items" @@ -1597,7 +1600,8 @@ do_yank() { else entry=$(get_entry "$SEL") [ -z "$entry" ] && return - CLIPBOARD="${CWD}/${entry%/}" + _yname="${entry%@}"; _yname="${_yname%/}" + CLIPBOARD="${CWD}/${_yname}" CLIP_MODE="copy" INFO_MSG="yanked: ${entry}" fi @@ -1609,7 +1613,8 @@ do_cut() { if [ "$_c" -gt 0 ]; then CLIPBOARD=$(printf '%s\n' "$SELECTED" | while IFS= read -r _e; do [ -z "$_e" ] && continue - printf '%s\n' "${CWD}/${_e%/}" + _cname="${_e%@}"; _cname="${_cname%/}" + printf '%s\n' "${CWD}/${_cname}" done) CLIP_MODE="cut" INFO_MSG="cut ${_c} items" @@ -1617,7 +1622,8 @@ do_cut() { else entry=$(get_entry "$SEL") [ -z "$entry" ] && return - CLIPBOARD="${CWD}/${entry%/}" + _cname="${entry%@}"; _cname="${_cname%/}" + CLIPBOARD="${CWD}/${_cname}" CLIP_MODE="cut" INFO_MSG="cut: ${entry}" fi @@ -1630,7 +1636,6 @@ do_paste() { NEED_FULL_REDRAW=1 return fi - _ok=0; _fail=0 printf '%s\n' "$CLIPBOARD" | while IFS= read -r _src; do [ -z "$_src" ] && continue _name="${_src##*/}" @@ -1641,8 +1646,8 @@ do_paste() { _dest="${CWD}/${_base}_copy${_ext}" fi case "$CLIP_MODE" in - copy) cp -r "$_src" "$_dest" && _ok=$((_ok+1)) || _fail=$((_fail+1)) ;; - cut) mv "$_src" "$_dest" && _ok=$((_ok+1)) || _fail=$((_fail+1)) ;; + copy) cp -r "$_src" "$_dest" ;; + cut) mv "$_src" "$_dest" ;; esac done CLIPBOARD=""; CLIP_MODE="" @@ -1651,7 +1656,7 @@ do_paste() { } # --- 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 hide_cursor @@ -1709,6 +1714,7 @@ while true; do m) do_mkdir ;; n) do_newfile ;; q|Q) break ;; + '!') do_shell ;; esac done -- cgit v1.2.3