Eiffel: is there a way to print colorized characters into a terminal (console) - linux

Trying to write some logger enhancements I'd like to deal with ANSI codes interpretation, and it seems that it's not working with the standard io.putstring method neither with print I wonder there is a way to do something such as
echo -e "\u001B[31mSome_red_txt" in bash
Seems other languages can do it but I can't in Eiffel.

Using ANSI codes interpretation, you can do
print ("%/27/[31mSome_red_txt%N")
print ("%/27/[1;31mbold red text%/27/[0m%N")
You may check for existing C libraries like ncurses.
Note it will not work on Windows console, as now it does not support ANSI code anymore, so you need to use the Windows API.

To complement Jocelyn's answer, the same ANSI code sequences can be used on Windows with recent update by making sure the console is initialized to handle the sequences:
-- Make sure the console is allocated in a non-console application.
io.output.end_of_file.do_nothing
-- Set console to terminal mode.
initialize_terminal
-- Use ANSI codes to color text.
print ("%/27/[31mSome_red_txt")
where
initialize_terminal
external "C inline"
alias "[
#ifdef EIF_WINDOWS
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut == INVALID_HANDLE_VALUE) return;
DWORD dwMode = 0;
if (!GetConsoleMode(hOut, &dwMode)) return;
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
}
#endif
]"
end
After such initialization, print statements work the same on Windows and Linux.
If output can go not only to the console, but also to a file, a pipe, etc., error status of setting the terminal mode on Windows console can be recorded in the external feature and used later to avoid outputting ANSI sequences in such cases.

Related

Using the "alternate screen" in a bash script

The alternate screen is used by many "user-interactive" terminal applications like vim, htop, screen, alsamixer, less, ... It is like a different buffer of the terminal content, which disappears when the application exits, so the whole terminal gets restored and it looks like the application hasn't output anything.
I'd like to achieve exactly the same thing in my own shell (bash) script, except that it doesn't have to be that portable. I'd stick to linux only and xterm-based terminal emulators; but the solution should use something like tput if it's possible. However, I don't want to use some external scripting language (or even something like C).
Although I don't want to use C (as it should be a bash-script with as few dependencies as possible), I had a look into the source code of less. It seems to use terminfo as the database and looks up the "ti" terminal capability in its initialisation. When removing the line, it doesn't use the alternate sceen, so I assumed that I found the responsible code line.
However, I can't find such a capability in man terminfo. But maybe I'm on the wrong path finding a solution for this. Maybe terminfo / tput isn't my friend.
So (how) can I use the alternate screen in a bash script? Does somebody know a simple application in which source code I may find a hint? (C application or bash script or whatever...)
You can switch to the alternate screen using this command:
$ tput smcup
And back with:
$ tput rmcup
These commands just output the appropriate escape sequences for your terminal. If it is an XTERM they will be equivalent to the (more known but less elegant or portable):
$ echo -e "\e[?1049h"
And:
$ echo -e "\e[?1049l"
For more terminal control commands see man 5 terminfo.
smcup/rmcup are used, but only for the side effect: the escape sequence which switches between normal/alternate screens is usually embedded in those terminfo capabilities -- not always.
Some background is in the xterm faq Why doesn't the screen clear when running vi?
For C console application:
ncurses
Wikipedia:
ncurses (new curses) is a programming library that provides an API which allows the programmer to write text-based user interfaces in a terminal-independent manner.
less uses this library.
A hello world program from here, to compile it in gcc, flag -lncurses is needed.
#include <ncurses.h>
int main()
{
initscr(); /* Start curses mode */
printw("Hello World !!!"); /* Print Hello World */
refresh(); /* Print it on to the real screen */
getch(); /* Wait for user input */
endwin(); /* End curses mode */
return 0;
}
To build:
$ gcc hello-world-ncurses.c -lncurses

Is it possible to display text in a console with a strike-through effect?

I have already looked into ANSI escape codes, but it looks like only underlining is supported.
Do I miss something or is there another option?
If it is not possible, is there something equivalent in the meaning of "this is deprecated"?
According to the ECMA-48 standard for terminals, SGR (Select Graphic Rendition) code number 9 is supposed to enable crossed-out text. However, the ANSI escape code wikipedia page says that it's not widely supported, and I'm not aware of any that do. I'd suspect that's because DEC's VTxxx series didn't support it.
An alternative solution for applications written in C11 or C++11 is to use the Unicode combining long stroke overlay character.
In C++11 you can write code something like this:
#include <iostream>
#include <string>
std::string strikethrough(const std::string& text) {
std::string result;
for (auto ch : text) {
result.append(u8"\u0336");
result.push_back(ch);
}
return result;
}
int main() {
std::cout << strikethrough("strikethrough") << std::endl;
}
The code prefixes each character in the input text with the stroke overlay \u0336. Note that the function assumes that text is encoded in a singlebyte encoding such as ASCII or Latin. If the input is in UTF-8 it must be converted to UTF-32 first to get the character boundaries.
The output then is s̶t̶r̶i̶k̶e̶t̶h̶r̶o̶u̶g̶h in a UTF-8 capable terminal. I don't know why the first character has no strike-through, must be a terminal issue. I could work around this by printing at least one character before the strikethrough function call.
The Unicode solution also generates a slightly different locking in my terminal (terminator) compared to the ANSI escape sequence mentioned above. The former renders the line exactly in the middle of the text whereas the latter renders it a bit below.
This works for me.
$ echo -e `echo "this is a strikethrough text" | sed 's/.\{1\}/&\\\u0336/g'`
couldn't find anything easier than this:
$ echo -e "\e[9myour text goes here\e"

