-- Leo's gemini proxy

-- Connecting to thrig.me:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

TERM Tweaking for Fun and maybe not Profit


One may see advice to use some TERM environment setting or the other. This can be good or bad advice in that the custom TERM may fix or break something, or fix one thing and break another. Maybe the breakage (if any) may not be noticed because you do not miss some feature, or never hit some key.


Another method is to modify an existing or to create a custom terminal definition that has or lacks the features you want or do not want. This too has downsides: it takes time, the custom terminal definition may need to be present on all the systems you use, shared accounts may be complicated if two users disagree on the necessary terminal, vendors may improve their terminal defintions whose changes you now have to merge into yours, etc.


terminfo(5) is assumed, the older termcap(3) is ignored. First up, have a look at the current terminal definition. These on OpenBSD live under the /usr/share/terminfo directory, though one could have custom definitions in ~/.terminfo or other directories.


infocmp


    $ echo $TERM
    xterm
    $ infocmp
    #       Reconstructed via infocmp from file: /usr/share/terminfo/x/xterm
    xterm|xterm terminal emulator (X Window System),
            am, bce, km, mc5i, mir, msgr, npc, xenl,
            colors#8, cols#80, it#8, lines#24, pairs#64,
            acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
            bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
            clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r,
    ...

So what we have here is a bunch of line noise, or attributes (colors, etc) and features (bold, clear, etc) and the corresponding character sequence to emit to cause the terminal to carry out the feature. xterm(1) and many other implementations use "Xterm Control Sequences", though other software (or hardware!) will vary. Your TERM will likely be different; tmux(1) may use a screen terminfo file, and some like the *-256color variants while I go out of my way to disable colors.


The definitions could be modified or created at the system level, though I've never done that as then you need to manage vendor updates, and potentially other users unhappy with a change to a standard definition. Instead, one can compile a terminal definition into a new directory; ~/.terminfo is common, though the TERMINFO and other environment variables can change this.


    $ TERM=screen TERMINFO=/usr/share/terminfo infocmp > blah
    $ tic -o ~/.terminfo blah
    $ rm blah
    $ find .terminfo -type f
    .terminfo/s/screen

That's an expensive way to copy a definition; typically one would use this method to modify the blah file between the infocmp(1) and tic(1). This process will be necessary on any system that generates the wanted (or undesired) escape sequences; a modified terminfo database on your local system will be unknown to something you SSH to; the escape sequences are generated on the remote system, so will need to be corrected there. NFS or configuration management such as Ansible is typical to either have a shared home directory or means to automate configuration file changes across a large number of systems (where large for some is any number greater than zero). You could also simply rsync(1) your local ~/.terminfo directory up to that OpenBSD virt you have, and not customize any other systems you rarely deal with.


