• Nice prompt [Bash specific]

    From Kaz Kylheku@21:1/5 to All on Thu Aug 3 18:31:59 2023
    I "redesigned" my shell prompt yesterday.

    I now have the current date/time, host name and working directory
    (possibly shortened) in a scroll-protected status line at the bottom of
    the terminal.

    The prompt is just a dollar sign (PS1='\$ ') which is very refreshing.

    Here is the code. I have this in a ~/.bash_functions file that gets
    sourced.

    Using the PROMPT_COMMAND hook mechanism, before every command, we
    update the status area and scroll-protect it. Before that, we check whether the LINES or COLUMNS has changed, in which case we call prepare_terminal
    to reserve the bottom row for ourselves, and make sure the cursor is
    off thatline. This happens on the first call to update_status_line
    also, since old_lines and old_cols are zero.

    Whenever a command has a termination status other than successful, we
    print the line !<n>! where <n> is the status before the prompt.
    Logic in update_status_line ensures that repetitions of this are suppressed
    if the user hits enter on an empty line.

    prepare_terminal()
    {
    stty rows $((LINES - 1))
    printf "\n\033[1A"
    old_lines=$LINES
    old_cols=$COLUMNS
    }

    old_cmdno=0
    old_lines=0
    old_cols=0

    update_status_line()
    {
    local exit=$?
    local getcmdno='\#'
    local cmdno=${getcmdno@P}
    local esc=$(printf "\033")
    local pwd=$PWD
    local dots=

    [ $LINES -eq $old_lines -a $COLUMNS -eq $old_cols ] || prepare_terminal

    while true; do
    [ "${pwd#/*/}" == "$pwd" ] && break
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    done

    [ ${#status_nohl} -gt $COLUMNS ] && return

    printf "${esc}7$esc[%s;1H$esc[K%s$esc[1;%sr${esc}8" $((LINES + 1)) "$status" $
    LINES
    if [ $exit -ne 0 -a $cmdno -ne $old_cmdno ] ; then
    printf "!%s!\n" $exit
    fi
    old_cmdno=$cmdno
    }

    PROMPT_COMMAND='update_status_line'


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Fri Aug 4 17:55:12 2023
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    I "redesigned" my shell prompt yesterday.

    [...]

    Hi Kaz, I wanted to try out your script and have two questions...

    My bash doesn't seem to support the @P in cmdno=${getcmdno@P}
    what does it do?

    At certain places you use in printf contexts \033 or define a
    variable esc to carry that value. In Kornshell I use \E or \e
    for ANSI escapes, and my bash also supports that. Is there in
    bash with PROMPT_COMMAND some technical necessity to use $esc ?

    Janis


    prepare_terminal()
    {
    stty rows $((LINES - 1))
    printf "\n\033[1A"
    old_lines=$LINES
    old_cols=$COLUMNS
    }

    old_cmdno=0
    old_lines=0
    old_cols=0

    update_status_line()
    {
    local exit=$?
    local getcmdno='\#'
    local cmdno=${getcmdno@P}
    local esc=$(printf "\033")
    local pwd=$PWD
    local dots=

    [ $LINES -eq $old_lines -a $COLUMNS -eq $old_cols ] || prepare_terminal

    while true; do
    [ "${pwd#/*/}" == "$pwd" ] && break
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    done

    [ ${#status_nohl} -gt $COLUMNS ] && return

    printf "${esc}7$esc[%s;1H$esc[K%s$esc[1;%sr${esc}8" $((LINES + 1)) "$status" $
    LINES
    if [ $exit -ne 0 -a $cmdno -ne $old_cmdno ] ; then
    printf "!%s!\n" $exit
    fi
    old_cmdno=$cmdno
    }

    PROMPT_COMMAND='update_status_line'



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Fri Aug 4 17:36:12 2023
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    while true; do
    [ "${pwd#/*/}" == "$pwd" ] && break
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    done

    Erratum: the break test at the top of the loop body should be moved to the bottom:

    while true; do
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_len=$((${#status} - ${#status_esc}))
    [ $status_len -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    [ "${pwd#/*/}" == "$pwd" ] && break
    done

    This causes an issue reported by a user on Mastodon, that
    when you change to a directory like / or /home, the status
    line disappears. We need the loop body executed unconditionally
    at least to calculate a status line, and then to stop iterating
    if the path cannot be reduced further after that.

    There is one more check after the loop for a status line that cannot fit
    the current $COLUMNS with, in which case it is turned off.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Fri Aug 4 17:26:56 2023
    On 2023-08-04, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    I "redesigned" my shell prompt yesterday.

    [...]

    Hi Kaz, I wanted to try out your script and have two questions...

    Hi Janis,

    Thanks for trying it; if you get it working 100%, you will like it,
    I'm sure. The idea is to set the prompt to just PS=1'\$ ' which
    is clean and refreshing, and feels like old Unix. All the junk
    like showing the date, time, host name and current directory, goes into
    a scroll-protected status line at the bottom.

    My bash doesn't seem to support the @P in cmdno=${getcmdno@P}
    what does it do?

    This is a newer feature (Bash 4.4+)? It specifies prompt-like
    expansion.

    The variable getcmdno contains a code that is normally used
    in the PS1 prompt. @{getcmdno@P} handles that variable as if it
    were PS1, expanding that code.

    The only reason I used this was to gain access to the current prompt
    line number. I don't think there is any other way to get to it. The line
    number lets us distinguish whether the user is just hitting Enter on an
    empty line:

    $
    $
    $ _

    or actually issuing new commands.

    When the user issues an empty line, the value of $? observed by the PROMPT_COMMAND hook remains the same. I wanted the failed status like
    !1! to be printed only once, like this:

    $ false
    !1!
    $
    $
    $ _

    and not:

    $ false
    !1!
    $
    !1!
    $
    !1!
    $ _

    which is achieved by observing no change in the prompt number.

    So it is all for aesthetics.

    Someone who doesn't mind that, or doesn't want the termination
    status, or does something else with it can remove all that
    code.

    At certain places you use in printf contexts \033 or define a
    variable esc to carry that value. In Kornshell I use \E or \e
    for ANSI escapes, and my bash also supports that. Is there in
    bash with PROMPT_COMMAND some technical necessity to use $esc ?

    I initially had a hard-coded escape.

    By defining it as a named term, we can do this:

    esc=ESC # for debugging

    The performance hit of the extra substitutions is not an issue.

    Also, the reason the following variables are defined the way they are is
    to support reloading of the code.

    old_cmdno=${old_cmdno-0}
    old_lines=${old_lines-0}
    old_cols=${old_cols-0}

    If the variables already exist, their values are left alone, so that update_statusS_line will not call prepare_terminal, which will steal
    another line of display, which is an annoying behavior.

    In Common Lisp, (defvar variable value) will not evaluate the value
    expression or assign the variable if the variable already exists. This
    supports reloading of modules without disturbing their global variables.
    (You use defparameter, which always assigns, if you want that.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Janis Papanagnou on Fri Aug 4 11:54:11 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    [...]
    Hi Kaz, I wanted to try out your script and have two questions...

    My bash doesn't seem to support the @P in cmdno=${getcmdno@P}
    what does it do?

    What version of bash are you using? This feature was introduced in bash
    4.4, released in 2016.

    From "bash info param":

    '${PARAMETER@OPERATOR}'
    The expansion is either a transformation of the value of PARAMETER
    or information about PARAMETER itself, depending on the value of
    OPERATOR. Each OPERATOR is a single letter:
    [...]
    'P'
    The expansion is a string that is the result of expanding the
    value of PARAMETER as if it were a prompt string (*note
    Controlling the Prompt::).

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Will write code for food.
    void Void(void) { Void(); } /* The recursive call of the void */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Elvidge@21:1/5 to Kaz Kylheku on Fri Aug 4 21:04:43 2023
    On 04/08/2023 18:36, Kaz Kylheku wrote:
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    while true; do
    [ "${pwd#/*/}" == "$pwd" ] && break
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    done

    Erratum: the break test at the top of the loop body should be moved to the bottom:

    while true; do
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_len=$((${#status} - ${#status_esc}))
    [ $status_len -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    [ "${pwd#/*/}" == "$pwd" ] && break
    done

    This causes an issue reported by a user on Mastodon, that
    when you change to a directory like / or /home, the status
    line disappears. We need the loop body executed unconditionally
    at least to calculate a status line, and then to stop iterating
    if the path cannot be reduced further after that.

    There is one more check after the loop for a status line that cannot fit
    the current $COLUMNS with, in which case it is turned off.


    Yes, nice, but you lose a line on the terminal (but who cares?). If you
    use Esc]2; you can update the terminal title. However, no highlights.

    Should PROMPT_COMMAND= be PROMPT_COMMAND[0]= for modernity?

    As it happens, mine looks like this:
    PROMPT_COMMAND=(
    [0]="echo -ne \"\\033]11;#204A87\\007\""
    [1]="echo -ne \"\\033]2;\${USER}@\${HOSTNAME} (\${IP}) : (\${KERNEL}, \${BASHVR}) : \$(pwd) : \${LAST_COMMAND}\\007\""
    )

    Cheers

    --

    Chris Elvidge, England
    I AM NOT THE NEW DALAI LAMA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris Elvidge on Fri Aug 4 20:56:23 2023
    On 2023-08-04, Chris Elvidge <chris@mshome.net> wrote:
    Yes, nice, but you lose a line on the terminal (but who cares?). If you
    use Esc]2; you can update the terminal title. However, no highlights.

    Yes; putting stuff into the title is an old trick.

    That stuff is far away from where my attention is, usually close
    to the prompt.

    Secondly, it's sometimes occupied. Right now I'm in screen session; when
    you swap screens, screen updates the title.
    (Though, that doesn't mean we can't clobber it, too.)

    Should PROMPT_COMMAND= be PROMPT_COMMAND[0]= for modernity?

    I guess so; if there is a stack/list of them now, we want to
    just be one entry. But probably [0] is already occupied?
    We want to add to the array, not clobber an arbitrary index.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Aug 5 03:33:42 2023
    On 2023-08-05, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    I have discovered an issue. When I run the "top" utility from procps-ng,
    and then exit, stupidly puts the cursor into the scroll-protected area,
    for whatever reason.

    The fix has the unexpected tiny benefit that when you quit top, the top
    output left on the screen no longer scrolls up by one line The cursor is
    placed over the last line of the top output, and you can still see the
    first line of the display.

    Normally, top occupies the entire screen. When you quit, it puts the
    cursor on the next line after that, so the display scrolls by one line.

    Now it's putting the cursor into the status line which we adjust to the previous line, and so no scrolling takes place.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Aug 5 03:30:40 2023
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    I have discovered an issue. When I run the "top" utility from procps-ng,
    and then exit, stupidly puts the cursor into the scroll-protected area,
    for whatever reason.

    I implemented a countermeasure. As the last interaction with the
    terminal, after we print the status line, and restore the cursor
    position, we ask the terminal the X, Y position. If it is beyond the
    last line, then we move the cursor to the first colum of the last line.

    To read the X, Y position, we issue ESC[6h, and then parse the response
    which is like ESC[<y>;<x>]R. Bash's read function lets us do this
    nicely: read -s -d R <var>. Read until the delimiter R, silently
    without echoing.

    I will probably optimize things. Now that we are getting the ecurrent
    position, we don't have to use the ESC 7 and ESC 8 trick for saving
    and restoring position. We can just get the position once,
    do the status line and then before restoring the position, check
    for a valid line number.

    For now it's like this:

    old_cmdno=${old_cmdno-0}
    old_lines=${old_lines-0}
    old_cols=${old_cols-0}

    prepare_terminal()
    {
    stty rows $((LINES - 1))
    printf "\n\033[1A"
    old_lines=$LINES
    old_cols=$COLUMNS
    }

    get_current_line()
    {
    local esc=$(printf "\033")
    local response
    printf "$esc[6n" > /dev/tty
    read -s -d R response < /dev/tty
    local IFS="[;"
    set -- $response
    printf "%s\n" "$2"
    }

    update_status_line()
    {
    local exit=$?
    local getcmdno='\#'
    local cmdno=${getcmdno@P}
    local esc=$(printf "\033")
    local pwd=$PWD
    local dots=

    [ $LINES -eq $old_lines -a $COLUMNS -eq $old_cols ] || prepare_terminal

    local status_esc="$esc[7m$esc[m"

    while true; do
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_len=$((${#status} - ${#status_esc}))
    [ $status_len -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    [ "${pwd#/*/}" == "$pwd" ] && break
    done

    status_len=$((${#status} - ${#status_esc}))

    [ $status_len -gt $COLUMNS ] && status=

    printf "${esc}7$esc[%s;1H$esc[K%s$esc[1;%sr${esc}8" $((LINES + 1)) "$status" $LINES
    if [ $exit -ne 0 -a $cmdno -ne $old_cmdno ] ; then
    printf "!%s!\n" $exit
    fi

    local curln=$(get_current_line)

    if [ $curln > $LINES ]; then
    printf "$esc[%s;1H" $LINES
    fi

    old_cmdno=$cmdno
    }

    PROMPT_COMMAND='update_status_line'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Aug 5 05:50:58 2023
    On 2023-08-05, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    if [ $curln > $LINES ]; then

    You want to make this $curln -gt $LINES, if you don't want to keep
    creating a file called 52 (or however many lines you have).

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Aug 5 06:28:36 2023
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    This now lives here:

    https://www.kylheku.com/cgit/kabapro/tree/bash-prompt.sh

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sat Aug 5 08:10:27 2023
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    LOL, I got the clock in the status line to update spontaneously.

    $ trap update_status_line ALRM
    $ while true; do kill -ALRM $$ ; sleep 15 ; done &
    [1] 19410

    Background process sends SIGALRM every 15 seconds; status
    line updates.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Sat Aug 5 12:31:13 2023
    On 05.08.2023 10:10, Kaz Kylheku wrote:
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    LOL, I got the clock in the status line to update spontaneously.

    $ trap update_status_line ALRM
    $ while true; do kill -ALRM $$ ; sleep 15 ; done &
    [1] 19410

    Background process sends SIGALRM every 15 seconds; status
    line updates.

    I think this extension introduced a bug; I started a bash shell,
    sourced the script, exited the bash instance, and on a new start
    and sourcing I get every 15s (even after leaving bash completely)
    ./bash-prompt-V0: line 73: kill: (4118) - No such process
    I got rid by the timer background process only after a 'pkill bash'
    (couldn't identify the actual PID under my own or init's UID).

    I suppose it needs at least a process cleanup before termination?

    Janis

    PS: Does bash support (as known from ksh) process bound timers?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Sat Aug 5 16:10:15 2023
    On 04.08.2023 19:26, Kaz Kylheku wrote:

    Thanks for trying it; if you get it working 100%, you will like it,
    I'm sure. The idea is to set the prompt to just PS=1'\$ ' which
    is clean and refreshing, and feels like old Unix. All the junk
    like showing the date, time, host name and current directory, goes into
    a scroll-protected status line at the bottom.

    I can already say that I like the idea! Formerly we already had "all
    the junk" in a separate line by adding a \n before the $ prompt. The
    fixed (and protected) status line is certainly a step forward. When
    (or if) I write my ksh variant - bash is not my standard shell -, I
    want to make further changes; the attributes I like to be displayed
    are of course different, but I also prefer to change the cursor
    position for the command input to be constantly at the bottom of the
    screen. - The short time special case that the first commands are
    entered at the top of the screen and only if the screen gets filled
    it moves downwards until it permanently reaches the bottom of the
    screen is somewhat strange if you think about it; after the first
    or second command entered my screen is anyway full and the cursor
    at the bottom, so why not keep it static where it usually is. For
    my taste that would be another little step forwards.

    I want to strive to this layout...

    +-----
    |
    | Scroll area (Input and Output)
    |
    :
    :
    |
    | Prompt (Input)
    | Status (scroll protected)
    +-----

    (That faintly reminds me the old mainframe times.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Sat Aug 5 15:21:48 2023
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    I "redesigned" my shell prompt yesterday.
    [...]

    Yet another question...

    printf "${esc}7$esc[%s;1H$esc[K%s$esc[1;%sr${esc}8" $((LINES + 1)) "$status" $LINES

    What does $esc[1;%sr (with $LINES expanded for %s) do?
    Say, \033[1;24r
    (It seems I cannot find the ANSI control character 'r'.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Janis Papanagnou on Sat Aug 5 16:22:43 2023
    On 05.08.2023 15:21, Janis Papanagnou wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    I "redesigned" my shell prompt yesterday.
    [...]

    Yet another question...

    printf "${esc}7$esc[%s;1H$esc[K%s$esc[1;%sr${esc}8" $((LINES + 1)) "$status" $LINES

    What does $esc[1;%sr (with $LINES expanded for %s) do?
    Say, \033[1;24r
    (It seems I cannot find the ANSI control character 'r'.)

    I suppose I found the answer...

    | The basics are: configure your device's shell to set scroll
    | region once the command is accepted from user (equivalent of
    | printf "\033[1,24r") -- and reset it once it completes --
    | printf "\033[r".
    [Stackexchange]

    ...presuming the differences (';' vs. ',') are not changing semantics.


    Janis


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Sat Aug 5 15:40:47 2023
    On 2023-08-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 05.08.2023 15:21, Janis Papanagnou wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    I "redesigned" my shell prompt yesterday.
    [...]

    Yet another question...

    printf "${esc}7$esc[%s;1H$esc[K%s$esc[1;%sr${esc}8" $((LINES + 1)) "$status" $LINES

    What does $esc[1;%sr (with $LINES expanded for %s) do?
    Say, \033[1;24r
    (It seems I cannot find the ANSI control character 'r'.)

    I suppose I found the answer...

    | The basics are: configure your device's shell to set scroll
    | region once the command is accepted from user (equivalent of
    | printf "\033[1,24r") -- and reset it once it completes --
    | printf "\033[r".
    [Stackexchange]

    ...presuming the differences (';' vs. ',') are not changing semantics.

    The documented syntax requires a semicolon. If it works with the
    comma, it's some exstension; it doesn't on the terminal I'm using.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Sat Aug 5 15:36:47 2023
    On 2023-08-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 05.08.2023 10:10, Kaz Kylheku wrote:
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    LOL, I got the clock in the status line to update spontaneously.

    $ trap update_status_line ALRM
    $ while true; do kill -ALRM $$ ; sleep 15 ; done &
    [1] 19410

    Background process sends SIGALRM every 15 seconds; status
    line updates.

    I think this extension introduced a bug; I started a bash shell,
    sourced the script, exited the bash instance, and on a new start
    and sourcing I get every 15s (even after leaving bash completely)
    ./bash-prompt-V0: line 73: kill: (4118) - No such process

    I looked for this problem proactively but didn't see it! Looks
    like it has to be handled. A simple hack would be to terminate if
    the kill fails. The ugly error should go to /dev/null.

    I noticed another problem. In a fresh login shell, if I do a reverse
    search with Ctrl-R for something, and cancel the search with Ctrl-C,
    the background process dies. Orphaning the process so that it becomes
    the child of PID 1 might fix it. It needs to be removed from the
    TTY session so that it doesn't get signals.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Sat Aug 5 15:45:19 2023
    On 2023-08-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 04.08.2023 19:26, Kaz Kylheku wrote:

    Thanks for trying it; if you get it working 100%, you will like it,
    I'm sure. The idea is to set the prompt to just PS=1'\$ ' which
    is clean and refreshing, and feels like old Unix. All the junk
    like showing the date, time, host name and current directory, goes into
    a scroll-protected status line at the bottom.

    I can already say that I like the idea! Formerly we already had "all
    the junk" in a separate line by adding a \n before the $ prompt. The
    fixed (and protected) status line is certainly a step forward.

    I like being able to cut and paste commands in examples without
    the chore of editing out the long prompts down to '$'.

    (or if) I write my ksh variant - bash is not my standard shell -, I
    want to make further changes; the attributes I like to be displayed
    are of course different, but I also prefer to change the cursor
    position for the command input to be constantly at the bottom of the
    screen.

    The logic is already there; it's executed when the cursor intrudes
    into the status line.

    We just have to execute that logic whenever the prior cursor position is
    not exactly one line before the status line.

    Maybe just executing the move unconditionally would also work,
    in which case saving the previous position can be dropped.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Sun Aug 6 01:46:55 2023
    On 05.08.2023 17:40, Kaz Kylheku wrote:
    On 2023-08-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 05.08.2023 15:21, Janis Papanagnou wrote:
    [ANSI escape codes, Stackexchange quote]

    ...presuming the differences (';' vs. ',') are not changing semantics.

    The documented syntax requires a semicolon. If it works with the
    comma, it's some exstension; it doesn't on the terminal I'm using.

    If it doesn't work it was probably just a typo of that poster.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Sun Aug 6 01:58:11 2023
    On 2023-08-05, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    This now lives here:

    https://www.kylheku.com/cgit/kabapro/tree/bash-prompt.sh

    Renamed. It's not a prompt but a status line.
    So it's called Basta! (BAsh STAtus).

    https://www.kylheku.com/cgit/basta/tree/

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Tue Aug 8 03:57:24 2023
    On 05.08.2023 17:45, Kaz Kylheku wrote:
    On 2023-08-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote: [...]
    (or if) I write my ksh variant - bash is not my standard shell -, I
    want to make further changes; the attributes I like to be displayed
    are of course different, but I also prefer to change the cursor
    position for the command input to be constantly at the bottom of the
    screen.

    The logic is already there; it's executed when the cursor intrudes
    into the status line.

    We just have to execute that logic whenever the prior cursor position is
    not exactly one line before the status line.

    Maybe just executing the move unconditionally would also work,
    in which case saving the previous position can be dropped.

    I've written some experimental code using some Kornshell mechanisms,
    so it differs from yours. (I've adopted/adapted your name though; I
    like "basta" and named mine accordingly "kosta", kornshell status :-)
    One thing I used (that I think doesn't exist in bash) are discipline
    functions; I use them to update changes in rows/columns, and trigger
    prompt PS1 access. And I have a few own details. Yet I've a lot to
    add to that basic frame (path shortening, coloring, maybe composing
    status lines, ...), it certainly can be shortened, and needs testing.
    But I'm anyway just playing around with your status line idea...

    Janis


    function LINES.set { .kosta.rows=${.sh.value} ; .kosta.update ;}
    function COLUMNS.set { .kosta.cols=${.sh.value} ;}
    function PS1.get { .kosta.compose ;}

    namespace kosta
    {
    typeset -i rows=${LINES} cols=${COLUMNS}
    typeset -i prompt_line=$((rows-1))
    typeset -i status_line=$((rows))

    function update
    {
    prompt_line=$((rows-1))
    status_line=$((rows))
    }

    function compose
    {
    prev_rc=$?
    PS1='(!)$ '
    status="${USER}@${HOSTNAME}:${PWD/${HOME}\//\~/} ${SHLVL_P}"

    printf '\E[%d;1H\E[K' status_line # goto(row,column);
    clear-to-eol;
    if (( prev_rc > 255 )) # signal
    then printf 'SIG %d (%s)! ' prev_rc-256 $( kill -l $((prev_rc)) )
    elif (( prev_rc > 0 )) # error
    then printf 'ERR %d! ' prev_rc
    fi
    printf '%s' "${status}"
    printf '\E[1;%dr' prompt_line # scroll-region(from,to);
    printf '\E[%d;1H' prompt_line # goto(row,column);
    }

    } # namespace kosta

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Tue Aug 8 05:35:05 2023
    On 2023-08-08, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 05.08.2023 17:45, Kaz Kylheku wrote:
    On 2023-08-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    [...]
    (or if) I write my ksh variant - bash is not my standard shell -, I
    want to make further changes; the attributes I like to be displayed
    are of course different, but I also prefer to change the cursor
    position for the command input to be constantly at the bottom of the
    screen.

    The logic is already there; it's executed when the cursor intrudes
    into the status line.

    We just have to execute that logic whenever the prior cursor position is
    not exactly one line before the status line.

    Maybe just executing the move unconditionally would also work,
    in which case saving the previous position can be dropped.

    I've written some experimental code using some Kornshell mechanisms,
    so it differs from yours. (I've adopted/adapted your name though; I
    like "basta" and named mine accordingly "kosta", kornshell status :-)

    Great!

    See, you can still innovate in the area of 1970's ECMA/ANSI terminal
    control combined with 1980's Unix shells.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Wed Aug 9 20:14:29 2023
    On 2023-08-09, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Anyway, resize will just have to be handled more smoothly, so there
    is no glitch.

    ... and I fixed it. Part the fix is to catch the window change
    signal: "trap basta.update_status WINCH"!

    This way, after a resize, we immediately restore the status line
    and the scroll-protected region; the user has no chance to run
    a command with the scroll-protection off where their output
    then runs into the status line, which then scrolls up.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Wed Aug 9 19:25:38 2023
    On 2023-08-06, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    On 2023-08-05, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    On 2023-08-03, Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    I "redesigned" my shell prompt yesterday.

    This now lives here:

    https://www.kylheku.com/cgit/kabapro/tree/bash-prompt.sh

    Renamed. It's not a prompt but a status line.
    So it's called Basta! (BAsh STAtus).

    https://www.kylheku.com/cgit/basta/tree/

    I'm seeing a weird glitch with Gnome terminal.

    I have multiple terminal tabs open, each running a Bash with a Basta
    status line.

    When I switch from one tab to another, I see a spontaneous cursor
    movement and scrolling a few seconds later.

    What is happening is that the value of $LINES has spontaneously changed.
    The basta.update_status function notices this and handles it like a
    resize.

    No resize has taken place; I'm just switching tabs.

    This is some Gnome Terminal B.S. Probably, whenever a tab is selected to
    be current, it is sending a SIGWINCH to the TTY session.

    I'm guessing, they are doing it just in case the application window had
    changed since the last time that tab was shown, whether it actually
    changed or not.

    Anyway, resize will just have to be handled more smoothly, so there
    is no glitch.

    Another, unrelated issue is that the background signal for updating the
    screen can occur in the middle of an command line editing action. The
    escape sequences get bungled.

    I suspect my latest public changes already have the effect of mostly
    mitigating it. I refactored the update function into two smaller ones,
    and the trap handler only calls one of the two. That one no longer
    interrogates the current position of the cursor, and so it isn't
    processing input from the TTY that could be fooled by user input. I'm
    keeping an eye on this issue to see how much of a problem it still is,
    if any.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Janis Papanagnou on Wed Aug 30 01:07:53 2023
    On 08.08.2023 03:57, Janis Papanagnou wrote:
    [...a Kornshell variant of "basta"...]

    namespace kosta
    {
    typeset -i rows=${LINES} cols=${COLUMNS}

    When installing the module to be sourced during .profile/.kshrc
    initialization I noticed that these two shell variables aren't
    yet defined at that instance of time (which at least initially
    produced some weird output once before the first hit of <Enter>).
    Therefore I changed that in the code to

    typeset -i rows cols
    stty size | read rows cols

    [...]
    } # namespace kosta


    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Tue Aug 29 23:42:02 2023
    On 2023-08-29, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 08.08.2023 03:57, Janis Papanagnou wrote:
    [...a Kornshell variant of "basta"...]

    namespace kosta
    {
    typeset -i rows=${LINES} cols=${COLUMNS}

    When installing the module to be sourced during .profile/.kshrc initialization I noticed that these two shell variables aren't
    yet defined at that instance of time (which at least initially

    Yes. I discovered this too. A while ago, in fact.
    Then I forgot about it, so that a bug recently crept in. I
    I changed a certain global variable initialization to this:

    basta_scroll_lines=${basta_scroll_lines-$LINES}

    which left the variable with a blank value when initializing out
    of .bashrc!

    I pushed out a fix for this just this morning!

    However, LINES is available by the Bash calls the prompt hook.
    So the bulk of the initialization is done at that time.

    Speaking of globals, I initializee all global variables conditionally:

    basta_old_cmdno=${basta_old_cmdno-0}
    basta_old_lines=${basta_old_lines-0}
    basta_old_cols=${basta_old_cols-0}

    basta_scroll_lines=${basta_scroll_lines-0}
    basta_prev_reserved_rows=${basta_prev_reserved_rows-}

    basta_status_alarm_pid=${basta_status_alarm_pid-}

    This allows for hot reloading without clobbering the variables.

    In Common Lisp there is a defvar macro operator which defines
    a variable if it doesn't already exist. If a value is specified
    it assigns that value. If the variable already exists, it does
    not assign the value. E.g.

    (defvar *obj-count* 0)

    If you reload the code, it will not reset your *obj-count* to zero.

    If you want that you use:

    (defparameter *obj-count* 0)

    whose second argument is mandatory and always assigned to the
    variable.

    typeset -i rows cols
    stty size | read rows cols

    I no longer trust the size, except on first invocation.

    In the first-time, once-only initialization code that is done by the
    prompt hook when it detects that startup, I trust that the $LINES
    variable corresponds to the usable area of the terminal, not
    necessary the whole thing. I then query the terminal to determine
    its size: position the cursor to 999,999 and then inquire about
    the current position, to get ther row number and that's the size.
    From that, it is inferred whethere or not there is already a protected
    status line; we put our status line above any protected status lines.

    On subsequent terminal resize operations, the terminal is always
    queried for the size, rather than believing that $LINES or the tty
    driver has the correct size.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Wed Aug 30 01:48:44 2023
    On 30.08.2023 01:42, Kaz Kylheku wrote:
    On 2023-08-29, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote: [...]
    typeset -i rows cols
    stty size | read rows cols

    I no longer trust the size, except on first invocation.

    This is only called initially once in my ksh-code, later I use the
    discipline function to update the variable on LINES changes:

    function LINES.set { .kosta.rows=${.sh.value} ; .kosta.update ;}

    (That's Kornshell-specific, of course.)

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Wed Aug 30 00:02:08 2023
    On 2023-08-29, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 30.08.2023 01:42, Kaz Kylheku wrote:
    On 2023-08-29, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    [...]
    typeset -i rows cols
    stty size | read rows cols

    I no longer trust the size, except on first invocation.

    This is only called initially once in my ksh-code, later I use the
    discipline function to update the variable on LINES changes:

    function LINES.set { .kosta.rows=${.sh.value} ; .kosta.update ;}

    (That's Kornshell-specific, of course.)

    By the way, likewise, my code check $LINES and $COLUMNS to detect
    a change:


    [ $LINES -eq $basta_old_lines -a \
    $COLUMNS -eq $basta_old_cols ] || basta.prepare_term

    but beyond that basta.prepare_term function won't rely on $LINES; the
    terminal is queried to determine its size, from which we subtract the
    number of lines needing to be protected, and set LINES accordingly,
    punching that into the TTY with stty.

    I found that not trusting LINES eliminated the rare glitches I was still
    seeing where the scrolling region got set wrong, clobbering the status.

    A good change also was not doing the cursor position check when there is pending input in the TTY. In Bash we can use the read function to detect whether input is pending. If input from the user is pending, we can't
    be starting a transaction to query the terminal; we would have to read
    and discard the user's input to get to the termina's response.

    Skipping the check is likely okay because most of the time the cursor
    does not stray into the status line.

    If the user has run some program that has wrecked the scroll region,
    *and* is rapidly typing ahead, then we won't catch the bad cursor
    position.

    I think I have it debugged close to as well as it can be; I put
    almost 30 commits into this thing since I started the git repo.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Wed Aug 30 12:46:50 2023
    On 30.08.2023 02:02, Kaz Kylheku wrote:
    [...]
    I think I have it debugged close to as well as it can be; I put
    almost 30 commits into this thing since I started the git repo.

    It's amazing how many things (including edge and corner cases)
    have to be considered to make a piece of software bulletproof,
    even for a function that appears so simple at first glance as
    a shell status line. :-)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Fri Sep 8 16:04:10 2023
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    [...]
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    [...]

    One more question; is there any neat way to get the "net" length of a
    string that carries ANSI control characters (without duplicating the
    code as above)?

    Initially I wanted to add [ANSI-escape] colors to my "kosta" variant
    (e.g. somthing like <red>errors<blue>shlvl<green>path<yellow>...),
    but lacking a good idea I abstained from supporting that feature.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Harnden@21:1/5 to Janis Papanagnou on Fri Sep 8 19:13:05 2023
    On 08/09/2023 15:04, Janis Papanagnou wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    [...]
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    [...]

    One more question; is there any neat way to get the "net" length of a
    string that carries ANSI control characters (without duplicating the
    code as above)?

    Initially I wanted to add [ANSI-escape] colors to my "kosta" variant
    (e.g. somthing like <red>errors<blue>shlvl<green>path<yellow>...),
    but lacking a good idea I abstained from supporting that feature.

    Janis


    I was also having problems with printf and ansi/utf8.

    Don't have a ksh solution, but a short bit of C ... maybe it's useful.

    eg:

    $ R_ARROW=$(/bin/echo -en "\xe2\x86\x92")
    $ RED=$(/bin/echo -ne "\x1b[38;5;1m")
    $ GREEN=$(/bin/echo -ne "\x1b[38;5;2m")
    $ BOLD=$(/bin/echo -ne "\x1b[1m")
    $ RESET=$(/bin/echo -ne "\x1b[0m")

    $ TEST="${GREEN}This is a ${R_ARROW} ${BOLD}${RED}test!${RESET}"

    $ echo ${#TEST}
    43

    It isn't 43 - there are only 17 printable characters, so printf gets
    confused:

    $ printf "%s%50s%s\n" ">>" "${TEST}" "<<"
    This is a → test!<<


    $ printf "%s%s%s\n" ">>" "$(str_pad "${TEST}" 50)" "<<"
    This is a → test!<<

    or left justifed:
    $ printf "%s%s%s\n" ">>" "$(str_pad "${TEST}" -50)" "<<"
    This is a → test! <<

    Anyway ...

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>

    static int str_length(const char *s)
    {
    int length = 0;

    while ( *s )
    {
    /* ansi escapes don't use any glyphs */
    if ( *s == 0x1b )
    {
    s++;

    while ( *s )
    {
    if ( islower(*s) )
    break;

    s++;
    }

    s++;
    continue;
    }


    if ( (*s & 0xc0) != 0x80 ) /* don't count uft8 continuation
    bytes */
    length++;

    s++;
    }

    return length;
    }

    int main(int argc, char *argv[])
    {
    int pad;
    char *endptr;
    int len;
    int p;

    if ( argc != 3 )
    {
    fprintf(stderr, "Usage: %s <str> <pad>\n", argv[0]);
    return EXIT_FAILURE;
    }

    if ( argc == 2 && ( strcmp(argv[1], "-?") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "--help") == 0 ) )
    {
    printf("Usage: %s <str> <pad>\n", argv[0]);
    return 0;
    }

    pad = strtol(argv[2], &endptr, 10);

    if ( *endptr != '\0' )
    {
    fprintf(stderr, "%s: \"%s\" is not a number!\n", argv[0], argv[2]);
    return EXIT_FAILURE;
    }

    if ( abs(pad) > 200 )
    {
    fprintf(stderr, "%s: pad, %d, is out of range. [-200..200]\n", argv[0], pad);
    return EXIT_FAILURE;
    }

    len = str_length(argv[1]);

    if ( pad == 0 || len >= abs(pad) )
    pad = 0;
    else
    p = abs(pad) - len;

    if ( pad > 0 )
    printf("%*c", p, ' ');

    printf("%s", argv[1]);

    if ( pad < 0 )
    printf("%*c", p, ' ');

    return 0;
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Sat Sep 9 03:03:20 2023
    On 2023-09-08, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    [...]
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    [...]

    One more question; is there any neat way to get the "net" length of a
    string that carries ANSI control characters (without duplicating the
    code as above)?

    In the current version of the code, I see I had abandoned the
    above approach. I have only one string which is rendered.
    There is still duplication, but only of the escapes:

    local status_esc="$esc[7m$esc[m"

    while true; do
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_len=$((${#status} - ${#status_esc}))
    [ $status_len -le $COLUMNS ] && break
    pwd=${pwd#/}
    pwd=/${pwd#*/}
    dots='...'
    [ "${pwd#/*/}" == "$pwd" ] && break
    done

    status_esc is a string consisting of all the escape material
    which occurs in status, so has to be maintained in parallel.

    While that sucks, it sucks less than repeating the $(date ...)
    and $HOSTNAME and other details.

    In the above loop, I should be using a cached sample of the
    $(date ...) too.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Sat Sep 9 02:53:25 2023
    On 2023-09-08, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    [...]
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    [...]

    One more question; is there any neat way to get the "net" length of a
    string that carries ANSI control characters (without duplicating the
    code as above)?

    There could be ways, like using a function like $(ansi "$esc[m").

    Let's recall that the code is Bash specific.

    Bash has dynamically scoped local variables, allowing us to cleanly
    control the behavior of functions with flags, without resorting
    to blatant global variables.

    local ansi_enable=y

    such that $(ansi ...) will output its argument, rather than
    consume it and output nothing. When the scope terminates
    the prior value of ansi_enable is restored automatically.

    Now the thing is that I have a code smell: the double invocation
    of date! In status_nohl, I'm calling date a second time, only
    to get the format for the purposee of counting length.

    I would prefer some solution in which we mark up the ANSI stuff
    somehow like:

    @@$esc [m@@.

    For the purposes of rendering the codes, we filter out the expanded
    version of @@$esc[m@@ (and everything similar) into just $esc[m.

    For the purposes of calculating size, we filter @@$esc[m@@ into
    nothing.

    Bash should be able to do this ${var/this/that} substitution;
    I will look into it.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Sat Sep 9 05:39:01 2023
    On 09.09.2023 04:53, Kaz Kylheku wrote:
    On 2023-09-08, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    [...]
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME $dots$pwd" >>> local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    [...]

    One more question; is there any neat way to get the "net" length of a
    string that carries ANSI control characters (without duplicating the
    code as above)?

    There could be ways, like using a function like $(ansi "$esc[m").

    Yes, any neat interface that prevents duplication would be nice.


    Let's recall that the code is Bash specific.

    I took the code from your bash code for explanation only, but I
    have the same issue with my ksh variant; it's a general task
    independent from these status line projects.


    Bash has dynamically scoped local variables, allowing us to cleanly
    control the behavior of functions with flags, without resorting
    to blatant global variables.

    local ansi_enable=y

    such that $(ansi ...) will output its argument, rather than
    consume it and output nothing. When the scope terminates
    the prior value of ansi_enable is restored automatically.

    The interface question is crucial for a neat implementation.

    For ksh, maybe defining a discipline function for a "no_ansi"
    variable would be a sensible implementation, say,

    no_ansi=${status} ; printf "${no_ansi}"

    with function no_ansi.set removing the ANSI escapes using a
    variable substitution (or a C-compiled shell built-in)?

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Janis Papanagnou on Sat Sep 9 05:47:27 2023
    On 09.09.2023 05:39, Janis Papanagnou wrote:

    For ksh, maybe defining a discipline function for a "no_ansi"
    variable would be a sensible implementation, say,

    no_ansi=${status} ; printf "${no_ansi}"

    no_ansi=${status} ; printf "${#no_ansi}"

    Typo; I wanted to get the length of the string for the status line
    calculation, but both calls are useful and the logic would anyway
    be in the no_ansi.set discipline function.

    Hmm.. - pondering also about a type definition with that property
    (vs. a variable specific definition).


    with function no_ansi.set removing the ANSI escapes using a
    variable substitution (or a C-compiled shell built-in)?

    Janis


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Richard Harnden on Sat Sep 9 05:22:57 2023
    On 08.09.2023 20:13, Richard Harnden wrote:
    On 08/09/2023 15:04, Janis Papanagnou wrote:
    On 03.08.2023 20:31, Kaz Kylheku wrote:
    [...]
    local status="$esc[7m$(date +%m-%d/%H:%M)$esc[m $HOSTNAME
    $dots$pwd"
    local status_nohl="$(date +%m-%d/%H:%M) $HOSTNAME $dots$pwd"
    [ ${#status_nohl} -le $COLUMNS ] && break
    [...]

    One more question; is there any neat way to get the "net" length of a
    string that carries ANSI control characters (without duplicating the
    code as above)?

    Initially I wanted to add [ANSI-escape] colors to my "kosta" variant
    (e.g. somthing like <red>errors<blue>shlvl<green>path<yellow>...),
    but lacking a good idea I abstained from supporting that feature.

    I was also having problems with printf and ansi/utf8.

    Don't have a ksh solution, but a short bit of C ... maybe it's useful.

    eg:
    [snip samples ]

    Anyway ...
    [ snip C-code ]

    I'm looking for any ideas or insights, so thanks for your idea and
    C-code.

    I see that you seem to have used a heuristic, triggering with Esc
    and terminating with any lower-case character. - This is (almost)
    exactly what I tried in shell with patterns (operating on text
    that uses an \E escape literal for Esc)...

    plain=${var//\\E\[*([!a-z])[a-z]} ; len=${#plain}

    I had used "Esc[" (for my cases) but "Esc]" might also be relevant
    for a more general application of the pattern (an extension to both
    brackets is straightforward, of course). I had included the brace
    because I wasn't sure whether an Esc might occur in other contexts
    as well alone. (I wasn't sure, though, whether such patterns are
    just a hack or sufficient for all ANSI escapes and not conflicting
    with other applications.)

    The C-code reminds me that any clumsy solution could also be hidden
    by creating a shell built-in from a C-function. (But that relocates
    the task just to another place.)

    Instead of a C program another option could also be a sed-script.
    (Performance should not be an issue here.)

    Initially I was hoping for a _standard_ tool (like col(1), a tool
    to filter reverse line feeds from input), since shell patterns (or
    C-code) appears to me to be a bit bulky. (But probably there isn't
    any neat and bulletproof standard tool existing...?)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)