How interactive command line utilities actually work? - command-line

I grasp the basic concept of stdin, stdout, stderr and how programs work with a command line/terminal.
However, I've always wondered how utilities like less in Linux and git log work because they are interactive.
My current thought is that the program does not quit, writes to stdout, listens to key events and writes more to stdout until the user quits pressing q or sends the close signal.
Is my intuition right or is there more to it? Do they detect the amount of lines and characters per line to determine how much to output? And do they clear the screen always before output?

Very interesting question, even if it's a bit open ended.
As mentioned by #tripleee, ncurses is a foundational library for interactive CLI apps.
A bit of history ...
Terminal == "printer" ...
To understand POSIX "terminals", you have to consider their history ... more specifically, you need to think about what a "terminal" meant in the 70's where you'd have a keyboard+printer attached to a serial cable. As you type, a stream of bytes flows to the mainframe which echos them back to the printer causing the printer to echo the command as you type it. Then, typically after pressing ENTER, the mainframe would go off and do some work and then send output back to be printed. Since it basically a glorified dot-matrix printer, we are talking append-only here. There was no "painting the screen" or anything fancy like that.
Try this:
echo -e "Hi there\rBye"
and you'll see it print "Bye there". "\r" is a carriage return with no line feed. A carriage is that printer part that moves back and forth in an old dot-matrix printer and actually does the printing. So if you return the carriage back to the left side of the page and fail to advance the paper (ie, "line feed"), then you will start printing over the current line of text. "terminal" == "printer".
Monitors and software terminals ... still line-oriented
So flash forward a bit and a revolutionary tech called "monitors" comes about where you have a virtualized terminal display that can be rewritten. So like all good tech, we innovated incrementally by adding more and more special escape codes. For example, check out the ANSI color codes. If you are on a terminal that doesn't recognize those escape codes, you'll see a bunch of gibberish in the output from those uninterpreted codes:
"methodName": ESC[32m"newInstance"ESC[39m,
"fileName": ESC[32m"NativeConstructorAccessorImpl.java"ESC[39m,
"className": ESC[32m"sun.reflect.NativeConstructorAccessorImpl"ESC[39m,
"nativeMethod": ESC[33mfalseESC[39m,
When your terminal sees '\033' (ESC), '[', ..., 'm', it interprets it as a command to change the color. Try this:
echo -e "\033[32mgreen\033[39m"
So anyways, that's the history/legacy of the Unix terminal system which was then inherited by Linux and BSD (eg, macs), and semi-standardized as POSIX. Check out termio.h which defines the kernel interface for interacting with terminals. Almost certainly, Linux/BSD have a bunch of more advanced functions that aren't fully standardized into POSIX. While talking about "standards", there's also a bunch of de-facto terminal device protocol standards like the venerable VT100. Software "terminal emulators" like SSH or PuTTY know how to speak VT100 and usually a bunch of more advanced dialects as well.
Shoe-horning "interactive" onto a "line-oriented" interface ...
So ... interactive ... that doesn't really fit well with a line-printer view of the world. It's layered on top. Input is easy; instead of automatically echoing each keystroke typed and waiting for ENTER (ala "readline"), we have the program consume keystrokes as they come in from the TTY. Output is more complex. Even though the fundamental abstraction is a stream of output, with enough escape codes you can repaint the screen by positioning the "caret" and writing new text on top of old text (just like with my "\r" example). None of this is fun to implement yourself, especially when you want to support multiple environments with different escape codes. .... thus the libraries, of which ncurses is one of the most well known. To get an idea of the funky magic done to efficiently render a dynamic screen into a line-oriented TTY, check out Output and Screen Updating from "A Hacker's Guide to NCURSES".

Related

how to read texts on the terminal inside perl script

Is there any way to capture the texts on termianl screen inside a perl script. I know there are some functions like system,exec,backticks but the problem is that they execute commands FROM the script.For ex:- in terminal i write cd/ (or) ls,and after that i run my perl script which will read what was written on termianl screen(in this case, scipt will capture cd/ (or) ls-whichever was given to termianl). I came with one solution that by passing the commands which you wrote on termianl as a command line arguments to the script,but any other way???
Like this maybe:
history | perl -ne 'print $_'
As I understand it, in a situation where you've typed some stuff into a terminal like this:
[tai#littlerobot ~] echo "Hello"
Hello
[tai#littlerobot ~] perl myscript.pl
You want myscript.pl to be able to access the echo "Hello" part, and possibly also the Hello that was that command's output.
Perl does not provide such a feature. No programming language does or can provide such a feature because the process in which your script/program runs has no intrinsic knowledge about what happened in the same terminal before it was run. The only way it could access this text would be if it could ask the currently running terminal, which will have some record of this information (i.e. the scrollback buffer), even if it cannot distinguish between which characters in the text were typed by you, and which are output. However, I know of no terminal that exposes that information via any kind of public API.
So if you want myscript.pl to be able to access that echo "Hello", you'll need to pass it to your script. Piping history to your script (as shown by Mark Setchell in his answer) is one technique. history is a shell built-in, so it has as much knowledge as your shell has (which is not quite the same knowledge as your terminal has). In particular it can give you a list of what commands have been typed in this shell session. However, it cannot tell you about the output generated by those commands. And it cannot tell you about other shell sessions, so doing this in Perl is fairly useless:
my #history = `tcsh -c history`;
The last thing you could try (though it would be incredibly complicated to do) would be to ask the X server (or Windows if running on that operating system) for a screen shot and then attempt to locate which rectangle the current terminal is running in and perform OCR on it. This would be fraught with problems though, such as dealing with overlapping windows.
So, in summary, you cannot do this. It's nothing to do with Perl. You cannot do this in any programming language.

What is the file descriptor that the terminal uses to talk to an interactive program?

echo "abc" | less
less receives the 4 bytes "a", "b", "c", "\x0A" over STDIN, and displays "abc" to the user in its own special way (with the alternate screen mode, etc.).
Then user types "n" at the keyboard, less responds by writing "Pattern not found (press RETURN)" in reverse color at the bottom-left of the terminal. We also see it print a series of tilde's along the left.
Clearly less had to have received input of the "n" character in order to know to attempt to search what was in its search buffer.
Where did it get "n" through? I typed it into the terminal, but is the terminal attached to less's STDIN? If so, wouldn't less just have stuck the "n" into the display buffer? Well, it'd be able to tell, but not if I ran echo "abc" | tail -f for example.
How can I mess around with this file descriptor? I am building a perl program to convert mouse escape codes to key codes so I can make a special pipeline or wrapped command/program that works like a pager but can be mouse-interactive. But I can't figure out how to get in there if less's STDIN is the input file rather than the terminal itself. I really hope it's possible for me to pipe the interactive terminal output through my mapper program.
I also know that it's possible to check if a program's STDIN is a terminal or not, but that doesn't help me to find out
whether a terminal is connected at all, or
how to redirect/pipe/munge that terminal file input that may not be STDIN
Update: Okay I did some digging, it looks like I've been searching for the magical /dev/tty. Hopefully someone can show me how to mess around with it.
Update: Refined question: Do I essentially need to build and run a pty on the specific instances of pagers that I need to translate their events for? Sounds like it will be a pain as I'll need to have all of this process management and stuff. IO:Pty:Easy looks promising though.
if you do
$ lsof -p <pid-of-less>
you will see at the bottom something like
less 30875 user 3r CHR 5,0 0t0 1037 /dev/tty
which shows you how less has open the controlling terminal /dev/tty to read commands.

How to read only one character from stdin without waiting for pressing ENTER?

I have CLI implementation for my VisualWorks application.
There is "IOAccessor defaultClass stdin" and I want to read one character without waiting for user enter press.
It is possible?
in C language there is function like getCh ...
This is not a problem of the language: also the C-language getchar() will have to wait.
It is a matter of the underlying Operating System's tty-code. On UNIX (eg. Linux), you'd have to change the tty-characteristics to unbuffered, using an stty (old-style) or ioctl call.
Under other OSs, similar system calls do similar things.

how to perl for bi-directional communication with dsmadmc.exe?

I have simple web-form with a little js script that sends form values to a text box. This combined value becomes a database query.
This will be sendt to dsmadmc (TSM administrative command line).
How can I use perl to keep the dsmadmc process open for consecutive input/output without the dsmadmc process closing between each input command sent?
And how can I capture the output - this is to be sent back to the same web page, in a separate div.
Any thought, anyone?
Probably IPC::Open2 could help. It allows to read/write to/from both input and output of an external process.
Beware of deadlocks though (i.e. situations where both your code and the app wait for their counterpart). You might want to use IO::Select to handle that.
P.S. I don't know how these modules behave on windows (.exe?..), but from a quick google search it looks like they are compatible.

Get current terminal color pair in Perl

I'm trying to learn about color text in a terminal window. (In case it matters I'm using Terminal.app on OS X.) I'd like to get the terminal's current foreground and background color pair. It looks like I should be able to get this info in a perl script using the Term::Cap library, but the solution eludes me.
In a perl script how would I query the terminal's current foreground and background color pair value?
The feature is outside the scope of terminfo and termcap, because it deals with terminal responses, while terminfo/termcap describe these capabilities:
how to tell the terminal to do some commonly-implemented feature (such as clearing the screen), or
what sequence of characters might some special key (such as Home) send from the keyboard.
While in principle, there is no limitation on what could be part of a terminal description, there was little commonality across terminals back in the 1980s for responses. A few terminals could report specific features, most of those were constant (e.g., version information). Most of the variable responses came after terminfo/termcap had more or less solidified in X/Open Curses. ncurses extends that, but again, most of the extensions are either features or special keys.
Terminal.app implements the most commonly-used features of xterm, but (like other imitators) omits most of the terminal responses. Among other things, xterm provides terminal responses which can tell an application what the window's colors are currently. There are a couple of command-line utilities (xtermset and xtermcontrol) which have been written to use this information (and again, they cover only a part of the repertoire). Using xtermcontrol demonstrates that Terminal.app is lacking in this area — see screenshot:
I don't think most terminals support reporting this -- and it doesn't look like termcap or terminfo have any entries for it. You're just expected to set the color pair as necessary, not to ask the terminal what it's set to right now. In the ECMA-48 standard (better known as "ANSI" after ANSI X3.64, where it used to live), the only command that makes reference to color is SGR "Set Graphic Rendition", which is purely write-only.
Dunno about perl or Terminal.app, but xterm etc will write foreground/background color control sequences to stdin if you output "\033]10;?\07" or "\033]11;?\07" respectively. Check out http://invisible-island.net/xterm/ctlseqs/ctlseqs.html, http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Controls in particular.