14

OSX 10.6.8, GCC 4.2 86_64

#include <stdio.h>

/* count lines in input */
main()
{
    int c, nl;

    nl = 0;
    while ((c = getchar()) != EOF)
        if (c == '\n')
            ++nl;
    printf("%d\n", nl);
}

Run

./a.out

press ctrl+d to send EOF

0D

It should be just 0. Why does it append D? What does it mean?

Dmitry
  • 2,068
  • 2
  • 21
  • 30
  • 5
    If you are pressing ctrl+D, then it sounds like the D comes from the terminal ... e.g. it doesn't relate to the printf (or the code at all?). What happens if you try it with say, `more` or `less`? What about if you pipe the input in to the program? –  Nov 29 '11 at 05:22
  • 2
    Does that happen every time you send EOF with CTR D, to any program, or just to this test? I think that the `D` besides the `0` comes just from the key combination above. – Nicolás Ozimica Nov 29 '11 at 05:23
  • i got 0 when i run on my linux machine – Jeegar Patel Nov 29 '11 at 05:24
  • @pst no, D doesn't come from that — ctrl+D is a shortcut to send EOF – Dmitry Nov 29 '11 at 21:49
  • @Nicolás no, only with this example. – Dmitry Nov 29 '11 at 21:49
  • @Mr.32 well, it may be related to only OSX/BSD – Dmitry Nov 29 '11 at 21:50
  • 2
    See also: [Simple program adding D to output](http://stackoverflow.com/questions/1450324/simple-program-adding-d-to-output). – Jonathan Leffler Nov 29 '11 at 22:41

1 Answers1

21

I've seen this one - it confused me, too.

The terminal is echoing ^D and then the 0 is output from the program, overwriting the caret.

You can demonstrate this by changing the print format in your program to "\n%d\n".


When asked 'Why?', I went exploring. The answer is in the tty settings. For my terminal, the output from stty -a is:

speed 9600 baud; 65 rows; 120 columns;
lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl
    -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
    -extproc
iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8
    -ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
    -dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
    eol2 = <undef>; erase = ^?; intr = ^C; kill = ^X; lnext = ^V;
    min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
    stop = ^S; susp = ^Z; time = 0; werase = ^W;

Notice the echoctl at the end of the second line - it is for 'echo control characters'.

$ stty -echoctl
$ cat > /dev/null
asdsadasd
$ stty echoctl
$ cat > /dev/null
asasada^D
$

You can't see it, but for each cat command, I typed a Control-D at the end of the line of asd characters, and a second one after hitting return. The prompt erased the second echoed ^D in the second example.

So, if you don't like the control characters being echoed, turn the echoing off:

stty -echoctl

The shell can also get in the way; I experimented with Control-R and my shell (bash) decided to go into

(reverse-i-search)`': aasadasdadadasdadadadadadsad

I'd typed the unoriginal sequence of 'asd' characters and then typed Control-R, and this is where I ended up in the shell. I interrupted; I'm not sure what a reverse-i-search is, but I suspect it is Emacs-ish; it was not what I expected.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Wow, that was spot on! But why does it happen only in this particular case? Or does it happen all the time? And why is ^D echoed? – Dmitry Nov 29 '11 at 21:53
  • 1
    Thank you very much for doing all this research! It's very interesting to see the implementation. – Dmitry Nov 30 '11 at 19:08