sfm

Simple File Manager
git clone git://git.emmett1.my/sfm.git
Log | Files | Refs | README | LICENSE

commit f374506215371cd375b43f54135cbf46dc202ea0
parent fe57971b9192d5ee093df12b288d5d2d9fd119c0
Author: emmett1 <me@emmett1.my>
Date:   Sat, 11 Apr 2026 08:51:01 +0800

update

Diffstat:
Msfm | 80+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 46 insertions(+), 34 deletions(-)

diff --git a/sfm b/sfm @@ -244,40 +244,42 @@ apply_filter() { if [ -z "$FILTER" ]; then ENTRIES="$ALL_ENTRIES" COUNT="$ALL_COUNT" - return - fi - ENTRIES="" - COUNT=0 - printf '%s\n' "$ALL_ENTRIES" | while IFS= read -r line; do - case "$line" in - *"$FILTER"*) printf '%s\n' "$line" ;; - esac - done | { - while IFS= read -r line; do - ENTRIES="$ENTRIES -$line" - COUNT=$((COUNT + 1)) - done - # write back via temp — subshell can't modify parent vars directly - printf '%s\n' "$COUNT" > /tmp/_fm_count - printf '%s' "$ENTRIES" > /tmp/_fm_entries - } - COUNT=$(cat /tmp/_fm_count 2>/dev/null || echo 0) - ENTRIES=$(cat /tmp/_fm_entries 2>/dev/null || echo "") - ENTRIES="${ENTRIES# + else + ENTRIES="" + COUNT=0 + _rest="$ALL_ENTRIES" + while [ -n "$_rest" ]; do + _line="${_rest%% +*}"; _next="${_rest#* }" + [ "$_next" = "$_rest" ] && _next="" + _rest="$_next" + [ -z "$_line" ] && continue + case "$_line" in + *"$FILTER"*) + if [ -z "$ENTRIES" ]; then ENTRIES="$_line" + else ENTRIES="$ENTRIES +$_line"; fi + COUNT=$((COUNT + 1)) ;; + esac + done + fi + # write to tmp file for O(1) line access in get_entry + printf '%s\n' "$ENTRIES" > /tmp/_sfm_list } get_entry() { - printf '%s\n' "$ENTRIES" | awk -v n="$(($1 + 1))" 'NR==n{print; exit}' + sed -n "$(($1 + 1))p" /tmp/_sfm_list } # find index of entry by name (0-based), returns -1 if not found find_entry() { - printf '%s\n' "$ENTRIES" | awk -v name="$1" ' - { if ($0 == name) { print NR-1; found=1; exit } } - END { if (!found) print -1 } - ' + _fe_n=$(grep -n "^${1}$" /tmp/_sfm_list 2>/dev/null | head -1 | cut -d: -f1) + if [ -n "$_fe_n" ]; then + printf '%d' $((_fe_n - 1)) + else + printf '%d' -1 + fi } # is entry name in SELECTED list? @@ -344,13 +346,19 @@ render_row() { */) colour="${BLUE}${BOLD}" ;; *@) _name="${entry%@}" - # broken symlink if target doesn't exist if [ -e "${CWD}/${_name}" ]; then colour="${CYAN}${BOLD}" else colour="${RED}${BOLD}" fi ;; - *) colour=$(file_colour "$entry") ;; + *) + _fc="${CWD}/${entry}" + if [ -x "$_fc" ]; then colour="${GREEN}${BOLD}" + elif [ -c "$_fc" ]; then colour="${MAGENTA}${BOLD}" + elif [ -b "$_fc" ]; then colour="${MAGENTA}${BOLD}" + elif [ ! -r "$_fc" ] || [ ! -w "$_fc" ]; then colour="${RED}${BOLD}" + else colour="${WHITE}" + fi ;; esac # multi-select marker @@ -382,17 +390,21 @@ render_row() { if [ "$SHOW_DETAILS" = "1" ]; then _path="${CWD}/${entry%/}"; _path="${_path%@}" _info=$(ls -ldh "$_path" 2>/dev/null) - _sz=$(printf '%s' "$_info" | awk '{print $5}') - _dt=$(printf '%s' "$_info" | awk '{print $6, $7}') - # fixed width: size=6, date=6 → total detail = 14 chars + # parse ls output with parameter expansion — no awk fork + set -- $_info + _sz=$5; _dt="$6 $7" _detail=$(printf ' %6s %-6s' "$_sz" "$_dt") + set -- fi # layout: marker(1) + name + spaces + detail _dlen=${#_detail} - maxw=$((_cols - 1 - _dlen - 1)) + maxw=$((_cols - 2 - _dlen)) if [ "${#display}" -gt "$maxw" ]; then - display=$(printf '%s' "$display" | cut -c1-$((_cols - _dlen - 4))) + _trunc=$((maxw - 3)) + while [ "${#display}" -gt "$_trunc" ]; do + display="${display%?}" + done display="${display}..." fi _namew=$((${#display} + 1)) # 1 for marker @@ -1633,7 +1645,7 @@ do_paste() { } # --- main --- -trap 'restore_term; show_cursor; printf "\033[2J\033[H"; 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 TERM EXIT setup_term hide_cursor