The 2024 Wheel Reinvention Jam just concluded. See the results.

Making a console app

Hi,

I recently saw a video of Jon saying a simple console/terminal app wouldn't be that hard to make over a weekend. I was wondering how you would go about making one?

For unix style commands like mkdir, cd etc. would you call out to a seperate program to do this, or is it up to the app to mkdir etc.? For more complex commands & obscure commands it seems like it would get a lot harder.

Also for the PATH variable, it would be up to the app to parse it, and call the corresponding exes etc.? Also things like symlinks in unix, would the app have to handle all this as well (creating them, and presumably writing them into a file somwhere)?

I think knowing where the boundary is between what the console app is doing & what the operating system is doing is the hardest thing.

Any clarification would be very appreciated, also think it would be a cool handmade network project to make a console.

Thankyou,
Oliver

Edited by Oliver Marsh on Reason: Initial post
As far as I understand it, by the console/terminal app, we're just talking about a window that processes text input, runs the commands in the input and renders the input and results back to the screen.

In your specific example, mkdir is a program, so what the terminal needs to do is call the program, pass the inputs and report the outputs back to the user (print them on the terminal).

You can start getting fancy from there: command history, history search, different input modes (e.g. vim mode), color output, remote terminal (terminal to another physical computer), autocomplete, sky's the limit.
Check out st. It is very simple, but fully functional self-contained terminal. No dependencies aside standard syscalls and X11 for drawing/windowing. Main source code is only around 5K lines (2.5K for terminal functionality, 2K for X11 window management).

Terminals do not handle mkdir, cd, symlink commands, or process command history, autocomplete or process environment variables. Shell does (bash, zsh, ...). Terminal simply runs shell and sends to it keyboard input, and reads its output to display on screen. Basically you execute process in background (fork) and communicate to it over file descriptor.

Another simple terminal (the one I use) is termite. Just one file, ~2K lines, but it delegates terminal processing to vte3 library that uses GTK/cairo to drawing/window processing. Termite source code is a good reference of what terminal does, but if you want to understand how it does it, you need to look at vte source code.

Edited by Mārtiņš Možeiko on
Thanks for the info & the links very, was hoping it was like that where you just pass the commands to another program.
Another question, is the shell an exe that lives in /bin, for example /bin/bash ? And what's the equivalent on windows & where does it live on windows?

Shell executable can be anywhere. It is just a regular executable. Nothing prevents you to put bash executable into your ~/ home folder. Default location depends on your Linux distribution. Usually in /bin/bash. But could be also /usr/bin/bash.

On windows cmd.exe or powershell.exe handles shell things (mkdir, cd, etc...). ConHost.exe handles GUI things. Here's an overview how it works on Windows: https://devblogs.microsoft.com/co...-line-inside-the-windows-console/

There's also a new terminal called Terminal which open-source. Its repository contains source of ConHost.exe.

And now with WSL in Windows 10, you can also use bash to run your shell (in any terminal - old ConHost, or new Terminal).

Terminal I use is ConEmu (open-source). It is very customizable and very rich with features. I think I use it close to 10 years now. Main reason I use it is its integration with FarManager. Using Far with ConEmu is much better experience than with default Windows terminal.

Edited by Mārtiņš Možeiko on
Thanks Martins, That link with the blog post was very helpful.
You'll remember that, decades ago, people hooked up to a single, central computer using dumb terminals. Thus programs living on the central computer were expected to communicate with terminal screens. Like, if a C program printed a specific code to a terminal then it could control its behavior (cursor positioning, instructions to clear the screen, change colors, and so on.) The class of programs that have a terminal device as their output are called terminal-oriented. Today's terminals are emulators that mimic the old ones, such that terminal-oriented programs like the vi editor -- from 1976 -- still work today.

These days, you can write a program that opens up a pseudoterminal (pty), which is a virtual device with a bi-directional channel for inter-process communication. On one end of the channel, you have the pty "master," and on the other is the "slave." It is the slave end that is special, because it simulates a physical terminal device. Here's the diagram:



So if you're writing a terminal, you want to understand its place in the diagram above. The driver program is your actual console app which should employ a pseudoterminal (the pty master & slave pipe) and does the fork / exec operations necessary to ensure it has master, and its child process the slave. The child would become the terminal-oriented program like the vi editor, or, more generally, a shell such as bash. They write their output to the slave, which pretends to be a real terminal and can process or discard all the codes on your behalf.

Hopefully I got that right. I recommend reading The Linux Programming Interface book, last chapter. It's where I got this diagram from and gives you the basic source code to begin writing a terminal.

EDIT: Your driver will need to process certain codes itself, but getting into the nitty gritty is out of the scope of this post.

Edited by Abner Coimbre on Reason: Simple clarifications.
Thanks Abner, that explanation helped out, theres more i think ive got to understand 😜 im going to have a look into the book

Edited by Oliver Marsh on
Hello,

Everything has been said above, I just wanted to add that making a terminal over a weekend is certainly doable, as long as you're American and can just display ASCII text and get away with it.

After a quick search on Qwant, I found:
=>A terminal that supports Arabic on Github: https://github.com/zakariakov/elokab-terminal
=>...And bug reports for Windows Terminal and VSCode saying that Arabic text isn't handled properly:
https://github.com/microsoft/terminal/issues/1592
https://github.com/Microsoft/vscode/issues/72581

Now, nothing forces you to display properly every Terrestrial language in your terminal on the first go, I just think it's good to know that text handling probably won't be the easiest part of the project. Font rendering itself can be quite complicated, you can see it just by browsing the 4coder forums.
Oh, I made some updates for my previous post—mainly summarizing better—and have other comments below.

OliverMarsh
I think knowing where the boundary is between what the console app is doing & what the operating system is doing is the hardest thing.


Right, that's what I was trying to explain earlier. However the hardest thing might be deciding whether to break from the traditional diagram I showed you. Modern terminal emulators will bend over backwards to have vim, ssh, emacs, and other terminal-oriented programs fully supported under multiple emulation modes by default (or they will lose users in droves.) And if you forego connecting to bash, then you just lost your session leader and complex signal-handling features, meaning you assume you can do a better job.

If one goes the traditional route, it would be wise to stick to VT100 emulation at first; there's a famous test suite to prove one's terminal is VT100-compatible.

Guntha
Font rendering itself can be quite complicated, you can see it just by browsing the 4coder forums.


For sure. I would definitely start with an imgui library and some monospaced font baking.

Edited by Abner Coimbre on Reason: More comments.
Thanks Abner, I'm going to have a go to see if I can communicate between cmd.exe, I'll be referring back to the info here 🙂
What Abner said mostly relates to Linux and similar OS terminals. Windows cmd.exe historically worked very differently. That's why there were no good terminal replacements on Windows. It was not possible in sane way. Not sure if this is better nowadays. Maybe it is (haven't used these new APIs, so I cannot say anything about them).
That is correct, I mostly meant Linux, but like you point out Windows is catching up with ConPTY.

To be fair to the original post, you can indeed write something simple over a weekend -- only if you drop the old protocol, never consider ioctl, and completely ignore being backwards-compatible. And I'm not being flippant; there's an argument to be made about throwing away design assumptions made decades ago. However, the danger there is ending up with the equivalent of fishshell (a novelty, but not meaningfully replacing bash.)