Reputation: 5055
I'm trying my best on creating a simple shell on linux. Just something I can create to learn how to use basic system calls with.
Scenario: user types in the command, presses tab (so the shell auto-completes his command), the auto-completed command pops out (or suggestions), user presses enter, command evals and executes.
Just like in bash.
I have figured out how to make evals, convert command into tokens, execute it with pipes and stuff. What I can't figure out is the input part. Namely those tab keystrokes.
I know what options I have:
getc()
- get each character separately, store it in a buffer. Can't figure out how to get tab keystrokes, because it pauses execution until it sees '\n' or Ctrl+D. Kinda expensive, since there will be 1 getc() for every character in the command. Also, I will have to deal with buffer reallocation, amortization... boo...scanf("%s")
- too much worrying about buffer overflow. Can't get those tab keystrokes I wan't. Pauses executionread()
(from unistd.h) - could be something I wan't. But I've seen people here that said that it is a real pain to use it to do this. I checked. It is.getline()
- can't get tab keystrokes.I looked into bash source code, to see how it deals with input, and OH MY GOD. There are 450 lines of code dedicated to do this one simple thing (input.c file).
Are there really no simpler solutions than this? I don't want to use ncurses, I don't care about portability, I just wan't to achieve one goal: getting user input and knowing when he pressed tab key. Do it elegantly, with as little effort as possible.
Upvotes: 5
Views: 596
Reputation: 144740
In order to get individual keystrokes from the terminal without any delay or buffering, you must change its mode from cooked to raw. You can do this with the tcsetattr()
function defined in <termios.h>
. Look at the man page for details.
Once you have set the terminal to the appropriate mode, it is wise to use the read()
system call to read data from the terminal handle.
Be aware that you will have to deal with echoing user input and if you start doing advanced stuff like TAB expansion, you will need to implement most of a line editor... Not to mention handling character composition and other oddities the terminal gives you for free. As Basile says, there is no simple handcrafted solution for this, but it will be very instructive to dive into this mess!
If you are pressed for time and this is an assignent, just use readline()
and implement the rest of the shell first. This will be a lot of work already. You can always get back to this later if you still dare.
Upvotes: 4
Reputation: 1
To get some specific auto-completion, you could use the GNU readline library, which is used by bash
.
If you care about terminal full screen I /O (à la vi
or emacs
) consider GNU ncurses.
Terminals are quite complex and arcane things (because they want to emulate weird physical teletypes from the past century). Often, some of the line processing is done in the kernel for the line discipline of the tty. Read the tty demystified webpage. Hence, low level functions à la termios(3) are arcane to use, and you should prefer libraries like readline
or ncurses
.
So, no, there are no simple solutions for terminal I/O for autocompletion, because ttys are complex stuff. See also tty(4) & tty_ioctl(4) & pty(7)
You could also use strace(1) to understand all the complex system calls done by e.g. an interactive shell.
Upvotes: 4