Advertisement
Guest User

ttycomfy

a guest
Jan 27th, 2020
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 27.02 KB | None | 0 0
  1. #!/bin/bash
  2. readv() { IFS='' read -rd '' $1; }
  3.  
  4. readv LICENSE << 'EOT'
  5.  
  6.  
  7.  
  8.     ttycomfy v0.2.0
  9.     Copyright (c) 2020 Benjamin Isaac McCann
  10.     ---D1TW Fair Distribution License v0.7.0---
  11.     1)
  12.     The Licensor, henceforth the "Original Publisher,"
  13.     of the work hereby provided as Consideration,
  14.     henceforth the "Original Work," hereby grants any person,
  15.     henceforth an "Original Recipient," unrestricted permission
  16.     to use, copy, and/or produce private derivative works
  17.     of the Original Work, without being charged royalties.
  18.     2)
  19.     The Original Publisher hereby grants all Original Recipients
  20.     limited permission to publish and/or sell copies and/or derivative
  21.     works of the Original Work, without being charged royalties,
  22.     subject to the following restrictions:
  23.     2.a)
  24.     The Original Publisher hereby reserves the exclusive right
  25.     to relicense or sublicense the Original Work.
  26.     2.b)
  27.     Pursuant to paragraph 2.a, the Original Publisher hereby denies
  28.     the publisher, henceforth a "Sub-Publisher," of any copy
  29.     or derivative work, henceforth a "Sub-Publication,"
  30.     of the Original Work, the permission to sublicense any portion
  31.     of the Original Work, whether in distributing their Sub-Publication
  32.     or otherwise.
  33.     2.b.i)
  34.     Pursuant to paragraph 2.b, a Sub-Publisher shall not misconstrue,
  35.     explicitly or otherwise, any portion of the Original Work
  36.     as being a consideration to any license agreement applicable
  37.     to the Sub-Publisher's own Sub-Publication.
  38.     2.b.ii)
  39.     Pursuant to paragraph 2.b.i, in order to avoid so misconstruing
  40.     any such portion of the Original Work, the Sub-Publisher shall
  41.     explicitly disclaim from the license agreement applicable
  42.     to the Sub-Publication all portions of the Original Work,
  43.     and shall provide, separately, a verbatim copy of the text
  44.     of this license agreement, accompanied by a written statement
  45.     identifying said text as applicable to said portions
  46.     of the Original Work, and identifying the Original Publisher
  47.     as the Licensor of said portions.
  48.     2.b.iii)
  49.     Pursuant to paragraph 2.b, a Sub-Publisher shall respect,
  50.     and not modify or elaborate upon, the permissions and restrictions
  51.     which paragraph 1 has already extended to any potential recipient
  52.     of the Sub-Publication, henceforth a "Sub-Recipient," with respect
  53.     to the Sub-Recipient's use of any portion of the Original Work,
  54.     including any portion provided to the Sub-Recipient
  55.     via the Sub-Publication.
  56.     3)
  57.     Any license formed in violation of paragraphs 2.a through 2.b.iii,
  58.     henceforth a "Noncompliant Sub-Publication License," is hereby
  59.     null and void, insofar as it concerns, explicitly or otherwise,
  60.     any portion of the Original Work. Insofar as the Original Work
  61.     is not concerned, this license agreement shall not be taken
  62.     to invalidate the Noncompliant Sub-Publication License in question;
  63.     that is, only those portions concerning the Original Work
  64.     are invalidated.
  65.     3.a)
  66.     Any Sub-Publisher acting in violation of paragraphs
  67.     2.a through 2.b.iii shall be the sole liable party to any and all
  68.     civil disputes brought by the Sub-Publisher, at the Sub-Publisher's
  69.     behest, or on the Sub-Publisher's consenting behalf, on the grounds
  70.     that the Sub-Recipient has violated any null and void portion
  71.     (pursuant to paragraph 3) of the Noncompliant Sub-Publication License
  72.     under which the Sub-Publication in question is provided. If any such
  73.     dispute is brought on the Sub-Publisher's behalf, but without
  74.     their consent, whoever brought the dispute shall be the sole
  75.     liable party.
  76.     4)
  77.     In paragraphs 1 and 2, "without being charged royalties" shall not
  78.     be understood to excuse an Original Recipient from paying a flat
  79.     compensation fee charged by the Original Publisher prior
  80.     to the formation of this license agreement, nor from paying
  81.     any fees for optional services or privileges beyond those already
  82.     guaranteed in paragraphs 1 and 2.
  83.     5)
  84.     The Original Work is provided "as is," without warranty of any kind,
  85.     express or implied, including but not limited to the warranties
  86.     of merchantability, fitness for a particular purpose,
  87.     and noninfringement. In no event shall the Original Publisher
  88.     be liable for any claim, damages, or other liability,
  89.     whether in an action of contract, tort, or otherwise,
  90.     arising from, out of, or in connection with the Original Work
  91.     or the use or other dealings in the Original Work.
  92.     6)
  93.     Pursuant to paragraph 2.b.ii, the Licensor is required
  94.     to inform the Licensee as now they shall: namely, that this specific
  95.     license agreement does not cover as Consideration the text
  96.     of the license agreement itself, which is licensed separately,
  97.     but under the same terms. In this regard, the author, and licensor,
  98.     of the text of this license agreement, is one Benjamin Isaac McCann,
  99.     born in California in the United States in the year 1994,
  100.     having authored the text of this license agreement in the year 2020,
  101.     and having licensed it under itself to the general public.
  102.  
  103.  
  104.  
  105. EOT
  106.  
  107. catv() {
  108.     if [[ "$1" == '-i' ]]; then
  109.         echo "${!2}"
  110.     else
  111.         local line blanks
  112.         echo "${!1}" | while read -r line; do
  113.             if [[ "${line}" == '' ]]; then
  114.                 if [[ "${blanks}" != '' ]]; then
  115.                     blanks=$((blanks + 1))
  116.                 fi
  117.             else
  118.                 if [[ "${blanks}" != '' ]]; then
  119.                     while [[ blanks -gt 0 ]]; do
  120.                         echo
  121.                         blanks=$((blanks - 1))
  122.                     done
  123.                 fi
  124.                 echo "${line}"
  125.                 blanks=0
  126.             fi
  127.         done
  128.     fi
  129. }
  130.  
  131. terminit() {
  132.     clear
  133.     tput rmam
  134.     tput civis
  135.     stty -echo
  136. }
  137.  
  138. termcleanup() {
  139.     clear
  140.     tput smam
  141.     tput cnorm
  142.     stty echo
  143. }
  144.  
  145. getkey() {
  146.     local key full
  147.     if IFS= read -rsn1 key; then while true; do
  148.         full+=$(echo -n "${key}" | cat -v)
  149.         if IFS= read -rst0 key; then IFS= read -rsn1 key
  150.         else
  151.             echo "${full}"
  152.             [[ "${full}" != "^D" ]]
  153.             return $?
  154.         fi
  155.     done; else
  156.         return 1
  157.     fi
  158. }
  159.  
  160. # =====================
  161. # LINE BUFFER FUNCTIONS
  162. # =====================
  163.  
  164. TERM_WIDTH=$(tput cols)
  165. TERM_HEIGHT=$(($(tput lines) - 2))
  166. LINE_BUFFER=()
  167. LINE_BUFFER_SIZE=
  168. LINE_BUFFER_SCROLL=0
  169. LINE_BUFFER_ROW_INDEX=0
  170. LINE_BUFFER_COLUMN_INDEX=0
  171. LINE_BUFFER_DISPLAY_HEIGHT=$(($TERM_HEIGHT*3/4))
  172. LINE_BUFFER_DISPLAY_ROW_OFFSET=$(($TERM_HEIGHT/5))
  173. LINE_BUFFER_DISPLAY_COLUMN_OFFSET=$(($TERM_WIDTH/12))
  174. LINE_BUFFER_NEEDS_REDRAW=true
  175. LINE_BUFFER_ROW_SELECTED_SGR="\e[44m\e[37m"
  176. LINE_BUFFER_COLUMN_SELECTED_SGR="\e[47m\e[34m"
  177. LINE_BUFFER_DISABLE_COLUMNS=false
  178.  
  179. repositionlinebuf() {
  180.     #echo "repositionlinebuf start" 1>&2
  181.     # get variables and parameters
  182.     local -n buf=LINE_BUFFER
  183.     local -n count=LINE_BUFFER_SIZE
  184.     local -n scroll=LINE_BUFFER_SCROLL
  185.     local -n row=LINE_BUFFER_ROW_INDEX
  186.     local -n col=LINE_BUFFER_COLUMN_INDEX
  187.     local -n height=LINE_BUFFER_DISPLAY_HEIGHT
  188.     local -n firstrow=LINE_BUFFER_DISPLAY_ROW_OFFSET
  189.     local -n firstcol=LINE_BUFFER_DISPLAY_COLUMN_OFFSET
  190.     local -n needredraw=LINE_BUFFER_NEEDS_REDRAW
  191.     local -n rowselsgr=LINE_BUFFER_ROW_SELECTED_SGR
  192.     local -n colselsgr=LINE_BUFFER_COLUMN_SELECTED_SGR
  193.     local -n nocolumn=LINE_BUFFER_DISABLE_COLUMNS
  194.     local width=$(($TERM_WIDTH - $firstcol))
  195.     # update size
  196.     count=${#buf[@]}
  197.     # parse options
  198.     local drow dcol
  199.     local absolute=false
  200.     #echo "parsing options" 1>&2
  201.     while [[ ${1:0:2} == '--' ]]; do
  202.         case "$1" in
  203.         '--absolute')   absolute=true ;;
  204.         '--nocolumn')   nocolumn=true ;;
  205.         '--rowselsgr')  shift; rowselsgr=$1 ;;
  206.         '--colselsgr')  shift; colselsgr=$1 ;;
  207.         esac
  208.         shift
  209.     done
  210.     if $absolute; then
  211.         drow=$(($1 - $row))
  212.         dcol=$(($2 - $col))
  213.     else
  214.         drow=$1
  215.         dcol=$2
  216.     fi
  217.     #echo "calculation and bounds-checking" 1>&2
  218.     # bounds-check display height
  219.     #if [[ $height -gt $count ]]; then
  220.     #    height=$count
  221.     #fi
  222.     # bounds-check deltas
  223.     if [[ $drow == '' ]]; then drow=0; fi
  224.     if [[ $dcol == '' ]]; then dcol=0; fi
  225.     # declare calculation variables
  226.     local newrow newcol scrollbottom newscroll newscrollbottom
  227.     # don't have to do calculations if no movement
  228.     if [[ ${drow}${dcol} == 00 ]]; then
  229.         newrow=$row
  230.         newcol=$col
  231.         newscroll=$scroll
  232.         scrollbottom=$(($scroll + $height - 1))
  233.         newscrollbottom=$scrollbottom
  234.     else
  235.         # calculate initial new coords
  236.         local newrow=$(($row + $drow))
  237.         local newcol=$(($col + $dcol))
  238.         # bounds-check new coords
  239.         #echo "bounds-check loop" 1>&2
  240.         while true; do
  241.             # past last line -> lower-right
  242.             if [[ $newrow -ge ${#buf[@]} ]]; then
  243.                 newrow=$((${#buf[@]} - 1))
  244.                 newcol=${#buf[newrow]}
  245.                 break
  246.             # before first line -> upper-left
  247.             elif [[ $newrow -lt 0 ]]; then
  248.                 newrow=0
  249.                 newcol=0
  250.                 break
  251.             # past last column -> next line
  252.             elif [[ $newcol -gt ${#buf[newrow]} ]]; then
  253.                 newcol=0
  254.                 newrow=$(($newrow + 1))
  255.             # before first column -> previous line
  256.             elif [[ $newcol -lt 0 ]]; then
  257.                 newrow=$(($newrow - 1))
  258.                 if [[ $newrow -lt 0 ]]; then
  259.                     newrow=0
  260.                 fi
  261.                 newcol=${#buf[newrow]}
  262.             else
  263.                 break
  264.             fi
  265.         done
  266.         # recalculate deltas from bounds-checked new coords
  267.         drow=$(($newrow - $row))
  268.         dcol=$(($newcol - $col))
  269.         # change scrolling based on index
  270.         local scrollbottom=$(($scroll + $height - 1))
  271.         local newscroll=$scroll
  272.         # index above scroll top -> scroll up
  273.         if [[ $newrow -lt $scroll ]]; then
  274.             newscroll=$newrow
  275.         fi
  276.         # index below scroll bottom -> scroll down
  277.         if [[ $newrow -gt $scrollbottom ]]; then
  278.             newscroll=$(($newrow - $height + 1))
  279.         fi
  280.         # bounds-check scrolling
  281.         if [[ $newscroll -lt 0 ]]; then
  282.             newscroll=0
  283.         fi
  284.         local newscrollbottom=$((newscroll + $height - 1))
  285.         if [[ $newscrollbottom -ge $count ]]; then
  286.             newscroll=$(($count - $height))
  287.             newscrollbottom=$(($count - 1))
  288.         fi
  289.         # end of coord / scroll calculations
  290.     fi
  291.     # post-calculations
  292.     local srow=$(($row - $scroll + $firstrow))
  293.     local snewrow=$(($newrow - $newscroll + $firstrow))
  294.     local scol=$(($col + $firstcol))
  295.     local snewcol=$(($newcol + $firstcol))
  296.     # if scrolled or need redraw, do a full redraw
  297.     local didfullredraw=false
  298.     if [[ $scroll != $newscroll ]] || $needredraw; then
  299.         # get in position
  300.         echo -en "\e[${firstrow};${firstcol}H"
  301.         # erase drawn characters
  302.         for i in $(seq $scroll $scrollbottom); do
  303.             printf "\e[0m%${width}s\e[${width}D\e[B" ''
  304.         done
  305.         # draw over them
  306.         for i in $(seq $newscroll $newscrollbottom | tac); do
  307.             if [[ ($i -ge ${#buf[@]}) || ($i -lt 0) || ("${buf[$i]}" == "") ]]; then
  308.                 echo -en "\e[A"
  309.             else
  310.                 echo -en "\e[A"
  311.                 echo -En "${buf[$i]}"
  312.                 if [[ ${#buf[$i]} -ge $width ]]; then
  313.                     echo -en "\e[$((${firstrow}+${i}));${firstcol}H"
  314.                 else
  315.                     echo -en "\e[${#buf[$i]}D"
  316.                 fi
  317.             fi
  318.         done
  319.         # done with full redraw
  320.         needredraw=false
  321.         didfullredraw=true
  322.     fi
  323.     # if did full redraw or changed row, redraw row selection
  324.     local didrowredraw=false
  325.     if [[ $newrow != $row ]] || $didfullredraw; then
  326.         # only need to redraw previous selection
  327.         # if it's not already redrawn
  328.         if ! $didfullredraw; then
  329.             echo -en "\e[${srow};${firstcol}H"
  330.             if [[ "${buf[$row]}" == "" ]]; then
  331.                 echo -En " "
  332.             else
  333.                 echo -En "${buf[$row]} "
  334.             fi
  335.         fi
  336.         # always need to redraw new selection to show it's selected
  337.         echo -en "\e[${snewrow};${firstcol}H${rowselsgr}"
  338.         echo -En "${buf[$newrow]} "
  339.         echo -en "\e[0m"
  340.         # done with row redraw
  341.         didrowredraw=true
  342.     fi
  343.     # if did row redraw or changed column, redraw column selection
  344.     if [[ $newcol != $col ]] || $didrowredraw && ! $nocolumn; then
  345.         # only need to redraw previous selection
  346.         # if it's not already redrawn
  347.         if ! $didrowredraw; then
  348.             if [[ $newrow == $row ]]; then
  349.                 echo -en "\e[${srow};${scol}H${rowselsgr}"
  350.                 echo -En "${buf[$row]:$col:1}"
  351.                 echo -en "\e[0m"
  352.             else
  353.                 echo -en "\e[${srow};${scol}H\e[0m"
  354.                 echo -En "${buf[$row]:$col:1}"
  355.             fi
  356.         fi
  357.         # always need to redraw new selection to show it's selected
  358.         local chr=${buf[newrow]:$newcol:1}
  359.         # make sure it shows even at end of line
  360.         if [[ $chr == '' ]]; then chr=' '; fi
  361.         # draw it
  362.         echo -en "\e[${snewrow};${snewcol}H"`
  363.             `"${colselsgr}${chr}\e[0m"
  364.     fi
  365.     # update coords
  366.     scroll=$newscroll
  367.     row=$newrow
  368.     col=$newcol
  369. }
  370.  
  371. redrawlinebuf() {
  372.     LINE_BUFFER_NEEDS_REDRAW=true
  373.     repositionlinebuf
  374. }
  375.  
  376. putlinebuf() {
  377.     local -n buf=LINE_BUFFER
  378.     local chr=$1
  379.     local row=$2
  380.     local col=$3
  381.     # split line at position
  382.     local line="${buf[$row]}"
  383.     local firstpart="${line:0:$col}"
  384.     local restpart="${line:$col}"
  385.     # the line buffer is changing so needs redraw
  386.     LINE_BUFFER_NEEDS_REDRAW=true
  387.     # enter -> split line into two lines
  388.     if [[ $chr == '' ]]; then
  389.         buf=( "${buf[@]:0:$row}"
  390.                 "$firstpart"
  391.                 "$restpart"
  392.                 "${buf[@]:$(($row + 1))}"
  393.         );
  394.         repositionlinebuf --absolute $(($row + 1)) 0
  395.     # backspace at beginning of line
  396.     # -> merge line with previous
  397.     elif [[ ($chr == '^?') && ($col == 0) && ($row != 0) ]]; then
  398.         repositionlinebuf --absolute `
  399.             ` $(($row - 1)) ${#buf[$(($row - 1))]}
  400.         buf=( "${buf[@]:0:$(($row - 1))}"
  401.                 "${buf[$(($row - 1))]}${buf[$row]}"
  402.                 "${buf[@]:$(($row + 1))}"
  403.         );
  404.         LINE_BUFFER_NEEDS_REDRAW=true
  405.         repositionlinebuf
  406.     # backspace elsewhere
  407.     # -> reconstruct line with character missing
  408.     elif [[ ($chr == '^?') && ($col != 0) ]]; then
  409.         buf=( "${buf[@]:0:$row}"
  410.                 "${firstpart:0:-1}${restpart}"
  411.                 "${buf[@]:$(($row + 1))}"
  412.         );
  413.         repositionlinebuf --absolute $row $(($col - 1))
  414.     # single printable character
  415.     # -> reconstruct line with character inserted
  416.     elif [[ ${#chr} == 1 ]]; then
  417.         buf=( "${buf[@]:0:$row}"
  418.                 "${firstpart}${chr:0:1}${restpart}"
  419.                 "${buf[@]:$(($row + 1))}"
  420.         );
  421.         repositionlinebuf --absolute $row $(($col + 1))
  422.     fi
  423. }
  424.  
  425. loadlinebuf() {
  426.     mapfile -t LINE_BUFFER
  427.     LINE_BUFFER_SCROLL=0
  428.     LINE_BUFFER_NEEDS_REDRAW=true
  429.     repositionlinebuf --absolute 0 0
  430. }
  431.  
  432. dumplinebuf() {
  433.     for line in "${LINE_BUFFER[@]}"; do
  434.         echo "${line}"
  435.     done
  436. }
  437.  
  438. # =======================
  439. # LINEBUF MASTER FUNCTION
  440. # =======================
  441.  
  442. linebuf() {
  443.     # parse options
  444.     local readonly=false
  445.     while [[ "${1:0:2}" == "--" ]]; do
  446.         case "$1" in
  447.         '--ro')     readonly=true ;;
  448.         esac
  449.         shift
  450.     done
  451.     # logic
  452.     case $1 in
  453.     load)       loadlinebuf ;;
  454.     dump)       dumplinebuf ;;
  455.     loadfile)   loadlinebuf "$(cat $1)" ;;
  456.     dumpfile)   dumplinebuf > "$1" ;;
  457.     go)         repositionlinebuf --absolute "${@:1}" ;;
  458.     move)       repositionlinebuf "${@:1}" ;;
  459.     redraw)     LINE_BUFFER_NEEDS_REDRAW=true; repositionlinebuf ;;
  460.     nocol)      LINE_BUFFER_DISABLE_COLUMNS=true ;;
  461.     yescol)     LINE_BUFFER_DISABLE_COLUMNS=false ;;
  462.     row)        echo $LINE_BUFFER_ROW_INDEX ;;
  463.     col)        echo $LINE_BUFFER_COLUMN_INDEX ;;
  464.     put)        putlinebuf "$2" `
  465.                     `$LINE_BUFFER_ROW_INDEX `
  466.                     `$LINE_BUFFER_COLUMN_INDEX ;;
  467.     get)
  468.         echo "${LINE_BUFFER[$LINE_BUFFER_ROW_INDEX]:$LINE_BUFFER_COLUMN_INDEX:1}"
  469.         ;;
  470.     up)         repositionlinebuf -1 0 ;;
  471.     down)       repositionlinebuf 1 0 ;;
  472.     left)       repositionlinebuf 0 -1 ;;
  473.     right)      repositionlinebuf 0 1 ;;
  474.     home)
  475.         if [[ $LINE_BUFFER_COLUMN_INDEX == 0 ]]; then
  476.             local c=0
  477.             while [[ "${LINE_BUFFER[$LINE_BUFFER_ROW_INDEX]:$c:1}" == ' ' ]]; do
  478.                 c=$((c + 1))
  479.             done
  480.             repositionlinebuf 0 $c
  481.         else
  482.             repositionlinebuf 0 -$LINE_BUFFER_COLUMN_INDEX
  483.         fi ;;
  484.     end)
  485.         repositionlinebuf 0 `
  486.             ` $((${#LINE_BUFFER[$LINE_BUFFER_ROW_INDEX]} `
  487.                 ` - $LINE_BUFFER_COLUMN_INDEX)) ;;
  488.     pageup)     repositionlinebuf $((-$LINE_BUFFER_DISPLAY_HEIGHT/2 - 1)) ;;
  489.     pagedown)   repositionlinebuf $(($LINE_BUFFER_DISPLAY_HEIGHT/2 + 1)) ;;
  490.     key)
  491.         case "$2" in
  492.         '^[[A')             linebuf up ;;
  493.         '^[[B')             linebuf down ;;
  494.         '^[[C')
  495.             if ! $LINE_BUFFER_DISABLE_COLUMNS; then
  496.                 linebuf right
  497.             fi ;;
  498.         '^[[D')
  499.             if ! $LINE_BUFFER_DISABLE_COLUMNS; then
  500.                 linebuf left
  501.             fi ;;
  502.         '^[[1~'|'^[[7~')
  503.             if ! $LINE_BUFFER_DISABLE_COLUMNS; then
  504.                 linebuf home
  505.             fi ;;
  506.         '^[[4~'|'^[[8~')
  507.             if ! $LINE_BUFFER_DISABLE_COLUMNS; then
  508.                 linebuf end
  509.             fi ;;
  510.         '^[[5~')            linebuf pageup ;;
  511.         '^[[6~')            linebuf pagedown ;;
  512.         $(echo -e "\t"))
  513.             if ! { $readonly || $LINE_BUFFER_DISABLE_COLUMNS; }; then
  514.                 for i in $(seq $((4 - $LINE_BUFFER_COLUMN_INDEX%4))); do
  515.                     linebuf put ' '
  516.                 done
  517.             fi ;;
  518.         '^[^?')
  519.             if ! { $readonly || $LINE_BUFFER_DISABLE_COLUMNS; }; then
  520.                 if [[ $LINE_BUFFER_COLUMN_INDEX -gt 0 ]]; then
  521.                     for i in $(seq $((($LINE_BUFFER_COLUMN_INDEX + 3)%4 + 1))); do
  522.                         linebuf put '^?'
  523.                     done
  524.                 fi
  525.             fi ;;
  526.         *)
  527.             if ! { $readonly || $LINE_BUFFER_DISABLE_COLUMNS; }; then
  528.                 linebuf put "$2"
  529.             fi ;;
  530.         esac ;;
  531.     height)     LINE_BUFFER_DISPLAY_HEIGHT=$2 ;;
  532.     firstrow)   LINE_BUFFER_DISPLAY_ROW_OFFSET=$2 ;;
  533.     firstcol)   LINE_BUFFER_DISPLAY_COLUMN_OFFSET=$2 ;;
  534.     esac
  535. }
  536.  
  537. linebuftest() {
  538.     terminit
  539.     linebuf load < <(echo "${LICENSE}")
  540.     local key; while key=$(getkey); do case $key in
  541.     *)          linebuf key "$key" ;;
  542.     esac; done
  543.     termcleanup
  544.     linebuf dump
  545. }
  546.  
  547. # ====================
  548. # STATUS BAR FUNCTIONS
  549. # ====================
  550.  
  551. STATUS_BAR_ROW=$(($TERM_HEIGHT - 2))
  552. STATUS_BAR_SGR="\e[47m\e[30m"
  553. STATUS_BAR_ERROR_SGR="\e[41m\e[37m"
  554. STATUS_BAR_PROMPT_SGR="\e[43m\e[30m"
  555. STATUS_BAR_TEXT=
  556.  
  557. statusbar() {
  558.     # opts
  559.     local sgr=$STATUS_BAR_SGR
  560.     local nosleep=false
  561.     while [[ "${1:0:2}" == '--' ]]; do
  562.         case "$1" in
  563.         '--sgr')        shift; sgr="$1" ;;
  564.         '--nosleep')    nosleep=true ;;
  565.         esac
  566.         shift
  567.     done
  568.     # logic
  569.     local resp ret
  570.     if [[ "$1" == '' ]]; then
  571.         echo "${STATUS_BAR_TEXT}"
  572.     else
  573.         case "$1" in
  574.         'clear')
  575.             echo -en "\e[${STATUS_BAR_ROW};0H" 1>&2
  576.             printf "%$((${TERM_WIDTH} - 1))s" '' 1>&2
  577.             echo -en "\e[${STATUS_BAR_ROW};0H" 1>&2
  578.             STATUS_BAR_TEXT=
  579.             ;;
  580.         'error')
  581.             statusbar --sgr "${STATUS_BAR_ERROR_SGR}" "Error: $2"
  582.             ;;
  583.         'Y/n')
  584.             statusbar --nosleep --sgr "${STATUS_BAR_PROMPT_SGR}" "$2 [Y/n]"
  585.             local ret=0
  586.             case $(getkey) in
  587.             N|n) ret=1 ;;
  588.             esac
  589.             statusbar clear
  590.             return $ret
  591.             ;;
  592.         'y/N')
  593.             statusbar --nosleep --sgr "${STATUS_BAR_PROMPT_SGR}" "$2 [y/N]"
  594.             local ret=1
  595.             case $(getkey) in
  596.             Y|y) ret=0 ;;
  597.             esac
  598.             statusbar clear
  599.             return $ret
  600.             ;;
  601.         'prompt')
  602.             statusbar --nosleep --sgr "${STATUS_BAR_PROMPT_SGR}" "$2:"
  603.             stty echo
  604.             read -r resp
  605.             ret=$?
  606.             stty -echo
  607.             echo "${resp}"
  608.             return $ret
  609.             ;;
  610.         'password')
  611.             statusbar --nosleep --sgr "${STATUS_BAR_PROMPT_SGR}" "$2:"
  612.             read -rs resp
  613.             ret=$?
  614.             echo "${resp}"
  615.             return $ret
  616.             ;;
  617.         *)
  618.             local msg=" $1 "
  619.             echo -en "\e[${STATUS_BAR_ROW};0H" 1>&2
  620.             printf "%$((${TERM_WIDTH} - 1))s" '' 1>&2
  621.             echo -en "\e[${STATUS_BAR_ROW};0H" 1>&2
  622.             STATUS_BAR_TEXT="$1"
  623.             printf "%$((${TERM_WIDTH}/2 - ${#msg}/2))s" '' 1>&2
  624.             echo -en "${sgr}${msg}\e[0m" 1>&2
  625.             if ! $nosleep; then
  626.                 read -rst1.1 -n1
  627.             fi
  628.             ;;
  629.         esac
  630.     fi
  631. }
  632.  
  633. # =====================
  634. # PERMISSIONS FUNCTIONS
  635. # =====================
  636.  
  637. subypass() {
  638.     local user=$1
  639.     local pass=$2
  640.     local cmd="${@:3}"
  641.     { sleep 0.25; echo $pass; } |
  642.         script -eqc "su -c "'"'"${cmd}"'"'" - $user 2>&1 > /dev/null" > /dev/null
  643.     return $?
  644. }
  645.  
  646. auth() {
  647.     local admin=
  648.     local pass=
  649.     if ! admin=$(statusbar prompt "System administrator username"); then
  650.         statusbar "Cancelled"
  651.         return 1
  652.     fi
  653.     if ! pass=$(statusbar password "Password for ${admin}"); then
  654.         statusbar "Cancelled"
  655.         return 1
  656.     fi
  657.     statusbar --nosleep "Authenticating.  "
  658.     if ! subypass "$admin" "$pass" ":"; then
  659.         statusbar error "Incorrect username or password"
  660.         return 1
  661.     fi
  662.     statusbar --nosleep "Authenticating.. "
  663.     if ! subypass "$admin" "$pass" "echo '""$pass""' | sudo -lS 2>&1 > /dev/null"; then
  664.         statusbar error "User $admin is not a system administrator"
  665.         return 1
  666.     fi
  667.     local action="$@"
  668.     statusbar --nosleep "Authenticating..."
  669.     if ! subypass "$admin" "$pass" "echo '""$pass"`
  670.             `"' | sudo -S 2>&1 ${action}"; then
  671.         statusbar error "Privileged command returned a nonzero exit code"
  672.         return 1
  673.     fi
  674.     return 0
  675. }
  676.  
  677. promptchmod() {
  678.     # opts
  679.     local usesu=false
  680.     while [[ "${1:0:2}" == '--' ]]; do
  681.         case "$1" in
  682.         '--su') usesu=true ;;
  683.         esac
  684.         shift
  685.     done
  686.     # logic
  687.     local mode="$1"
  688.     local fname="$2"
  689.     if statusbar y/N "Force access (chmod ${mode})?"; then
  690.         if [[ -O "$fname" ]]; then
  691.             if chmod "u$mode" "$fname"; then
  692.                 statusbar "Performed chmod."
  693.                 return 0
  694.             else
  695.                 statusbar error "Couldn't chmod."
  696.                 return 1
  697.             fi
  698.         elif $usesu; then
  699.             if auth chmod "o$mode" "$fname"; then
  700.                 statusbar "Performed chmod."
  701.                 return 0
  702.             else
  703.                 return 1
  704.             fi
  705.         else
  706.             statusbar error "No permission to chmod this file."
  707.             return 1
  708.         fi
  709.     else
  710.         return 1
  711.     fi
  712. }
  713.  
  714. realpath_if_allowed() {
  715.     if ! realpath "$1" 2>/dev/null; then echo "$1"; fi
  716. }
  717.  
  718. fileperms() {
  719.     # opts
  720.     local usepromptchmod=false
  721.     local promptchmodopts=''
  722.     local create=false
  723.     while [[ "${1:0:2}" == '--' ]]; do
  724.         case "$1" in
  725.         '--chmod')  usepromptchmod=true ;;
  726.         '--su')     promptchmodopts='--su' ;;
  727.         '--create') create=true ;;
  728.         esac
  729.         shift
  730.     done
  731.     # logic
  732.     local fname="$1"
  733.     local dname="$(dirname $(readlink -m $fname))"
  734.     local cont
  735.     if [[ (-e "$dname") && (! -d "$dname") ]]; then
  736.         statusbar error "$(basename $dname) is not a directory."
  737.         echo '---'
  738.         return 1
  739.     elif [[ (! -e "$dname") ]]; then
  740.         statusbar error "Directory $(basename $dname) does not exist."
  741.         echo '---'
  742.         return 1
  743.     elif [[ (! -e "$fname") ]] && ! $create; then
  744.         statusbar error "File $(basename $fname) does not exist."
  745.         echo '---'
  746.         return 1
  747.     elif [[ (! -e "$fname") && (-w "$dname") && (-x "$dname") ]]; then
  748.         statusbar "File $(basename $fname) to be created."
  749.         echo '-w-'
  750.         return 0
  751.     elif [[ (! -e "$fname") ]] && $usepromptchmod; then
  752.         if promptchmod $promptchmodopts +wx "$dname"; then
  753.             statusbar "File $(basename $fname) to be created."
  754.             echo '-w-'
  755.             return 0
  756.         else
  757.             echo '---'
  758.             return 1
  759.         fi
  760.     elif [[ (! -e "$fname") ]]; then
  761.         statusbar error "No permission to create file $(basename $fname)."
  762.         echo '---'
  763.         return 1
  764.     else
  765.         local r=- w=- x=-
  766.         if [[ -r "$fname" ]]; then
  767.             r=r
  768.         fi
  769.         if [[ -w "$fname" ]]; then
  770.             w=w
  771.         fi
  772.         if [[ -x "$fname" ]]; then
  773.             x=x
  774.         fi
  775.         echo "$r$w$x"
  776.         return 0
  777.     fi
  778. }
  779.  
  780. # =========================
  781. # LINEBUF-BASED SUBPROGRAMS
  782. # =========================
  783.  
  784. texteditor() {
  785.     # opts
  786.     local readonly=false
  787.     while [[ "${1:0:2}" == '--' ]]; do
  788.         case "$1" in
  789.         '--ro') readonly=true ;;
  790.         esac
  791.         shift
  792.     done
  793.     # logic
  794.     clear
  795.     local fname="$@"
  796.     local perms=$(fileperms --create --chmod --su "$fname")
  797.     if $readonly; then
  798.         perms="${perms:0:1}-${perms:2:1}"
  799.     fi
  800.     case "${perms:0:2}" in
  801.     'rw')   linebuf yescol
  802.             linebuf load < <(cat "$fname")
  803.             statusbar --nosleep "Editing $fname" ;;
  804.     '-w')   linebuf yescol
  805.             linebuf load < <(echo)
  806.             statusbar --nosleep "Editing $fname (new)" ;;
  807.     'r-')   linebuf nocol
  808.             linebuf load < <(cat "$fname")
  809.             statusbar --nosleep "Viewing $fname" ;;
  810.     '--')   statusbar error "Cannot access $fname"
  811.             clear
  812.             return 1 ;;
  813.     esac
  814.     local key; while key=$(getkey); do case $key in
  815.     '^W')
  816.         if [[ ${perms:1:1} == 'w' ]]; then :
  817.             linebuf dump > "$fname"
  818.             statusbar --nosleep "Saved $fname"
  819.         else :
  820.             statusbar error "Read-only mode"
  821.         fi ;;
  822.     *)
  823.         statusbar clear
  824.         if [[ ${perms:1:1} == 'w' ]]; then
  825.             linebuf key "$key"
  826.         else
  827.             linebuf --ro key "$key"
  828.         fi ;;
  829.     esac; done
  830.     clear
  831.     return 0
  832. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement