Search code examples
cmacosterminalgetcharstty

Unix(Mac OS) terminal does not honor Ctrl+D as EOF


Problem I have: When I was playing getchar() behavior, I found my terminal (macOS Mohave Version 10.14.6 18G87) does not honor Ctrl+D as EOF.

How I got the problem:

The code with getchar() is attached below, basically this code is echo getchar() if the input char is not blank, otherwise, for multiple continously blank char it only output one single blank char. The code itself works, but when running it in terminal, the terminal does not honor Ctrl+D as EOF, thus the code never terminates. I am sure this is due to I mistakenly use system ("/bin/stty raw"); and system ("/bin/stty cooked"); , however, I dont' know how to fix it.

 /* K&R C Exercise 1-9. Write a program to copy its input to its output,
 * replacing each string of one or more blanks by a single blank.*/

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

int main(void){

    int ch;
    int prev = -1;

    /* if you are in a UNIX like environment
     * the ICANON flag is enabled by default, so input is buffered until the next '\n' or EOF
     * https://stackoverflow.com/a/1799024/5450745
     * */
    system ("/bin/stty raw");

    while ((ch=getchar())!= EOF){
       if (ch != ' ' ){
           if (prev == ' '){
               putchar(prev);
               putchar(ch);
           }
           else{
               putchar(ch);
           }
           prev = ch;
       }
       else{

           prev = ch;
           continue;
       }

    }
    /* use system call to set terminal behaviour to more normal behaviour */
    system ("/bin/stty cooked");

    return 0;
}

I checked stty, however, it does configure EOF as Ctrl +D. However, right now, if I press Ctrl + D, it only splits the terminal from one window to two.

enter image description here

What can I do to allow EOF re-enable again?

Thanks!


Solution

  • This code works for me on my Mac running macOS Catalina 10.15. I'd expect it to work essentially the same on just about any Unix-like system you can lay hands on, including older versions of macOS. It spots control-D by comparing ch with '\004' (octal 4 — writable numerous other ways in standard C, including just 4) is detected when typed and terminates the loop.

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        int ch;
        int prev = -1;
    
        /* if you are in a UNIX like environment
         * the ICANON flag is enabled by default, so input is buffered until the next '\n' or EOF
         * https://stackoverflow.com/a/1799024/5450745
         * */
        system("/bin/stty raw");
    
        while ((ch = getchar()) != EOF && ch != '\004')
        {
            if (ch != ' ')
            {
                if (prev == ' ')
                {
                    putchar(prev);
                    putchar(ch);
                }
                else
                {
                    putchar(ch);
                }
                prev = ch;
            }
            else
            {
                prev = ch;
                continue;
            }
        }
        /* use system call to set terminal behaviour to more normal behaviour */
        system("/bin/stty cooked");
    
        return 0;
    }
    

    Of course, the output display is a bit weird — typing return does not move the cursor to the next line, for example. But the test for ch == '\004' does detect when control-D is typed. (The space elimination code does work too.)

    Using stty cbreak instead of stty raw leaves the interrupt enabled, but EOF via control-D is still disabled.