0

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!

helloworld
  • 613
  • 8
  • 24
  • Voted to migrate to https://superuser.com/ As this is not a programming question but terminal configuration. – Eugene Sh. Oct 24 '19 at 14:48
  • The reason I put it here is because I think this problem is raised due to I wrongly use `system ("/bin/stty raw")` and `system ("/bin/stty cooked");`, I think I sort of mixed the C library, and UNIX terminal configurations in one question. – helloworld Oct 24 '19 at 15:06
  • 1
    Raw mode input doesn't honour any special characters — no EOF, no erase, no interrupt, no quit, no kill, etc. Maybe `stty cbreak` — I've not experimented, and I'm not sure. Note that you can't erase characters either at the moment. (And one key `stty` flag is `icanon`/`-icanon`.) – Jonathan Leffler Oct 24 '19 at 15:06
  • 2
    Your problem has nothing to do with MacOS, and everything to do with, yes, your `stty` call. If you want to do the exercise as the authors intended, get rid of the `stty` calls. If you want to see characters immediately as they're typed -- as I remember *I* did, the first time I did a similar exercise in K&R -- then yes, you're going to have to tinker with the tty modes, but you're also going to have to check for control-D yourself. Easiest way would be to change the loop to `while ((ch=getchar()) != EOF && ch != 4)`. – Steve Summit Oct 24 '19 at 15:14
  • 2
    Are you actually pressing `ctrl+d` and not `command+d`? Because `command+d` is the default shortcut to split the window in the MacOS Terminal. – pqnet Oct 24 '19 at 15:21
  • @SteveSummit Hi! Thanks for reply! May I know what does it mean for `ch!=4` ? what does `4` represent for , an integer 4 ? – helloworld Oct 24 '19 at 15:59
  • @JacquelineP. That's the value you'll get if you type control-D. (`A` is 65, `D` is 68, `a` is 97, `d` is 100, control-A is 1, control-D is 4, ...) – Steve Summit Oct 24 '19 at 16:03
  • I tried Ctrl +d and Command⌘ + d , neither works as EOF. And pressing " Command⌘ + d" splits from one window to two... – helloworld Oct 24 '19 at 16:14
  • Sorry, I really thought Control-D would map to 4. You may just have to get rid of the `stty` calls. – Steve Summit Oct 24 '19 at 17:46
  • 1
    You could use `while ((ch=getchar())!= EOF && ch != '\004'){` though since EOF will not be detected if the `stty` command works, you could simplify that to `while ((ch=getchar()) != `\004'){`. The value `'\004'` (equivalent to `'\4'` and `'\x04'` and `4`) is also the notation for control-D. – Jonathan Leffler Oct 24 '19 at 17:46

1 Answers1

1

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.

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