xrsh-buildroot/buildroot-v86/board/v86/rootfs_overlay/bin/filemanager

1080 lines
44 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env -S awk -f
BEGIN {
###################
# Configuration #
###################
OPENER = ( ENVIRON["FMAWK_OPENER"] == "" ? ( ENVIRON["OSTYPE"] ~ /darwin.*/ ? "open" : "xdg-open" ) : ENVIRON["FMAWK_OPENER"] )
LASTPATH = ( ENVIRON["LASTPATH"] == "" ? ( ENVIRON["HOME"] "/.cache/lastpath" ) : ENVIRON["LASTPATH"] )
HISTORY = ( ENVIRON["HISTORY"] == "" ? ( ENVIRON["HOME"] "/.cache/history" ) : ENVIRON["HISTORY"] )
CMDHIST = ( ENVIRON["CMDHIST"] == "" ? ( ENVIRON["HOME"] "/.cache/cmdhist" ) : ENVIRON["CMDHIST"] )
CACHE = ( ENVIRON["CACHE"] == "" ? ( ENVIRON["HOME"] "/.cache/imagecache" ) : ENVIRON["CACHE"] )
FIFO_UEBERZUG = ENVIRON["FIFO_UEBERZUG"]
FMAWK_PREVIEWER = ENVIRON["FMAWK_PREVIEWER"]
PREVIEW = 0
HIDDEN = 0
RATIO = 0.35
HIST_MAX = 5000
SUBSEP = ","
####################
# Initialization #
####################
# Credit: https://unix.stackexchange.com/questions/224969/current-date-in-awk/225463#225463
srand(); old_time = srand();
init()
RS = "\a"
dir = ( ENVIRON["PWD"] == "/" ? "/" : ENVIRON["PWD"] "/" )
cursor = 1; curpage = 1;
# load alias
cmd = "${SHELL:=/bin/sh} -c \". ~/.${SHELL##*/}rc && alias\""
cmd | getline alias
close(cmd)
split(alias, aliasarr, "\n")
for (line in aliasarr) {
key = aliasarr[line]; gsub(/=.*/, "", key); gsub(/^alias /, "", key)
cmd = aliasarr[line]; gsub(/.*=/, "", cmd); gsub(/^'|'$/, "", cmd)
cmdalias[key] = cmd
}
# defind [a]ttributes, [b]ackground and [f]oreground
a_bold = "\033\1331m"
a_reverse = "\033\1337m"
a_clean = "\033\1332K"
a_reset = "\033\133m"
b_red = "\033\13341m"
f_red = "\033\13331m"
f_green = "\033\13332m"
f_yellow = "\033\13333m"
f_blue = "\033\13334m"
f_magenta = "\033\13335m"
f_cyan = "\033\13336m"
f_white = "\033\13337m"
#############
# Actions #
#############
# action = "History" RS \
# "mv" RS \
# "cp -R" RS \
# "ln -sf" RS \
# "rm -rf"
action = "History"
help = "\n" \
"NUMBERS: \n" \
"\t[num] - move cursor to entry [num] \n" \
"\t[num]+G - Go to page [num] \n" \
"\n" \
"NAVIGATION: \n" \
"\tk/↑ - up j/↓ - down \n" \
"\tl/→ - right h/← - left \n" \
"\tCtrl-f - Half Page Down Ctrl-u - Half Page Up\n" \
"\tn/PageDown - PageDown p/PageUp - PageUp \n" \
"\tg/Home - first page G/End - last page \n" \
"\tH - first entry L - last entry \n" \
"\tM - middle entry\n" \
"\n" \
"MODES: \n" \
"\t/ - search \n" \
"\t: - commandline mode \n" \
"\t commandline mode special function: \n" \
"\t {}: represent selected files/directories\n" \
"\t tab completion on path: start with ' /', use tab to complete on that path \n" \
"\t tab completion on cmd: completion based on command history \n" \
"\t ><: enter selecting mode for directory (choose ./ to confirm destination)\n" \
"\n" \
"SELECTION: \n" \
"\t␣ - bulk (de-)selection S - bulk (de-)selection all \n" \
"\ts - show selected\n" \
"\n" \
"PREVIEW: \n" \
"\tv - toggle preview \n" \
"\t> - more directory ratio < - less directory ratio \n" \
"\n" \
"MISC: \n" \
"\tr - refresh a - actions \n" \
"\t- - previous directory ! - spawn shell \n" \
"\t. - toggle hidden ? - show keybinds\n" \
"\tq - quit \n" \
main();
}
END {
finale();
hist_clean();
cmd_clean();
system("[ -f " CACHE ".jpg ] && rm " CACHE ".jpg 2>/dev/null")
if (list != "empty") {
printf("%s", dir) > "/dev/stdout"; close("/dev/stdout")
printf("%s", dir) > LASTPATH; close(LASTPATH)
}
}
function main() {
do {
list = ( sind == 1 && openind == 1 ? slist : gen_content(dir, HIDDEN) )
delim = "\f"; num = 1; tmsg = dir; bmsg = ( bmsg == "" ? "Browsing" : bmsg );
menu_TUI(list, delim, num, tmsg, bmsg)
response = result[1]
bmsg = result[2]
#######################
# Matching: Actions #
#######################
if (bmsg == "Actions") {
if (response == "History") { hist_act(); sind = 0; response = result[1]; bmsg = "";}
}
########################
# Matching: Browsing #
########################
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", response)
if (response == "../") {
parent = ( dir == "/" ? "/" : dir )
old_dir = parent
if (hist != 1) {
gsub(/[^\/]*\/?$/, "", dir)
gsub(dir, "", parent)
}
# empty_selected()
dir = ( dir == "" ? "/" : dir ); hist = 0; sind = 0; openind = 0;
printf("%s\n", dir) >> HISTORY; close(HISTORY)
continue
}
if (response == "./") {
finale()
system("cd \"" dir "\" && ${SHELL:=/bin/sh}")
init()
sind = 0; openind = 0;
continue
}
if (response ~ /.*\/$/) {
# empty_selected()
old_dir = dir
dir = ( hist == 1 ? response : dir response )
printf("%s\n", dir) >> HISTORY; close(HISTORY)
cursor = 1; curpage = 1; hist = 0; sind = 0; openind = 0;
continue
}
finale()
system("cd \"" dir "\" && " OPENER " \"" dir response "\"")
init()
openind = 1; old_dir = ""; parent = "";
} while (1)
}
function hist_act() {
list = ""
getline hisfile < HISTORY; close(HISTORY);
N = split(hisfile, hisarr, "\n")
# for (i = N; i in hisarr; i--) {
for (i = N; i >= 1; i--) {
list = list "\n" hisarr[i]
}
list = substr(list, 3)
list = list "\n../"; delim = "\n"; num = 1; tmsg = "Choose history: "; bmsg = "Action: " response; hist = 1;
menu_TUI(list, delim, num, tmsg, bmsg)
}
function cmd_clean() { # act like uniq
tmp = "";
getline cmdhist < CMDHIST; close(CMDHIST);
N = split(cmdhist, cmdarr, "\n")
for (i = 1; i in cmdarr; i++) { # collect items not seen
if (! (cmdarr[i] in seen)) { seen[cmdarr[i]]++ }
}
for (key in seen) { # expand seen array into string
if (key != "") { tmp = tmp "\n" key }
}
tmp = substr(tmp, 2)
printf("%s", tmp) > CMDHIST; close(CMDHIST)
}
function hist_clean() {
getline hisfile < HISTORY; close(HISTORY);
N = split(hisfile, hisarr, "\n")
if (N > HIST_MAX) {
for (i = N-HIST_MAX+1; i in hisarr; i++) {
histmp = histmp "\n" hisarr[i]
}
hisfile = substr(histmp, 2)
printf("%s", hisfile) > HISTORY; close(HISTORY)
}
}
function gen_content(dir, HIDDEN) {
if (HIDDEN == 0) {
cmd = "for f in \"" dir "\"*; do "\
"test -L \"$f\" && test -f \"$f\" && symFileList=\"$symFileList$(printf '\f" a_bold f_cyan "%s" a_reset "' \"$f\")\" && continue; "\
"test -L \"$f\" && test -d \"$f\" && symDirList=\"$symDirList$(printf '\f" a_bold f_cyan "%s" a_reset "' \"$f\"/)\" && continue; "\
"test -x \"$f\" && test -f \"$f\" && execList=\"$execList$(printf '\f" a_bold f_green "%s" a_reset "' \"$f\")\" && continue; "\
"test -f \"$f\" && fileList=\"$fileList$(printf '\f%s' \"$f\")\" && continue; "\
"test -d \"$f\" && dirList=\"$dirList$(printf '\f" a_bold f_blue "%s" a_reset "' \"$f\"/)\" ; "\
"done; "\
"printf '%s' \"$dirList\" \"$symDirList\" \"$fileList\" \"$execList\" \"$symFileList\""
}
else if (HIDDEN == 1) {
cmd = "for f in \"" dir "\"* \"" dir "\".* ; do "\
"test -L \"$f\" && test -f \"$f\" && symFileList=\"$symFileList$(printf '\f" a_bold f_cyan "%s" a_reset "' \"$f\")\" && continue; "\
"test -L \"$f\" && test -d \"$f\" && symDirList=\"$symDirList$(printf '\f" a_bold f_cyan "%s" a_reset "' \"$f\"/)\" && continue; "\
"test -x \"$f\" && test -f \"$f\" && execList=\"$execList$(printf '\f" a_bold f_green "%s" a_reset "' \"$f\")\" && continue; "\
"test -f \"$f\" && fileList=\"$fileList$(printf '\f%s' \"$f\")\" && continue; "\
"test -d \"$f\" && dirList=\"$dirList$(printf '\f" a_bold f_blue "%s" a_reset "' \"$f\"/)\" ; "\
"done; "\
"printf '%s' \"$dirList\" \"$symDirList\" \"$fileList\" \"$execList\" \"$symFileList\""
}
code = cmd | getline dirlist
close(cmd)
if (code <= 0) {
dirlist = "empty"
}
else if (dir != "/") {
gsub(/[\\.^$(){}\[\]|*+?]/, "\\\\&", dir) # escape special char
gsub(dir, "", dirlist)
dirlist = substr(dirlist, 2)
}
else {
Narr = split(dirlist, dirlistarr, "\f")
delete dirlistarr[1]
dirlist = ""
for (entry = 2; entry in dirlistarr; entry++) {
sub(/\//, "", dirlistarr[entry])
dirlist = dirlist "\f" dirlistarr[entry]
}
dirlist = substr(dirlist, 2)
}
return dirlist
}
# Credit: https://stackoverflow.com/a/20078022
function isEmpty(arr) { for (idx in arr) return 0; return 1 }
function len(arr, i) { for (idx in arr) { ++i }; return i }
function maxidx(arr, idx) { for (idx in selorder) { pidx = (pidx <= idx ? idx : pidx ) }; return pidx }
##################
# Start of TUI #
##################
function finale() {
clean_preview()
printf "\033\1332J\033\133H" >> "/dev/stderr" # clear screen
printf "\033\133?7h" >> "/dev/stderr" # line wrap
printf "\033\1338" >> "/dev/stderr" # restore cursor
printf "\033\133?25h" >> "/dev/stderr" # show cursor
printf "\033\133?1049l" >> "/dev/stderr" # back from alternate buffer
system("stty isig icanon echo")
ENVIRON["LANG"] = LANG; # restore LANG
}
function init() {
system("stty -isig -icanon -echo")
printf "\033\1332J\033\133H" >> "/dev/stderr" # clear screen
printf "\033\133?1049h" >> "/dev/stderr" # alternate buffer
printf "\033\1337" >> "/dev/stderr" # save cursor
printf "\033\133?25l" >> "/dev/stderr" # hide cursor
printf "\033\1335 q" >> "/dev/stderr" # blinking bar
printf "\033\133?7l" >> "/dev/stderr" # line unwrap
LANG = ENVIRON["LANG"]; # save LANG
ENVIRON["LANG"] = C; # simplest locale setting
}
function CUP(lines, cols) {
printf("\033\133%s;%sH", lines, cols) >> "/dev/stderr"
}
function draw_selected() {
for (sel in selected) {
if (selpage[sel] != curpage || selected[sel] != dir seldisp[sel]) continue
selN = selnum[sel]
CUP(top + (selN-dispnum*(curpage-1))*num - num, 1)
for (i = 1; i <= num; i++) {
printf "\033\1332K" >> "/dev/stderr" # clear line
CUP(top + cursor*num - num + i, 1)
}
CUP(top + (selN-dispnum*(curpage-1))*num - num, 1)
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", seldisp[sel])
if (cursor == selN-dispnum*(curpage-1)) {
printf "%s %s%s%s%s%s", a_clean, a_reverse, f_red, selN ". ", seldisp[sel], a_reset >> "/dev/stderr"
}
else {
printf "%s %s%s%s%s%s", a_clean, a_bold, f_red, selN ". ", seldisp[sel], a_reset >> "/dev/stderr"
}
}
}
function empty_selected() {
split("", selected, ":"); split("", seldisp, ":");
split("", selpage, ":"); split("", selorder, ":")
sellist = ""; order = 0
}
function dim_setup() {
cmd = "stty size"
cmd | getline d
close(cmd)
split(d, dim, " ")
top = 3; bottom = dim[1] - 4;
fin = bottom - ( bottom - (top - 1) ) % num; end = fin + 1;
dispnum = (end - top) / num
}
function menu_TUI_page(list, delim) {
answer = ""; page = 0; split("", pagearr, ":") # delete saved array
dim_setup()
Narr = split(list, disp, delim)
dispnum = (dispnum <= Narr ? dispnum : Narr)
move = int( ( dispnum <= Narr ? dispnum * 0.5 : Narr * 0.5 ) )
# generate display content for each page (pagearr)
for (entry = 1; entry in disp; entry++) {
if ((+entry) % (+dispnum) == 1 || Narr == 1) { # if first item in each page
pagearr[++page] = entry ". " disp[entry]
}
else {
pagearr[page] = pagearr[page] "\n" entry ". " disp[entry]
}
loc = disp[entry]
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", loc)
if (parent != "" && loc == parent) {
cursor = entry - dispnum*(page - 1); curpage = page
}
}
}
function search(list, delim, str, mode) {
find = ""; str = tolower(str);
if (mode == "dir") { regex = "^" str ".*/" }
else if (mode == "begin") {regex = "^" str ".*"}
else { regex = ".*" str ".*" }
gsub(/[(){}\[\]]/, "\\\\&", regex) # escape special char
# get rid of coloring to avoid find irrelevant item
tmplist = list
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", tmplist)
split(list, sdisp, delim); split(tmplist, tmpsdisp, delim)
for (entry = 1; entry in tmpsdisp; entry++) {
match(tolower(tmpsdisp[entry]), regex)
if (RSTART) { find = find delim sdisp[entry]; }
}
slist = substr(find, 2)
return slist
}
function key_collect(list, pagerind) {
key = ""; rep = 0
do {
cmd = "trap 'printf WINCH' WINCH; dd ibs=1 count=1 2>/dev/null"
cmd | getline ans;
close(cmd)
if (++rep == 1) {
srand(); time = srand()
if (time - old_time == 0) { sec++ }
else { sec = 0 }
old_time = time
}
gsub(/[\\^\[\]]/, "\\\\&", ans) # escape special char
# if (ans ~ /.*WINCH/ && pagerind == 0) { # trap SIGWINCH
if (ans ~ /.*WINCH/) { # trap SIGWINCH
cursor = 1; curpage = 1;
if (pagerind == 0) {
menu_TUI_page(list, delim)
redraw(tmsg, bmsg)
}
else if (pagerind == 1) {
printf "\033\1332J\033\133H" >> "/dev/stderr"
dim_setup()
Npager = (Nmsgarr >= dim[1] ? dim[1] : Nmsgarr)
for (i = 1; i <= Npager; i++) {
CUP(i, 1)
printf "%s", msgarr[i] >> "/dev/stderr"
}
}
gsub(/WINCH/, "", ans);
}
if (ans ~ /\033/ && rep == 1) { ans = ""; continue; } # first char of escape seq
else { key = key ans; }
if (key ~ /[^\x00-\x7f]/) { break } # print non-ascii char
if (key ~ /^\\\[5$|^\\\[6$$/) { ans = ""; continue; } # PageUp / PageDown
} while (ans !~ /[\x00-\x5a]|[\x5f-\x7f]/)
# } while (ans !~ /[\006\025\033\003\177[:space:][:alnum:]><\}\{.~\/:!?*+-]|"|[|_$()]/)
return key
}
function cmd_mode(list, answer) {
### comment for scrollable cmd mode:
# |------------b1--------------------b2-------------length(reply)
# b1 to b2 is the show-able region in the whole reply.
# b1 and b2 update according to keyboard inputs.
# keyboard inputs:
# - Left arrow, right arrow, tab completion
cmd_trigger = answer;
cc = 0; dd = 0;
# b1 = 1; b2 = dim[2] - 50; bb = b2 - b1 - 1; curloc = 0;
b1 = 1; b2 = dim[2]; bb = b2 - b1 - 1; curloc = 0;
while (key = key_collect(list, pagerind)) {
if (key == "\003" || key == "\033" || key == "\n") {
split("", comparr, ":")
if (key == "\003" || key == "\033") { reply = "\003"; break } # cancelled
# if Double enter as confirm current path and exit
if (key_last !~ /\t|\[Z/) break
}
if (key == "\177") { # backspace
reply = substr(reply, 1, length(reply) + cc - 1) substr(reply, length(reply) + cc + 1);
if (length(reply) + cc < b1 && b1 > 1) { b1 = b1 - 1; b2 = b1 + bb; }
else if (curloc > 1) { curloc--; }
split("", comparr, ":")
}
# path completion: $HOME
else if (cmd_trigger reply ~ /:cd |:.* / && key == "~") { reply = reply ENVIRON["HOME"] "/" }
# path completion
else if (cmd_trigger reply ~ /:cd .*|:.* \.?\.?\// && key ~ /\t|\[Z/) { # Tab / Shift-Tab
cc = 0; dd = 0;
if (isEmpty(comparr)) {
comp = reply;
if (cmd_trigger reply ~ /:cd .*/) gsub(/cd /, "", comp)
else {
if (comp ~ /.* \.\.\//) {
match(comp, /.* \.\.\//)
cmd_run = substr(comp, RSTART, RLENGTH-3)
comp = substr(comp, RLENGTH-2)
}
if (comp ~ /.* \.\//) {
match(comp, /.* \.\//)
cmd_run = substr(comp, RSTART, RLENGTH-2)
comp = substr(comp, RLENGTH-1)
}
if (comp ~ /.* \//) {
match(comp, /.* \//)
cmd_run = substr(comp, RSTART, RLENGTH-1)
comp = substr(comp, RLENGTH)
}
}
compdir = comp;
if (compdir ~ /^\.\.\/.*/) {
tmpdir = dir
while (compdir ~ /^\.\.\/.*/) { # relative path
gsub(/[^\/]*\/?$/, "", tmpdir)
gsub(/^\.\.\//, "", compdir)
tmpdir = ( tmpdir == "" ? "/" : tmpdir )
}
compdir = tmpdir
}
else {
# allow single Enter to confirm current reply
# as completion dir (compdir)
if (key_last ~ /~|\n/) {
comp = ""
}
else {
gsub(/[^\/]*\/?$/, "", compdir);
gsub(compdir, "", comp)
}
}
compdir = (compdir == "" ? dir : compdir);
tmplist = gen_content(compdir, 1)
complist = ( cmd_trigger reply ~ /:cd .*/ ?
search(tmplist, delim, comp, "dir") :
search(tmplist, delim, comp, "begin") )
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", complist)
Ncomp = split(complist, comparr, delim)
## save space for completion
space = length(comparr[1])
for (item in comparr) {
space = ( space < length(comparr[item]) ? length(comparr[item]) : space )
}
if (length(compdir) + space > b2) { b2 = b2 + space; b1 = b2 - bb; }
c = ( key == "\t" ? 1 : Ncomp )
}
else {
if (key == "\t") c = (c == Ncomp ? 1 : c + 1)
else c = (c == 1 ? Ncomp : c - 1)
}
if (cmd_trigger reply ~ /:cd .*/) reply = "cd " compdir comparr[c]
else reply = cmd_run compdir comparr[c]
CUP(dim[1] - 2, 1)
printf("%s%s%s%s%s", a_clean, a_reverse, f_yellow, "looping through completion", a_reset)
}
# command completion
else if (cmd_trigger == ":" && key ~ /\t|\[Z/) {
if (isEmpty(comparr)) {
getline cmdhist < CMDHIST; close(CMDHIST);
comp = reply;
complist = search(cmdhist, "\n", comp, "begin")
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", complist)
Ncomp = split(complist, comparr, "\n")
c = ( key == "\t" ? 1 : Ncomp )
}
else {
if (key == "\t") c = (c == Ncomp ? 1 : c + 1)
else c = (c == 1 ? Ncomp : c - 1)
}
reply = comparr[c]
CUP(dim[1] - 2, 1)
printf("%s%s%s%s%s", a_clean, a_reverse, f_yellow, "looping through completion", a_reset)
}
else if (cmd_trigger == ":" && key ~ /\[A|\[B/) {
getline cmdhist < CMDHIST; close(CMDHIST);
Ncmd = split(cmdhist, cmdarr, "\n")
reply = cmdarr[Ncmd - dd]
if (key ~ /\[A/) { dd = (dd < Ncmd - 1 ? dd + 1 : dd) }
if (key ~ /\[B/) { dd = (dd == 0 ? dd : dd - 1) }
}
# search
else if (cmd_trigger == "/" && key ~ /\t|\[Z/) {
cc = 0; dd = 0;
if (isEmpty(comparr)) {
comp = reply; complist = search(list, delim, comp, "begin")
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", complist)
Ncomp = split(complist, comparr, delim)
c = ( key == "\t" ? 1 : Ncomp )
}
else {
if (key == "\t") c = (c == Ncomp ? 1 : c + 1)
else c = (c == 1 ? Ncomp : c - 1)
}
reply = comparr[c]
}
else if (key ~ /\[C/) { # Right arrow
if (cc < 0) {
cc++
if (length(reply) + cc > b2 && b2 < length(reply)) { b2 = b2 + 1; b1 = b2 - bb; }
else if (curloc < bb) { curloc++; }
}
}
else if (key ~ /\[D/) { # Left arrow
if (-cc < length(reply)) {
cc--
if (length(reply) + cc < b1 && b1 > 1) { b1 = b1 - 1; b2 = b1 + bb; }
else if (curloc > 1) { curloc--; }
}
}
# single Enter clear the completion array (comparr)
else if (key ~ /\n/) {
CUP(dim[1] - 2, 1)
printf("%s%s%s%s%s", a_clean, a_reverse, f_yellow, "confirm current completion", a_reset)
curloc = ( length(reply) > bb ? bb : length(reply) )
split("", comparr, ":")
}
# Reject other escape sequence
else if (key ~ /\[.+/) {
continue
}
else {
reply = substr(reply, 1, length(reply) + cc) key substr(reply, length(reply) + cc + 1);
if (length(reply) + cc > b2) { b2 = b2 + 1; b1 = b2 - bb }
else if (curloc < bb) { curloc++; }
split("", comparr, ":")
}
if (cmd_trigger == "/") {
slist = search(list, delim, reply, "")
for (i = top; i <= end; i++) {
CUP(i, 1)
printf "\033\133K" >> "/dev/stderr" # clear line
}
if (slist != "") {
Nsarr = split(slist, sarr, delim)
Nsarr = (Nsarr > dispnum ? dispnum : Nsarr)
for (i = 1; i <= Nsarr; i++) {
CUP(i + 2, 1)
printf "%d. %s", i, sarr[i] >> "/dev/stderr"
}
}
}
if (cmd_trigger ~ /^[[:digit:]]$/) {
status = sprintf("%sChoose [%s1-%d%s], current page num is %s%d%s, total page num is %s%d%s: %s%s", a_clean, a_bold, Narr, a_reset, a_bold, curpage, a_reset, a_bold, page, a_reset, cmd_trigger, reply)
if (cmd_trigger reply ~ /^[[:digit:]]+[Gjk]$/) { split("", comparr, ":"); break; }
}
else {
# status = sprintf("%s%s%s", a_clean, cmd_trigger, reply)
status = sprintf("%s%s%s", a_clean, cmd_trigger, substr(reply, b1, bb))
}
CUP(dim[1], 1)
# printf(status) >> "/dev/stderr"
# if (cc < 0) { CUP(dim[1], length(status) + cc - 3) } # adjust cursor
# printf(status ", " b1 ", " b2 ", " curloc ", " cc ", " length(reply) ", " space) >> "/dev/stderr"
printf(status) >> "/dev/stderr"
if (cc < 0) { CUP(dim[1], curloc + 2) } # adjust cursor
key_last = key
}
}
function yesno(command) {
CUP(dim[1], 1)
printf("%s%s %s? (y/n) ", a_clean, "Really execute command", command) >> "/dev/stderr"
printf "\033\133?25h" >> "/dev/stderr" # show cursor
key = key_collect(list, pagerind)
printf "\033\133?25l" >> "/dev/stderr" # hide cursor
if (key ~ /[Yy]/) return 1
}
function redraw(tmsg, bmsg) {
printf "\033\1332J\033\133H" >> "/dev/stderr" # clear screen and move cursor to 0, 0
CUP(top, 1); print pagearr[curpage] >> "/dev/stderr"
CUP(top + cursor*num - num, 1); printf "%s%s%s%s", Ncursor ". ", a_reverse, disp[Ncursor], a_reset >> "/dev/stderr"
CUP(top - 2, 1); print tmsg >> "/dev/stderr"
CUP(dim[1] - 2, 1); print bmsg >> "/dev/stderr"
CUP(dim[1], 1)
# printf "Choose [\033\1331m1-%d\033\133m], current page num is \033\133;1m%d\033\133m, total page num is \033\133;1m%d\033\133m: ", Narr, curpage, page >> "/dev/stderr"
printf "%sChoose [%s1-%d%s], current page num is %s%d%s, total page num is %s%d%s: ", a_clean, a_bold, Narr, a_reset, a_bold, curpage, a_reset, a_bold, page, a_reset >> "/dev/stderr"
if (bmsg !~ /Action.*|Selecting\.\.\./ && ! isEmpty(selected)) draw_selected()
if (bmsg !~ /Action.*|Selecting\.\.\./ && PREVIEW == 1) draw_preview(disp[Ncursor])
}
function menu_TUI(list, delim, num, tmsg, bmsg) {
menu_TUI_page(list, delim)
while (answer !~ /^[[:digit:]]+$|\.\.\//) {
oldCursor = 1;
## calculate cursor and Ncursor
cursor = ( cursor+dispnum*(curpage-1) > Narr ? Narr - dispnum*(curpage-1) : cursor )
Ncursor = cursor+dispnum*(curpage-1)
clean_preview()
redraw(tmsg, bmsg)
while (1) {
answer = key_collect(list, pagerind)
#######################################
# Key: entry choosing and searching #
#######################################
if ( answer ~ /^[[:digit:]]$/ || answer == "/" || answer == ":" ) {
CUP(dim[1], 1)
if (answer ~ /^[[:digit:]]$/) {
# printf "Choose [\033\1331m1-%d\033\133m], current page num is \033\133;1m%d\033\133m, total page num is \033\133;1m%d\033\133m: %s", Narr, curpage, page, answer >> "/dev/stderr"
printf "%sChoose [%s1-%d%s], current page num is %s%d%s, total page num is %s%d%s: %s", a_clean, a_bold, Narr, a_reset, a_bold, curpage, a_reset, a_bold, page, a_reset, answer >> "/dev/stderr"
}
else {
printf "%s%s", a_clean, answer >> "/dev/stderr" # clear line
}
printf "\033\133?25h" >> "/dev/stderr" # show cursor
cmd_mode(list, answer)
printf "\033\133?25l" >> "/dev/stderr" # hide cursor
if (reply == "\003") { answer = ""; key = ""; reply = ""; break; }
answer = cmd_trigger reply; reply = ""; split("", comparr, ":"); cc = 0; dd = 0;
## cd
if (answer ~ /:cd .*/) {
old_dir = dir
gsub(/:cd /, "", answer)
if (answer ~ /^\/.*/) { # full path
dir = ( answer ~ /.*\/$/ ? answer : answer "/" )
}
else {
while (answer ~ /^\.\.\/.*/) { # relative path
gsub(/[^\/]*\/?$/, "", dir)
gsub(/^\.\.\//, "", answer)
dir = ( dir == "" ? "/" : dir )
}
dir = ( answer ~ /.*\/$/ || answer == "" ? dir answer : dir answer "/" )
}
# empty_selected()
tmplist = gen_content(dir, HIDDEN)
if (tmplist == "empty") {
dir = old_dir
# bmsg = sprintf("\033\13338;5;15m\033\13348;5;9m%s\033\133m", "Error: Path Not Exist")
bmsg = sprintf("%s%s%s%s", b_red, f_white, "Error: Path Not Exist", a_reset)
}
else {
list = tmplist
}
menu_TUI_page(list, delim)
tmsg = dir;
cursor = 1; curpage = (+curpage > +page ? page : curpage);
break
}
## cmd mode
if (answer ~ /:[^[:cntrl:]*]/) {
command = substr(answer, 2)
savecmd = command; post = ""
match(command, /\{\}/)
if (RSTART) {
post = substr(command, RSTART+RLENGTH+1);
command = substr(command, 1, RSTART-2)
}
if (command ~ /.*\$@.*/) {
idx = maxidx(selorder)
for (j = 1; j <= idx; j++) {
if (selorder[j] == "") continue
sellist = sellist " \"" selected[selorder[j]] "\" "
}
gsub(/\$@/, sellist, command)
empty_selected()
}
if (command in cmdalias) { command = cmdalias[command] }
if (command ~ /^rm$|rm .*/) { suc = yesno(command); if (suc == 0) break }
gsub(/["]/, "\\\\&", command) # escape special char
finale()
if (isEmpty(selected)) {
# code = system("cd \"" dir "\" && eval \"" command "\" 2>/dev/null")
code = system("cd \"" dir "\" && eval \"" command "\"")
}
else {
idx = maxidx(selorder)
for (j = 1; j <= idx; j++) {
if (selorder[j] == "") continue
sel = selorder[j]
match(post, /\{\}/)
if (RSTART) {
post = substr(post, 1, RSTART-1) selected[sel] substr(post, RSTART+RLENGTH)
}
if (post) {
# code = system("cd \"" dir "\" && eval \"" command " \\\"" selected[sel] "\\\" \\\"" post "\\\"\" 2>/dev/null")
code = system("cd \"" dir "\" && eval \"" command " \\\"" selected[sel] "\\\" \\\"" post "\\\"\"")
}
else {
# code = system("cd \"" dir "\" && eval \"" command " \\\"" selected[sel] "\\\"\" 2>/dev/null")
code = system("cd \"" dir "\" && eval \"" command " \\\"" selected[sel] "\\\"\"")
}
}
empty_selected()
}
init()
list = gen_content(dir, HIDDEN); tmsg = dir;
menu_TUI_page(list, delim)
if (code > 0) { printf("\n%s", savecmd) >> CMDHIST; close(CMDHIST) }
break
}
## search
if (answer ~ /\/[^[:cntrl:]*]/) {
slist = search(list, delim, substr(answer, 2), "")
if (slist != "") {
menu_TUI_page(slist, delim)
cursor = 1; curpage = 1; sind = 1
}
break
}
## go to page
if (answer ~ /[[:digit:]]+G$/) {
ans = answer; gsub(/G/, "", ans);
curpage = (+ans <= +page ? ans : page)
break
}
if (answer ~ /[[:digit:]]+$/) {
if (+answer > +Narr) answer = Narr
if (+answer < 1) answer = 1
curpage = answer / dispnum
curpage = sprintf("%.0f", (curpage == int(curpage)) ? curpage : int(curpage)+1)
cursor = answer - dispnum*(curpage-1); answer = ""
break
}
}
if (answer ~ /[?]/) { pager(help); break; }
if (answer == "!") {
finale()
system("cd \"" dir "\" && ${SHELL:=/bin/sh}")
init()
list = gen_content(dir, HIDDEN)
menu_TUI_page(list, delim)
break
}
if (answer == "-") {
if (old_dir == "") break
TMP = dir; dir = old_dir; old_dir = TMP;
list = gen_content(dir, HIDDEN)
menu_TUI_page(list, delim)
tmsg = dir; bmsg = "Browsing"
cursor = 1; curpage = (+curpage > +page ? page : curpage);
break
}
########################
# Key: Total Redraw #
########################
if ( answer == "v" ) { PREVIEW = (PREVIEW == 1 ? 0 : 1); break }
if ( answer == ">" ) { RATIO = (RATIO > 0.8 ? RATIO : RATIO + 0.05); break }
if ( answer == "<" ) { RATIO = (RATIO < 0.2 ? RATIO : RATIO - 0.05); break }
if ( answer == "r" || answer == "." ||
( answer == "h" && ( bmsg == "Actions" || sind == 1 ) ) ||
( answer ~ /^[[:digit:]]$/ && (+answer > +Narr || +answer < 1 ) ) ) {
if (answer == ".") { HIDDEN = (HIDDEN == 1 ? 0 : 1); }
list = gen_content(dir, HIDDEN)
delim = "\f"; num = 1; tmsg = dir; bmsg = "Browsing"; sind = 0; openind = 0;
menu_TUI_page(list, delim)
empty_selected()
cursor = 1; curpage = (+curpage > +page ? page : curpage);
break
}
if ( answer == "\n" || answer == "l" || answer ~ /\[C/ ) { answer = Ncursor; break }
if ( answer == "a" ) {
menu_TUI_page(action, RS)
tmsg = "Choose an action"; bmsg = "Actions"
cursor = 1; curpage = 1;
break
}
if ( answer ~ /q|\003/ ) exit
if ( (answer == "h" || answer ~ /\[D/) && dir != "/" ) { answer = "../"; disp[answer] = "../"; bmsg = ""; break }
if ( (answer == "h" || answer ~ /\[D/) && dir = "/" ) continue
if ( (answer == "n" || answer ~ /\[6~/) && +curpage < +page ) { curpage++; break }
if ( (answer == "n" || answer ~ /\[6~/) && +curpage == +page && cursor != Narr - dispnum*(curpage-1) ) { cursor = ( +curpage == +page ? Narr - dispnum*(curpage-1) : dispnum ); break }
if ( (answer == "n" || answer ~ /\[6~/) && +curpage == +page && cursor == Narr - dispnum*(curpage-1) ) continue
if ( (answer == "p" || answer ~ /\[5~/) && +curpage > 1) { curpage--; break }
if ( (answer == "p" || answer ~ /\[5~/) && +curpage == 1 && cursor != 1 ) { cursor = 1; break }
if ( (answer == "p" || answer ~ /\[5~/) && +curpage == 1 && cursor == 1) continue
if ( (answer == "g" || answer ~ /\[H/) && ( curpage != 1 || cursor != 1 ) ) { curpage = 1; cursor = 1; break }
if ( (answer == "g" || answer ~ /\[H/) && curpage = 1 && cursor == 1 ) continue
if ( (answer == "G" || answer ~ /\[F/) && ( curpage != page || cursor != Narr - dispnum*(curpage-1) ) ) { curpage = page; cursor = Narr - dispnum*(curpage-1); break }
if ( (answer == "G" || answer ~ /\[F/) && curpage == page && cursor = Narr - dispnum*(curpage-1) ) continue
#########################
# Key: Partial Redraw #
#########################
if ( (answer == "j" || answer ~ /\[B/) && +cursor <= +dispnum ) { oldCursor = cursor; cursor++; }
if ( (answer == "j" || answer ~ /\[B/) && +cursor > +dispnum && +curpage < +page && +page > 1 ) { cursor = 1; curpage++; break }
if ( (answer == "k" || answer ~ /\[A/) && +cursor == 1 && +curpage > 1 && +page > 1 ) { cursor = dispnum; curpage--; break }
if ( (answer == "k" || answer ~ /\[A/) && +cursor > 1 ) { oldCursor = cursor; cursor--; }
if ( (answer == "\006") && cursor <= +dispnum ) { oldCursor = cursor; cursor = cursor + move }
if ( (answer == "\006") && +cursor > +dispnum && +curpage < +page && +page > 1 ) { cursor = cursor - dispnum; curpage++; break }
if ( (answer == "\006") && +cursor > Narr - dispnum*(curpage-1) && +curpage == +page ) { cursor = ( +curpage == +page ? Narr - dispnum*(curpage-1) : dispnum ); break }
if ( (answer == "\006") && +cursor == Narr - dispnum*(curpage-1) && +curpage == +page ) break
if ( (answer == "\025") && cursor >= 1 ) { oldCursor = cursor; cursor = cursor - move }
if ( (answer == "\025") && +cursor < 1 && +curpage > 1 ) { cursor = dispnum + cursor; curpage--; break }
if ( (answer == "\025") && +cursor < 1 && +curpage == 1 ) { cursor = 1; break }
if ( (answer == "\025") && +cursor == 1 && +curpage == 1 ) break
if ( answer == "H" ) { oldCursor = cursor; cursor = 1; }
if ( answer == "M" ) { oldCursor = cursor; cursor = ( +curpage == +page ? int((Narr - dispnum*(curpage-1))*0.5) : int(dispnum*0.5) ); }
if ( answer == "L" ) { oldCursor = cursor; cursor = ( +curpage == +page ? Narr - dispnum*(curpage-1) : dispnum ); }
####################
# Key: Selection #
####################
if ( answer == " " ) {
if (selected[dir,Ncursor] == "") {
TMP = disp[Ncursor];
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", TMP)
selected[dir,Ncursor] = dir TMP;
seldisp[dir,Ncursor] = TMP;
selpage[dir,Ncursor] = curpage;
selnum[dir,Ncursor] = Ncursor;
selorder[++order] = dir SUBSEP Ncursor
bmsg = disp[Ncursor] " selected"
}
else {
for (idx in selorder) { if (selorder[idx] == dir SUBSEP Ncursor) { delete selorder[idx]; break } }
delete selected[dir,Ncursor];
delete seldisp[dir,Ncursor];
delete selpage[dir,Ncursor];
delete selnum[dir,Ncursor];
bmsg = disp[Ncursor] " cancelled"
}
if (+Narr == 1) { break }
if (+cursor <= +dispnum || +cursor <= +Narr) { cursor++ }
if (+cursor > +dispnum || +cursor > +Narr) { cursor = 1; curpage = ( +curpage == +page ? 1 : curpage + 1 ) }
break
}
if (answer == "S") {
if (isEmpty(selected)) {
selp = 0
for (entry = 1; entry in disp; entry++) {
TMP = disp[entry];
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", TMP)
if (TMP != "./" && TMP != "../") {
selected[dir,entry] = dir TMP;
seldisp[dir,entry] = TMP;
selpage[dir,entry] = ((+entry) % (+dispnum) == 1 ? ++selp : selp)
selnum[dir,entry] = entry;
selorder[++order] = dir SUBSEP entry
}
}
bmsg = "All selected"
}
else {
empty_selected()
bmsg = "All cancelled"
}
break
}
if (answer == "s") {
# for (sel in selected) {
# selcontent = selcontent "\n" selected[sel]
# }
idx = maxidx(selorder)
for (j = 1; j <= idx; j++) {
if (selorder[j] == "") continue
selcontent = selcontent "\n" j ". " selected[selorder[j]]
}
pager("Selected item: \n" selcontent); selcontent = ""; break;
}
####################################################################
# Partial redraw: tmsg, bmsg, old entry, new entry, and selected #
####################################################################
Ncursor = cursor+dispnum*(curpage-1); oldNcursor = oldCursor+dispnum*(curpage-1);
if (Ncursor > Narr) { Ncursor = Narr; cursor = Narr - dispnum*(curpage-1); continue }
if (Ncursor < 1) { Ncursor = 1; cursor = 1; continue }
CUP(dim[1] - 2, 1); # bmsg
printf a_clean >> "/dev/stderr" # clear line
print bmsg >> "/dev/stderr"
CUP(top + oldCursor*num - num, 1); # old entry
for (i = 1; i <= num; i++) {
printf a_clean >> "/dev/stderr" # clear line
CUP(top + oldCursor*num - num + i, 1)
}
CUP(top + oldCursor*num - num, 1);
printf "%s", oldNcursor ". " disp[oldNcursor] >> "/dev/stderr"
CUP(top + cursor*num - num, 1); # new entry
for (i = 1; i <= num; i++) {
printf a_clean >> "/dev/stderr" # clear line
CUP(top + cursor*num - num + i, 1)
}
CUP(top + cursor*num - num, 1);
printf "%s%s%s%s", Ncursor ". ", a_reverse, disp[Ncursor], a_reset >> "/dev/stderr"
if (bmsg !~ /Action.*|Selecting\.\.\./ && ! isEmpty(selected)) draw_selected()
if (bmsg !~ /Action.*|Selecting\.\.\./ && PREVIEW == 1) draw_preview(disp[Ncursor])
}
}
result[1] = disp[answer]
result[2] = bmsg
}
function pager(msg) { # pager to print out stuff and navigate
printf "\033\1332J\033\133H" >> "/dev/stderr"
if (PREVIEW == 1) { printf "{\"action\": \"remove\", \"identifier\": \"PREVIEW\"}\n" > FIFO_UEBERZUG; close(FIFO_UEBERZUG) }
Nmsgarr = split(msg, msgarr, "\n")
Npager = (Nmsgarr >= dim[1] ? dim[1] : Nmsgarr)
for (i = 1; i <= Npager; i++) {
CUP(i, 1)
printf "%s", msgarr[i] >> "/dev/stderr"
}
pagerind = 1;
while (key = key_collect(list, pagerind)) {
if (key == "\003" || key == "\033" || key == "q" || key == "h") break
if ((key == "j" || key ~ /\[B/) && i < Nmsgarr) { printf "\033\133%d;H\n", Npager >> "/dev/stderr"; printf msgarr[i++] >> "/dev/stderr" }
if ((key == "k" || key ~ /\[A/) && i > dim[1] + 1) { printf "\033\133H\033\133L" >> "/dev/stderr"; i--; printf msgarr[i-dim[1]] >> "/dev/stderr" }
}
pagerind = 0;
}
######################
# Start of Preview #
######################
function draw_preview(item) {
border = int(dim[2]*RATIO) # for preview
# clear RHS of screen based on border
clean_preview()
gsub(/\033\[[0-9][0-9]m|\033\[[0-9]m|\033\[m/, "", item)
path = dir item
if (path ~ /.*\/$/) { # dir
content = gen_content(path)
split(content, prev, "\f")
for (i = 1; i <= ((end - top) / num); i++) {
CUP(top + i - 1, border + 1)
print prev[i] >> "/dev/stderr"
}
}
else { # Standard file
if (FMAWK_PREVIEWER == "") {
cmd = "file " path
cmd | getline props
if (props ~ /text/) {
getline content < path
close(path)
split(content, prev, "\n")
for (i = 1; i <= ((end - top) / num); i++) {
CUP(top + i - 1, border + 1)
code = gsub(/\000/, "", prev[i])
if (code > 0) {
# printf "\033\13338;5;0m\033\13348;5;15m%s\033\133m", "binary" >> "/dev/stderr"
printf "%s%s%s%s", a_reverse, f_white, "binary", a_reset >> "/dev/stderr"
break
}
print prev[i] >> "/dev/stderr"
}
}
}
else {
system(FMAWK_PREVIEWER " \"" path "\" \"" CACHE "\" \"" border+1 "\" \"" ((end - top)/num) "\" \"" top "\" \"" dim[2]-border-1 "\"")
}
}
}
function clean_preview() {
for (i = top; i <= end; i++) {
CUP(i, border - 1)
printf "\033\133K" >> "/dev/stderr" # clear line
}
if (FIFO_UEBERZUG == "") return
printf "{\"action\": \"remove\", \"identifier\": \"PREVIEW\"}\n" > FIFO_UEBERZUG
close(FIFO_UEBERZUG)
}