Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with user input on linux

Tags:

c

linux

input

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 execution
  • read() (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.

like image 612
Błażej Michalik Avatar asked Dec 26 '15 21:12

Błażej Michalik


People also ask

What is input Linux?

Input/Output (I/O) redirection in Linux refers to the ability of the Linux operating system that allows us to change the standard input ( stdin ) and standard output ( stdout ) when executing a command on the terminal. By default, the standard input device is your keyboard and the standard output device is your screen.

How do you take multiple inputs in Unix?

The multiple inputs can be taken at a time by using the read command with multiple variable names. In the following example, four inputs will be taken in four variables by using the read command. Output: The following output will appear after executing the above script.


2 Answers

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.

See also this & that answers.

like image 129
Basile Starynkevitch Avatar answered Sep 30 '22 15:09

Basile Starynkevitch


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.

like image 28
chqrlie Avatar answered Sep 30 '22 15:09

chqrlie