1

I am trying to implement a simple shell in linux and one of the features it should have is to enable to user to press <ctrl+D> and make it stop whatever it is doing - Basicly exactly what <ctrl+C> does in Bash.

Now I was wondering on the easiest way to do this, and I was thinking of some kind of key listener which would make the current task stop. Now the only way I could think of doing this would be to have a separate thread which would force stop and return the main thread to the base state, where it can take new input.

A separate thread would be the only way to "constantly" listen for the correct keypress.

I was hoping for some thoughts on this and possibly a better way to go about it.

[Edit] I am trying to make a simple shell which can execute external programs, print/change directory, print/set path, print command history, invoke commands from history and print/set/remove command aliases. The CTRL-D is meant to be the equivalent of the CTRL-C in Bash, which allows the user to immediately exit a currently running program, not to exit the shell itself. [/Edit]

wsmccusker
  • 2,631
  • 2
  • 14
  • 8
  • This seems a perfectly reasonable, real, constructive question to me. The proposed/desired keyboard mapping is unusual, but that doesn't make it wrong; it is just different. You might perhaps ask "what have you tried", but the question is basically reasonable. – Jonathan Leffler Feb 15 '13 at 04:15

3 Answers3

1

Why don't you just handle Ctrl-C?

Here is just one of many SO disussions on trapping the signal: Catch Ctrl-C in C

Ctrl-D generally represents EOF on standard input. You shouldn't mess with it.

Community
  • 1
  • 1
paddy
  • 60,864
  • 6
  • 61
  • 103
  • Your shell should continuously be prompting the user for input and reading and processing any user input, until the user either types ‘exit’ or ‘-D’. Note that in the latter case, we are in fact closing the standard input, which will give an error when trying to read from it. So, you need to use this as a way of exiting the loop Unfortunately it is in the spec I have been given. – wsmccusker Feb 14 '13 at 23:00
  • 1
    I think you've misunderstood the instructions. Try using "CTRL-D" in a regular shell. It will exit the shell. It should do that. However, if you use CTRL-D in a program that is currently running, the shell won't be receiving that input, and you shouldn't be trying to either, because it means something to the app running at the time. – Mats Petersson Feb 14 '13 at 23:29
  • I think I have understood your meaning, but if I was being obtuse I'm sorry. I have edited the question to make it clearer what I am trying to ask. – wsmccusker Feb 14 '13 at 23:51
1

If you want the Control-D character to generate an interrupt for you, then:

  1. You need to map the EOF character to something other than Control-D.
  2. You need to map the interrupt character to Control-D.

You do this in POSIX with the <termios.h> header and the functions:

You'd retrieve the current attributes in a struct termios using tcgetattr(). You'd make a copy of the structure, and modify (for sake of argument) the copy, changing the elements of the c_cc array indexed by VINTR and VEOF (plus any other changes you want to make), and then setting the new attributes using tcsetattr(). You'd also arrange to ensure that you restore the original terminal settings (by another call to tcsetattr() using the original set of attributes retrieved with tcgetattr()) before your shell exits. This might be done by a handler registered with atexit(), or by other mechanisms. You should endeavour to reset the terminal attributes under all circumstances. You can't do anything about a SIGKILL killing you.

While you're testing this, make a little script for yourself:

echo stty $(stty -g) > sane
chmod u+x sane

That records the current (presumably sane) terminal settings in a form that is designed for stty to read reliably. If (when) you have problems with your shell, you can use Control-JsaneControl-J to run the script and reset your terminal back to the known sane settings. This is also useful if you're developing programs that use the curses library.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

Unless my comment on the other answer is incorrect, I think what you should do is:

 if (!fgets(input, sizeof(input), stdin) == NULL)
 {
    ... do cleanup here ... 
    exit(0);
 }

or something equivalent to that.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227