|
|
|
@@ -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() {
|
|
|
|
@@ -799,8 +777,10 @@ open_file() {
|
|
|
|
|
# read a line of input in raw mode, returns result in READ_LINE
|
|
|
|
|
# returns 1 if user pressed Esc (cancel), 0 on Enter
|
|
|
|
|
read_line() {
|
|
|
|
|
READ_LINE=""
|
|
|
|
|
READ_LINE="${_init:-}"
|
|
|
|
|
_init=""
|
|
|
|
|
show_cursor
|
|
|
|
|
printf '%s' "$READ_LINE"
|
|
|
|
|
while true; do
|
|
|
|
|
IFS= read -r -n1 _ch 2>/dev/null || IFS= read -r _ch
|
|
|
|
|
case "$_ch" in
|
|
|
|
@@ -855,7 +835,7 @@ do_open_with() {
|
|
|
|
|
|
|
|
|
|
do_newfile() {
|
|
|
|
|
goto "$(term_rows)" 1
|
|
|
|
|
printf '%s%s New file name (esc=cancel): %s' "${ERASE_LINE}" "${WHITE}${BOLD}" "${RESET}"
|
|
|
|
|
printf '%s%s File name: %s' "${ERASE_LINE}" "${WHITE}${BOLD}" "${RESET}"
|
|
|
|
|
if read_line && [ -n "$READ_LINE" ]; then
|
|
|
|
|
touch "${CWD}/${READ_LINE}"
|
|
|
|
|
load_entries
|
|
|
|
@@ -932,8 +912,8 @@ do_help() {
|
|
|
|
|
help_row 30 " + chmod +x (make executable)"
|
|
|
|
|
help_row 31 " - chmod -x (remove executable)"
|
|
|
|
|
help_sep 32
|
|
|
|
|
help_row 33 " b bookmark current dir"
|
|
|
|
|
help_row 34 " B open bookmark picker"
|
|
|
|
|
help_row 33 " b open bookmark picker"
|
|
|
|
|
help_row 34 " B bookmark current dir"
|
|
|
|
|
help_row 35 " c copy path to clipboard"
|
|
|
|
|
help_row 36 " ~ go to home directory"
|
|
|
|
|
help_row 37 " \` jump to previous directory"
|
|
|
|
@@ -1033,7 +1013,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:]')
|
|
|
|
|
|
|
|
|
@@ -1236,82 +1216,149 @@ do_open() {
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# --- confirm overlay (yes/no popup) ---
|
|
|
|
|
confirm_overlay() {
|
|
|
|
|
_co_rows=$(term_rows); _co_cols=$(term_cols)
|
|
|
|
|
_co_yes=" Yes "
|
|
|
|
|
_co_no=" No "
|
|
|
|
|
_co_gap=" "
|
|
|
|
|
_co_content="${_co_yes}${_co_gap}${_co_no}"
|
|
|
|
|
|
|
|
|
|
# size box to fit prompt or buttons, whichever is wider
|
|
|
|
|
_co_w=$(( ${#_prompt} + 4 ))
|
|
|
|
|
[ "$_co_w" -lt $(( ${#_co_content} + 4 )) ] && _co_w=$(( ${#_co_content} + 4 ))
|
|
|
|
|
[ "$_co_w" -gt "$_co_cols" ] && _co_w=$((_co_cols - 2))
|
|
|
|
|
_co_iw=$((_co_w - 2))
|
|
|
|
|
_co_hl=$(printf '%*s' "$_co_iw" '' | tr ' ' '-')
|
|
|
|
|
|
|
|
|
|
_co_x=$(( (_co_cols - _co_w) / 2 )); [ "$_co_x" -lt 0 ] && _co_x=0
|
|
|
|
|
_co_y=$(( (_co_rows - 5) / 2 )); [ "$_co_y" -lt 1 ] && _co_y=1
|
|
|
|
|
_co_sel=2 # 1=Yes, 2=No (default No for safety)
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
# draw box top
|
|
|
|
|
goto "$_co_y" "$_co_x"
|
|
|
|
|
printf '%s+%s+%s' "${BOLD}${CYAN}" "$_co_hl" "${RESET}"
|
|
|
|
|
# prompt line
|
|
|
|
|
goto "$((_co_y+1))" "$_co_x"
|
|
|
|
|
_co_pl=$((_co_iw - ${#_prompt}))
|
|
|
|
|
[ "$_co_pl" -lt 0 ] && _co_pl=0
|
|
|
|
|
_co_pad=$(printf '%*s' "$_co_pl" '')
|
|
|
|
|
printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_prompt" "$_co_pad" "${BOLD}${CYAN}" "${RESET}"
|
|
|
|
|
# separator
|
|
|
|
|
goto "$((_co_y+2))" "$_co_x"
|
|
|
|
|
printf '%s|%s|%s' "${BOLD}${CYAN}" "$_co_hl" "${RESET}"
|
|
|
|
|
# yes/no buttons
|
|
|
|
|
goto "$((_co_y+3))" "$_co_x"
|
|
|
|
|
_co_cpad=$(( (_co_iw - ${#_co_content}) / 2 ))
|
|
|
|
|
[ "$_co_cpad" -lt 0 ] && _co_cpad=0
|
|
|
|
|
_co_left=$(printf '%*s' "$_co_cpad" '')
|
|
|
|
|
_co_cpad=$(( _co_iw - ${#_co_content} - _co_cpad ))
|
|
|
|
|
[ "$_co_cpad" -lt 0 ] && _co_cpad=0
|
|
|
|
|
_co_right=$(printf '%*s' "$_co_cpad" '')
|
|
|
|
|
printf '%s|%s%s' "${BOLD}${CYAN}" "${RESET}" "$_co_left"
|
|
|
|
|
if [ "$_co_sel" -eq 1 ]; then
|
|
|
|
|
printf '%s%s%s' "${REV}${GREEN}${BOLD}" "$_co_yes" "${RESET}"
|
|
|
|
|
else
|
|
|
|
|
printf '%s' "$_co_yes"
|
|
|
|
|
fi
|
|
|
|
|
printf '%s' "$_co_gap"
|
|
|
|
|
if [ "$_co_sel" -eq 2 ]; then
|
|
|
|
|
printf '%s%s%s' "${REV}${RED}${BOLD}" "$_co_no" "${RESET}"
|
|
|
|
|
else
|
|
|
|
|
printf '%s' "$_co_no"
|
|
|
|
|
fi
|
|
|
|
|
printf '%s%s|%s' "$_co_right" "${BOLD}${CYAN}" "${RESET}"
|
|
|
|
|
# box bottom
|
|
|
|
|
goto "$((_co_y+4))" "$_co_x"
|
|
|
|
|
printf '%s+%s+%s' "${BOLD}${CYAN}" "$_co_hl" "${RESET}"
|
|
|
|
|
|
|
|
|
|
# read key
|
|
|
|
|
IFS= read -r -n1 _co_ch 2>/dev/null || IFS= read -r _co_ch
|
|
|
|
|
case "$_co_ch" in
|
|
|
|
|
"$(printf '\033')")
|
|
|
|
|
IFS= read -r -n2 -t 0.1 _co_seq 2>/dev/null || _co_seq=""
|
|
|
|
|
case "$_co_seq" in
|
|
|
|
|
'[D'|'[C') # left/right arrow
|
|
|
|
|
if [ "$_co_sel" -eq 1 ]; then _co_sel=2; else _co_sel=1; fi ;;
|
|
|
|
|
*) return 1 ;; # bare esc or unrecognised sequence
|
|
|
|
|
esac ;;
|
|
|
|
|
h) _co_sel=1 ;;
|
|
|
|
|
l) _co_sel=2 ;;
|
|
|
|
|
j) _co_sel=2 ;;
|
|
|
|
|
k) _co_sel=1 ;;
|
|
|
|
|
y|Y) return 0 ;;
|
|
|
|
|
n|N) return 1 ;;
|
|
|
|
|
"$(printf '\n')"|"$(printf '\r')")
|
|
|
|
|
if [ "$_co_sel" -eq 1 ]; then return 0; else return 1; fi ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do_delete() {
|
|
|
|
|
INFO_MSG=""
|
|
|
|
|
_c=$(count_selected)
|
|
|
|
|
if [ "$_c" -gt 0 ]; then
|
|
|
|
|
# --- bulk delete selected ---
|
|
|
|
|
restore_term; show_cursor
|
|
|
|
|
goto "$(term_rows)" 1
|
|
|
|
|
printf '%s%s Delete %d selected items? [y/N] %s' \
|
|
|
|
|
"${ERASE_LINE}" "${RED}${BOLD}" "$_c" "${RESET}"
|
|
|
|
|
IFS= read -r ans
|
|
|
|
|
setup_term; hide_cursor
|
|
|
|
|
case "$ans" in
|
|
|
|
|
y|Y)
|
|
|
|
|
_rest="$SELECTED"
|
|
|
|
|
while [ -n "$_rest" ]; do
|
|
|
|
|
_line="${_rest%%
|
|
|
|
|
_prompt="Delete ${_c} selected items?"
|
|
|
|
|
confirm_overlay; _yes=$?
|
|
|
|
|
if [ "$_yes" -eq 0 ]; then
|
|
|
|
|
_rest="$SELECTED"
|
|
|
|
|
while [ -n "$_rest" ]; do
|
|
|
|
|
_line="${_rest%%
|
|
|
|
|
*}"
|
|
|
|
|
_next="${_rest#*
|
|
|
|
|
_next="${_rest#*
|
|
|
|
|
}"
|
|
|
|
|
[ "$_next" = "$_rest" ] && _next=""
|
|
|
|
|
_rest="$_next"
|
|
|
|
|
[ -z "$_line" ] && continue
|
|
|
|
|
_t="${CWD}/${_line%/}"
|
|
|
|
|
if [ -d "$_t" ]; then rm -rf "$_t"; else rm -f "$_t"; fi
|
|
|
|
|
done
|
|
|
|
|
SELECTED=""
|
|
|
|
|
[ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2))
|
|
|
|
|
[ "$SEL" -lt 0 ] && SEL=0
|
|
|
|
|
load_entries
|
|
|
|
|
;;
|
|
|
|
|
*) NEED_FULL_REDRAW=1 ;;
|
|
|
|
|
esac
|
|
|
|
|
[ "$_next" = "$_rest" ] && _next=""
|
|
|
|
|
_rest="$_next"
|
|
|
|
|
[ -z "$_line" ] && continue
|
|
|
|
|
_name="${_line%@}"; _name="${_name%/}"
|
|
|
|
|
_t="${CWD}/${_name}"
|
|
|
|
|
if [ -d "$_t" ]; then rm -rf "$_t"; else rm -f "$_t"; fi
|
|
|
|
|
done
|
|
|
|
|
SELECTED=""
|
|
|
|
|
[ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2))
|
|
|
|
|
[ "$SEL" -lt 0 ] && SEL=0
|
|
|
|
|
load_entries
|
|
|
|
|
else
|
|
|
|
|
NEED_FULL_REDRAW=1
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
# --- single delete ---
|
|
|
|
|
entry=$(get_entry "$SEL")
|
|
|
|
|
target="${CWD}/${entry%/}"
|
|
|
|
|
restore_term; show_cursor
|
|
|
|
|
goto "$(term_rows)" 1
|
|
|
|
|
printf '%s%s Delete "%s"? [y/N] %s' "${ERASE_LINE}" "${RED}${BOLD}" "$entry" "${RESET}"
|
|
|
|
|
IFS= read -r ans
|
|
|
|
|
setup_term; hide_cursor
|
|
|
|
|
case "$ans" in
|
|
|
|
|
y|Y)
|
|
|
|
|
if [ -d "$target" ]; then
|
|
|
|
|
if [ -n "$(ls -A "$target" 2>/dev/null)" ]; then
|
|
|
|
|
restore_term; show_cursor
|
|
|
|
|
goto "$(term_rows)" 1
|
|
|
|
|
printf '%s%s "%s" not empty. Delete ALL? [y/N] %s' \
|
|
|
|
|
"${ERASE_LINE}" "${RED}${BOLD}" "$entry" "${RESET}"
|
|
|
|
|
IFS= read -r ans2
|
|
|
|
|
setup_term; hide_cursor
|
|
|
|
|
case "$ans2" in
|
|
|
|
|
y|Y) rm -rf "$target" ;;
|
|
|
|
|
*) NEED_FULL_REDRAW=1; return ;;
|
|
|
|
|
esac
|
|
|
|
|
else
|
|
|
|
|
rm -rf "$target"
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
rm -f "$target"
|
|
|
|
|
_name="${entry%@}"; _name="${_name%/}"
|
|
|
|
|
target="${CWD}/${_name}"
|
|
|
|
|
_prompt="Delete \"${entry}\"?"
|
|
|
|
|
confirm_overlay; _yes=$?
|
|
|
|
|
if [ "$_yes" -ne 0 ]; then
|
|
|
|
|
NEED_FULL_REDRAW=1; return
|
|
|
|
|
fi
|
|
|
|
|
if [ -d "$target" ]; then
|
|
|
|
|
if [ -n "$(ls -A "$target" 2>/dev/null)" ]; then
|
|
|
|
|
_prompt="\"${entry}\" not empty. Delete ALL?"
|
|
|
|
|
confirm_overlay; _yes2=$?
|
|
|
|
|
if [ "$_yes2" -ne 0 ]; then
|
|
|
|
|
NEED_FULL_REDRAW=1; return
|
|
|
|
|
fi
|
|
|
|
|
[ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2))
|
|
|
|
|
[ "$SEL" -lt 0 ] && SEL=0
|
|
|
|
|
load_entries
|
|
|
|
|
;;
|
|
|
|
|
*) NEED_FULL_REDRAW=1 ;;
|
|
|
|
|
esac
|
|
|
|
|
fi
|
|
|
|
|
rm -rf "$target"
|
|
|
|
|
else
|
|
|
|
|
rm -f "$target"
|
|
|
|
|
fi
|
|
|
|
|
[ "$SEL" -ge "$((COUNT - 1))" ] && SEL=$((COUNT - 2))
|
|
|
|
|
[ "$SEL" -lt 0 ] && SEL=0
|
|
|
|
|
load_entries
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do_rename() {
|
|
|
|
|
entry=$(get_entry "$SEL")
|
|
|
|
|
_name="${entry%@}"; _name="${_name%/}"
|
|
|
|
|
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}"
|
|
|
|
|
printf '%s%s Rename: %s' "${ERASE_LINE}" "${YELLOW}${BOLD}" "${RESET}"
|
|
|
|
|
_init="$_name"
|
|
|
|
|
if read_line && [ -n "$READ_LINE" ] && [ "$READ_LINE" != "$_name" ]; then
|
|
|
|
|
mv "${CWD}/${_name}" "${CWD}/${READ_LINE}"
|
|
|
|
|
load_entries
|
|
|
|
|
else
|
|
|
|
|
NEED_FULL_REDRAW=1; draw
|
|
|
|
@@ -1320,7 +1367,7 @@ do_rename() {
|
|
|
|
|
|
|
|
|
|
do_mkdir() {
|
|
|
|
|
goto "$(term_rows)" 1
|
|
|
|
|
printf '%s%s New dir name (esc=cancel): %s' "${ERASE_LINE}" "${CYAN}${BOLD}" "${RESET}"
|
|
|
|
|
printf '%s%s Directory name: %s' "${ERASE_LINE}" "${CYAN}${BOLD}" "${RESET}"
|
|
|
|
|
if read_line && [ -n "$READ_LINE" ]; then
|
|
|
|
|
mkdir -p "${CWD}/${READ_LINE}"
|
|
|
|
|
load_entries
|
|
|
|
@@ -1358,15 +1405,96 @@ do_chmod_nox() {
|
|
|
|
|
do_info() {
|
|
|
|
|
entry=$(get_entry "$SEL")
|
|
|
|
|
[ -z "$entry" ] && return
|
|
|
|
|
target="${CWD}/${entry%/}"
|
|
|
|
|
# permissions + type
|
|
|
|
|
_perm=$(ls -ld "$target" 2>/dev/null | awk '{print $1}')
|
|
|
|
|
# size (human readable via du, fallback to ls)
|
|
|
|
|
_size=$(du -sh "$target" 2>/dev/null | cut -f1)
|
|
|
|
|
[ -z "$_size" ] && _size=$(ls -lh "$target" 2>/dev/null | awk '{print $5}')
|
|
|
|
|
# modification date
|
|
|
|
|
_date=$(ls -ld "$target" 2>/dev/null | awk '{print $6, $7, $8}')
|
|
|
|
|
INFO_MSG="${_perm} ${_size} ${_date}"
|
|
|
|
|
_name="${entry%@}"; _name="${_name%/}"
|
|
|
|
|
target="${CWD}/${_name}"
|
|
|
|
|
|
|
|
|
|
# gather info
|
|
|
|
|
_info_perm=$(ls -ld "$target" 2>/dev/null | awk '{print $1}')
|
|
|
|
|
_info_owner=$(ls -ld "$target" 2>/dev/null | awk '{print $3}')
|
|
|
|
|
_info_group=$(ls -ld "$target" 2>/dev/null | awk '{print $4}')
|
|
|
|
|
_info_size=$(du -sh "$target" 2>/dev/null | cut -f1)
|
|
|
|
|
[ -z "$_info_size" ] && _info_size=$(ls -lh "$target" 2>/dev/null | awk '{print $5}')
|
|
|
|
|
_info_date=$(ls -ld "$target" 2>/dev/null | awk '{print $6, $7, $8}')
|
|
|
|
|
|
|
|
|
|
case "$entry" in
|
|
|
|
|
*/) _info_type="directory" ;;
|
|
|
|
|
*@) _info_type="symlink -> $(ls -ld "$target" 2>/dev/null | awk '{print $NF}')" ;;
|
|
|
|
|
*) _info_type="file"
|
|
|
|
|
if [ -x "$target" ]; then _info_type="executable"; fi ;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
_info_lines=""
|
|
|
|
|
_info_lines="${_info_lines}Name: ${_name}
|
|
|
|
|
"
|
|
|
|
|
_info_lines="${_info_lines}Type: ${_info_type}
|
|
|
|
|
"
|
|
|
|
|
_info_lines="${_info_lines}Perm: ${_info_perm}
|
|
|
|
|
"
|
|
|
|
|
_info_lines="${_info_lines}Owner: ${_info_owner}:${_info_group}
|
|
|
|
|
"
|
|
|
|
|
_info_lines="${_info_lines}Size: ${_info_size}
|
|
|
|
|
"
|
|
|
|
|
_info_lines="${_info_lines}Mod: ${_info_date}
|
|
|
|
|
"
|
|
|
|
|
|
|
|
|
|
# measure longest line
|
|
|
|
|
_maxw=0
|
|
|
|
|
_info_rest="$_info_lines"
|
|
|
|
|
while [ -n "$_info_rest" ]; do
|
|
|
|
|
_info_l="${_info_rest%%
|
|
|
|
|
*}"
|
|
|
|
|
[ "${#_info_l}" -gt "$_maxw" ] && _maxw=${#_info_l}
|
|
|
|
|
_info_next="${_info_rest#*
|
|
|
|
|
}"
|
|
|
|
|
[ "$_info_next" = "$_info_rest" ] && break
|
|
|
|
|
_info_rest="$_info_next"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
_info_h=8 # 6 data rows + top + bottom
|
|
|
|
|
ROWS=$(term_rows); COLS=$(term_cols)
|
|
|
|
|
_iw=$((_maxw + 2)); [ "$_iw" -lt 20 ] && _iw=20
|
|
|
|
|
_bw=$((_iw + 2)); [ "$_bw" -gt "$COLS" ] && _bw=$COLS && _iw=$((_bw - 2))
|
|
|
|
|
_ix=$(( (COLS - _bw) / 2 )); [ "$_ix" -lt 0 ] && _ix=0
|
|
|
|
|
_iy=$(( (ROWS - _info_h) / 2 )); [ "$_iy" -lt 1 ] && _iy=1
|
|
|
|
|
_hl=$(printf '%*s' "$_iw" '' | tr ' ' '-')
|
|
|
|
|
|
|
|
|
|
# draw
|
|
|
|
|
goto "$_iy" "$_ix"
|
|
|
|
|
printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}"
|
|
|
|
|
goto "$((_iy+1))" "$_ix"
|
|
|
|
|
_title=" FILE INFO"
|
|
|
|
|
_tpad=$((_iw - ${#_title}))
|
|
|
|
|
[ "$_tpad" -lt 0 ] && _tpad=0
|
|
|
|
|
printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_title" "$(printf '%*s' "$_tpad" '')" "${BOLD}${CYAN}" "${RESET}"
|
|
|
|
|
goto "$((_iy+2))" "$_ix"
|
|
|
|
|
printf '%s|%s|%s' "${BOLD}${CYAN}" "$_hl" "${RESET}"
|
|
|
|
|
|
|
|
|
|
_ri=3
|
|
|
|
|
_info_rest="$_info_lines"
|
|
|
|
|
while [ -n "$_info_rest" ]; do
|
|
|
|
|
_info_l="${_info_rest%%
|
|
|
|
|
*}"
|
|
|
|
|
_info_next="${_info_rest#*
|
|
|
|
|
}"
|
|
|
|
|
[ "$_info_next" = "$_info_rest" ] && break
|
|
|
|
|
_info_rest="$_info_next"
|
|
|
|
|
[ -z "$_info_l" ] && continue
|
|
|
|
|
goto "$((_iy+_ri))" "$_ix"
|
|
|
|
|
_lpad=$((_iw - ${#_info_l}))
|
|
|
|
|
[ "$_lpad" -lt 0 ] && _lpad=0
|
|
|
|
|
printf '%s|%s%s%s%s|%s' "${BOLD}${CYAN}" "${RESET}" "$_info_l" "$(printf '%*s' "$_lpad" '')" "${BOLD}${CYAN}" "${RESET}"
|
|
|
|
|
_ri=$((_ri + 1))
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
goto "$((_iy+_ri))" "$_ix"
|
|
|
|
|
printf '%s+%s+%s' "${BOLD}${CYAN}" "$_hl" "${RESET}"
|
|
|
|
|
|
|
|
|
|
# wait for key
|
|
|
|
|
IFS= read -r -n1 _infok 2>/dev/null || IFS= read -r _infok
|
|
|
|
|
case "$_infok" in
|
|
|
|
|
"$(printf '\033')")
|
|
|
|
|
IFS= read -r -n5 -t 0.1 _infoseq 2>/dev/null || _infoseq="" ;;
|
|
|
|
|
esac
|
|
|
|
|
NEED_FULL_REDRAW=1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1396,9 +1524,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 +1550,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 +1609,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 +1620,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 +1650,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
|
|
|
|
@@ -1526,6 +1674,19 @@ do_bookmark_jump() {
|
|
|
|
|
fi
|
|
|
|
|
return ;;
|
|
|
|
|
'[D') NEED_FULL_REDRAW=1; return ;; # left — close
|
|
|
|
|
'[3') # delete — remove selected bookmark
|
|
|
|
|
IFS= read -r -n1 -t 0.05 _rest 2>/dev/null # swallow trailing ~
|
|
|
|
|
_chosen=$(awk -v n="$_bsel" 'NR==n&&NF{print;exit}' "$BOOKMARK_FILE")
|
|
|
|
|
if [ -n "$_chosen" ]; then
|
|
|
|
|
_tmp=$(grep -vxF "$_chosen" "$BOOKMARK_FILE")
|
|
|
|
|
printf '%s\n' "$_tmp" > "$BOOKMARK_FILE"
|
|
|
|
|
INFO_MSG="bookmark removed: ${_chosen}"
|
|
|
|
|
_bc=$((_bc-1))
|
|
|
|
|
if [ "$_bc" -eq 0 ]; then NEED_FULL_REDRAW=1; return; fi
|
|
|
|
|
[ "$_bsel" -gt "$_bc" ] && _bsel=$_bc
|
|
|
|
|
[ "$_bsel" -lt "$_boff" ] && _boff=$_bsel
|
|
|
|
|
[ "$_bsel" -ge $((_boff + _bvis)) ] && _boff=$((_bsel - _bvis + 1))
|
|
|
|
|
fi ;;
|
|
|
|
|
*) NEED_FULL_REDRAW=1; return ;; # bare esc — close
|
|
|
|
|
esac ;;
|
|
|
|
|
"$(printf '\n')"|\
|
|
|
|
@@ -1589,7 +1750,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 +1759,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 +1772,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 +1781,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 +1795,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 +1805,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 +1815,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
|
|
|
|
@@ -1678,8 +1842,8 @@ while true; do
|
|
|
|
|
l) do_open ;;
|
|
|
|
|
"$(printf '\033[D')"|\
|
|
|
|
|
h) do_go_back ;;
|
|
|
|
|
b) do_bookmark_add ;;
|
|
|
|
|
B) do_bookmark_jump ;;
|
|
|
|
|
b) do_bookmark_jump ;;
|
|
|
|
|
B) do_bookmark_add ;;
|
|
|
|
|
'?') do_help ;;
|
|
|
|
|
R) load_entries; INFO_MSG="refreshed" ;;
|
|
|
|
|
/) do_search ;;
|
|
|
|
@@ -1709,6 +1873,7 @@ while true; do
|
|
|
|
|
m) do_mkdir ;;
|
|
|
|
|
n) do_newfile ;;
|
|
|
|
|
q|Q) break ;;
|
|
|
|
|
'!') do_shell ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|