Remove ANSI codes when storing script output

Some programs makes beautiful progressbars and stuff using ANSI escape sequences. That's nice.
What's not nice though is that if i put the output of that kind of program into a file and then try to view it it's filled with strange escape sequences.
Is there a way to strip away all the ANSI codes while logging?
I usually log the output of a script this way:
./script >> /tmp/output.log
Try:
$ TERM=dumb ./script >> /tmp/output.log
If that doesn't work, it's because the ANSI codes have been hard-coded into the script, so there is no easy way to remove them. If it does, it's because it's doing the right thing, delegating things like pretty output to libncurses or similar, so that when you change the TERM variable, the library no longer sends those codes.

Redraw screen in terminal

How do some programs edit whats being displayed on the terminal (to pick a random example, the program 'sl')? I'm thinking of the Linux terminal here, it may happen in other OS's too, I don't know. I've always thought once some text was displayed, it stayed there. How do you change it without redrawing the entire screen?
Depending on the terminal you send control seuqences. Common sequences are for example esc[;H to send the cursor to a specific position (e.g. on Ansi, Xterm, Linux, VT100). However, this will vary with the type or terminal the user has ... curses (in conjunction with the terminfo files) will wrap that information for you.
Many applications make use of the curses library, or some language binding to it.
For rewriting on a single line, such as updating progress information, the special character "carriage return", often specified by the escape sequence "\r", can return the cursor to the start of the current line allowing subsequent output to overwrite what was previously written there.
try this shellscript
#!/bin/bash
i=1
while [ true ]
do
echo -e -n "\r $i"
i=$((i+1))
done
the -n options prevents the newline ... and the \r does the carriage return ... you write again and again into the same line - no scroling or what so ever
If you terminate a line sent to the terminal with a carriage return ('\r') instead of a linefeed ('\n'), it will move the cursor to the beginning of the current line, allowing the program to print more text over top of what it printed before. I use this occasionally for progress messages for long tasks.
If you ever need to do more terminal editing than that, use ncurses or a variant thereof.
There are characters that can be sent to the terminal that move the cursor back. Then text can be overwritten.
There is a list here. Note the "move cursor something" lines.
NCurses is a cross-platform library that lets you draw user interfaces on smart terminals.
Corporal Touchy has answered how this is done at the lowest level. For easier development the curses library gives a higher level of control than simply sending characters to the terminal.
To build on #Corporal Touchy's answer, there are libraries available that will handle some of this functionality for you such as curses/ncurses
I agree with danio, ncurses is the way to go. Here's a good tutorial:
http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/

Why do my keystrokes turn into crazy characters after I dump a bunch of binary data into my terminal?

If I do something like:
$ cat /bin/ls
into my terminal, I understand why I see a bunch of binary data, representing the ls executable. But afterwards, when I get my prompt back, my own keystrokes look crazy. I type "a" and I get a weird diagonal line. I type "b" and I get a degree symbol.
Why does this happen?
Because somewhere in your binary data were some control sequences that your terminal interpreted as requests to, for example, change the character set used to draw. You can restore everything to normal like so:
reset
Just do a copy-paste:
echo -e '\017'
to your bash and characters will return to normal. If you don't run bash, try the following keystrokes:
<Ctrl-V><Ctrl-O><Enter>
and hopefully your terminal's status will return to normal when it complains that it can't find either a <Ctrl-V><Ctrl-O> or a <Ctrl-O> command to run.
<Ctrl-N>, or character 14 —when sent to your terminal— orders to switch to a special graphics mode, where letters and numbers are replaced with symbols. <Ctrl-O>, or character 15, restores things back to normal.
The terminal will try to interpret the binary data thrown at it as control codes, and garble itself up in the process, so you need to sanitize your tty.
Run:
stty sane
And things should be back to normal. Even if the command looks garbled as you type it, the actual characters are being stored correctly, and when you press return the command will be invoked.
You can find more information about the stty command here.
You're getting some control characters piped into the shell that are telling the shell to alter its behavior and print things differently.
VT100 is pretty much the standard command set used for terminal windows, but there are a lot of extensions. Some control character set used, keyboard mapping, etc.
When you send a lot of binary characters to such a terminal, a lot of settings change. Some terminals have options to 'clear' the settings back to default, but in general they simply weren't made for binary data.
VT100 and its successors are what allow Linux to print in color text (such as colored ls listings) in a simple terminal program.
-Adam
If you really must dump binary data to your terminal, you'd have much better luck if you pipe it to a pager like less, which will display it in a slightly more readable format. (You may also be interested in strings and od, both can be useful if you're fiddling around with binary files.)

Resources