0

I am writing my own shell and I want to stop the user from exiting the shell with CTRL+D. Below is how I am checking for EOF and I am able to catch the CTRL+D:

However, my loop goes into infinite and does prints please use _exit over and over again.

How could I stop this from doing so?

Thanks

gooddayjay
  • 65
  • 6
  • 2
    Doesn't Ctrl-D close the input stream? There will never be anything to read. – Weather Vane Jul 20 '19 at 18:44
  • if there is a `while` around your snippet, it is the expected behaviour after closing stdin. `read` reads `0` bytes again and again. – mch Jul 20 '19 at 18:46
  • Okay. it sounds like after I hit control and prompt the message, before i do the `continue`, i need to re open stdin – gooddayjay Jul 20 '19 at 18:48
  • To get what you want you'll have to disable Control-D in the terminal. In Unix these are termios. Read man tcgetattr and tcsetattr. I think you'll have to entirely disable ICANON, and then terminal handling gets complicated. – Zan Lynx Jul 20 '19 at 18:50
  • Although maybe you can just unset the VEOF (virtual end of file) character. – Zan Lynx Jul 20 '19 at 18:51
  • You may find this worth of reading: [How to restart stdin after Ctrl+D?](https://stackoverflow.com/questions/51194821/how-to-restart-stdin-after-ctrld) . – WhozCraig Jul 20 '19 at 18:52
  • Note that if you don't exit on end-of-file being received by your shell, then it won't be compatible will every other shell out there. – Some programmer dude Jul 20 '19 at 21:18
  • Also note that your reading of the commands needs some work, unless you have made `STDIN_FILENO` non-blocking. – Some programmer dude Jul 20 '19 at 21:20
  • @WeatherVane No, it doesn't. –  Jul 21 '19 at 10:54
  • Please post a complete example with details. it's not even clear if you're reading from a tty, what OS you're trying that on. In the case where you're on unix and piping the input through another command (eg. `cat | ./your_shell`) that's expected to happen, and you should rethink your approach. –  Jul 21 '19 at 11:15

2 Answers2

0

OK, this seems to work:

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

void disable_veof(void) {
    struct termios t;
    int r;

    r = tcgetattr(STDIN_FILENO, &t);
    if(r)
        exit(EXIT_FAILURE);
    t.c_cc[VEOF] = _POSIX_VDISABLE;
    r = tcsetattr(STDIN_FILENO, TCSANOW, &t);
    if(r)
        exit(EXIT_FAILURE);
}

void echo_lines(void) {
    char buffer[4096];
    const size_t buffer_len = sizeof buffer;
    ssize_t bytes;
    while( 0 != (bytes = read(STDIN_FILENO, buffer, buffer_len)) ) {
        bytes = write(STDOUT_FILENO, buffer, bytes);
        if(bytes <= 0)
            exit(EXIT_FAILURE);
    }
}

int main() {

    disable_veof();
    echo_lines();
    return EXIT_SUCCESS;
}
Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
0

If the input is a terminal in canonical mode, EOF in raw io (as opposed to stdio where it is) is not sticky.

https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap11.html#tag_11_01_09

Special character on input, which is recognized if the ICANON flag is set. When received, all the bytes waiting to be read are immediately passed to the process without waiting for a newline, and the EOF is discarded. Thus, if there are no bytes waiting (that is, the EOF occurred at the beginning of a line), a byte count of zero shall be returned from the read(), representing an end-of-file indication. If ICANON is set, the EOF character shall be discarded when processed.

The next read() should return a positive value unless the user keeps pressing Ctrl+D.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142