#!/usr/bin/awk 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) }