0

I'm practicing using C & Unix by writing up some of the C programs in Vim and compiling them.

The word count program is supposed to end when the character read is EOF (CTRL-D). However, when I run it, the first CTRL-D pressed just makes it print "^D" (minus the quotes) on the terminal. The second time it's pressed, the "^D" goes away and it terminates normally.

How can I change this so that it terminates after only one CTRL-D? I notice that if I've just made a newline character, then pressing CTRL-D once does the trick. But I don't really understand why it works then and not in the general case.

Here's what the program looks like for those of you who don't have the book.

enter image description here

#include <stdio.h>

#define IN   1   /* Inside a word */
#define OUT  0   /* Outside a word */

int main()
{
    int c, nl, nw, nc, state;
    state = OUT;
    nl = nw = nc = 0;
    while ((c = getchar()) != EOF) {
        ++nc;
        if (c == '\n') {
            ++nl;
            --nc;
        }
        if (c == ' ' || c == '\n' || c == '\t')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            nw++;
        }
    }
    printf("%d %d %d\n", nl, nw, nc);
    return 0;
}
Matthew Strawbridge
  • 19,940
  • 10
  • 72
  • 93
Chris Middleton
  • 5,654
  • 5
  • 31
  • 68

2 Answers2

2

Input in Unix-type systems is typically taken from a shell, most often in canonical mode, which means that it is partially interpeted by the shell in order to implement important control functions that govern how the command is used. An example of such a control function is to use control-X to clear the current line. What is happening in your case is that the shell (for example, bash) is interpreting the control-D as a user command to close the stream of input. All prior characters get sent to your program, as well as a control-D -- but the control-D has not been converted to an EOF. When all characters have already been sent on, as is the case following a new line, then bash does not intercept the control-D function but re-interprets control-D as a stand-alone input which it translates to indicate EOF. This EOF then successfully closes your program out.

For further information see prior answer:

ctrl-d didn't stop the while(getchar()!=EOF) loop

Community
  • 1
  • 1
mikeTronix
  • 584
  • 9
  • 16
1

If you type Control-D at the start of the line, once should be enough. If you type Control-D after you've typed anything, then you need to type it twice.

That's because the Control-D tells the terminal driver to send all available characters to any waiting process. When it is at the start of a line, there are no available characters, so a process waiting on a read() gets zero characters returned, which is the meaning of EOF (no characters available for reading). When it is part way through a line, the first Control-D sends the characters to the program, which reads them; the second indicates no more characters and hence EOF once more.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • If I want to make it just a one-command thing, should I use a different key combination? Could I use CTRL-D explicitly in the program in place of EOF? Or some other CTRL-letter combination? I was searching for a long time before on how to say while((c = getchar()) != 'CTRL-D') instead, but I can't get it to work. Do I put the ASCII code in? Should I cast c to an integer first? – Chris Middleton Sep 15 '13 at 05:10