infocmp(1) has facilities for comparing definitions, or you could use diff(1) or similar. diff(1) might get pretty hard to read, though.


    $ TERMINFO=/usr/share/terminfo TERM=screen infocmp > sys
    $ TERMINFO=~/.terminfo TERM=screen infocmp > me
    $ diff sys me
    --- sys Thu Aug 10 10:16:44 2023
    +++ me  Thu Aug 10 10:16:57 2023
    @@ -1,7 +1,7 @@
    -#      Reconstructed via infocmp from file: /usr/share/terminfo/s/screen
    +#      Reconstructed via infocmp from file: /home/jmates/.terminfo/s/screen
     screen|VT 100/ANSI X3.64 virtual terminal,
            am, km, mir, msgr, xenl,
    -       colors#8, cols#80, it#8, lines#24, ncv@, pairs#64,
    +       cols#80, it#8, lines#24, ncv@,
            acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
            bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, civis=\E[?25l,
            clear=\E[H\E[J, cnorm=\E[34h\E[?25h, cr=\r,
    ...

Color support has accidentally been removed, among other changes. Monotype monochrome, baby!


infocmp(1) also supports diff, which may be more cromulent than the above. Here we compare screen from the system directory with the one in my local directory.


    $ infocmp -A /usr/share/terminfo -B ~/.terminfo screen screen
    comparing screen to screen.
        comparing booleans.
        comparing numbers.
            colors: 8, NULL.
            pairs: 64, NULL.
    ...

If you do modify terminfo files, you'll probably want a script that makes them, and to periodically update your ~/.terminfo from the vendor definitions, as the vendor may on occasion make updates. One way to automate this would be to checksum the vendor s/screen file or whatever, and emit a notification when that checksum changes in the operating system. On notification, review what the vendor changed and if necessary update your definition to include the vendor changes. Probably I should write that script.


How to change what??


This can be learned by inspecting what programs emit and the relevant terminfo files. Is anything annoying that you might want to change? The program can be run under different TERM settings to see if what it emits changes, and also under different terminals (xterm, urxvt, or a real actual terminal you found somewhere) and terminal multiplexers (screen, tmux). The raw terminal output will be useful, in addition to any relevant visual changes between TERM definitions. The terminal output might be run through a hex viewer (hexdump(1), xxd, etc) or a custom tool could be used to markup common escape sequences and to hexify non-printable characters. script(1) or expect(1) may be useful if the terminal does not let you log the output stream somewhere else.


    $ mutt
    ...
    $ TERM=vt220 mutt
    ...
    $ TERM=vt220 script -c mutt
    ...
    $ hexdump -C typescript | sed 8q
    00000000 53 63 72 69 70 74 20 73 74 61 72 74 65 64 20 6f |Script started o|
    00000010 6e 20 54 68 75 20 41 75 67 20 31 30 20 31 35 3a |n Thu Aug 10 15:|
    00000020 32 34 3a 33 33 20 32 30 32 33 0a 1b 28 42 1b 29 |24:33 2023..(B.)|
    00000030 30 1b 5b 31 3b 32 34 72 1b 5b 6d 0f 1b 5b 34 6c |0.[1;24r.[m..[4l|
    00000040 1b 5b 33 39 3b 34 39 6d 1b 5b 3f 31 68 1b 3d 1b |.[39;49m.[?1h.=.|
    00000050 5b 32 34 3b 31 48 0d 1b 5b 3f 31 6c 1b 3e 1b 5b |[24;1H..[?1l.>.[|
    00000060 31 3b 32 34 72 1b 5b 6d 0f 1b 5b 34 6c 1b 5b 48 |1;24r.[m..[4l.[H|
    00000070 1b 5b 4a 1b 5b 32 34 64 52 65 61 64 69 6e 67 20 |.[J.[24dReading |

Not very easy to read? Maybe try a custom debugging tool, short for "terminal explainer" as based on "mansplain":


termsplain.l


    $ CFLAGS=-lcurses make termsplain
    lex  -o lex.termsplain.c termsplain.l
    cc -lcurses   -o termsplain lex.termsplain.c  -ll
    rm -f lex.termsplain.c
    $ ./termsplain typescript | fmt | sed 3q
    Script started on Thu Aug 10 15:24:33
    2023\n\e(B\e)0\e[1;24r\e[m\x0F\e[4l\e[39;49m\e[?1h\e=\e[24;1H\r\e[?1l\e>\e[1;24r\e[m\x0F\e[4l\e[H\e[J\e[24dReading
    /home/jmates/mail/inbox...\rScanning /home/jmates/mail/inbox...

And then you would run the tool under script(1) under a different or new TERM, and then compare the saved outputs for differences, etc. Process tracing and inspecting the source code may also be necessary to chase down how a program is generating what.


Example Changes


One might want inverse instead of something else (italics?) for the search highlight in less(1).


    $ man terminfo | grep mso
              dest_tabs_magic_smso        xt        xt     tabs destructive,
                                                           smso or rmso
             enter_standout_mode         smso      so     begin standout mode
             exit_standout_mode          rmso      se     exit standout mode
    ...

Assuming the terminal uses Xterm Control Sequences, then


    #!/bin/sh
    set -e
    TF=`mktemp`
    infocmp |
    sed -e 's/smso=[^,]*/smso=\\E[7m/;s/rmso=[^,]*/rmso=\\E[27m/' \
    > "$TF"
    tic -o ~/.terminfo/ -- "$TF"
    rm -- "$TF"

This will need to be run for each TERM you use, e.g. xterm, screen, any of those with colors, etc, or you could have a loop in the script that iterates over all the definitions you want to modify.


A temporary file is not strictly necessary, but might be good to edit or review with diff(1) before passing the data to tic(1).


Renaming a Definition


This may be handy if you want to use the same directory tree for a modified terminal defintion. Simply modify the leading field which is the name (and alternate names) of the definition. Study the existing definitions to see how this works.


    $ find ~/.terminfo | grep myterm
    $ TERM=vt220 infocmp | sed '2s/.*/myterm,/' | tic -o ~/.terminfo /dev/stdin
    $ find .terminfo | grep myterm
    .terminfo/m/myterm
    $ TERM=myterm ...

References


http://man.openbsd.org/man1/infocmp.1

http://man.openbsd.org/man1/tic.1

http://man.openbsd.org/man5/terminfo.5

Xterm Control Sequences


tags #infocmp #terminal #unix


P.S. terminfo changes may be necessary but not sufficient to prevent an application from doing the wrong thing; an application could simply ignore the terminfo database and emit color codes or whatever that the terminal will honor. In this case either the application will need to be changed to not do that, or the terminal changed to filter out the codes (e.g. xterm*colorMode:false in ~/.Xdefaults) or software written that wraps the application and filters out the bad codes before they reach the terminal.


For example, a terminfo ignoring program might simply be


    $ printf '\e[42mtest\e[m'
    ...

assuming that Xterm Control Sequences are supported by the terminal. A program that honors the color definitions or whether colors are supported might use a library for this, curses for example:


hascolors.c


    $ CFLAGS=-lcurses make hascolors
    cc -lcurses   -o hascolors hascolors.c
    $ yes | ./hascolors | termsplain | fmt
    \e(B\e)0\e[1;24r\e[m\x0F\e[4l\e[H\e[J\e[0;1m\x0Fno
    colors\e[m\x0Fy\e[24;1H\r\e[?1l\e>

P.P.S. on some systems you may need something like


    CFLAGS="`pkg-config --libs --cflags ncurses` -lncurses" make termsplain

or at least I recall Mac OS X requiring that. YMMV. Oh and maybe to change "curses.h" to "ncurses.h" on the relevant include line in termsplain.l, the above code all assumes an OpenBSD shaped system.

-- Response ended

-- Page fetched on Tue May 21 23:18:29 2024