D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 10467 - readln problem with CTRL-Z
Summary: readln problem with CTRL-Z
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: All Windows
: P3 normal
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-06-24 14:12 UTC by bearophile_hugs
Modified: 2024-12-01 16:18 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description bearophile_hugs 2013-06-24 14:12:19 UTC
import std.stdio, std.string;
void main() {
    while (true) {
        write("Input: ");
        string guess = readln.strip;
        if (guess == "1")
            break;
    }
}



On Windows if I run that little program and I insert "1" the program terminates normally:

>dmd -run temp.d
Input: 1


If at the input promtp I give a Ctrl-C it breaks the program:

>dmd -run temp.d
Input: ^C



If at the input prompt I give a Ctrl-Z the while loop seems to go in an infinite loop:

>dmd -run temp.d
Input: ^Z
Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: ...
Comment 1 monarchdodra 2013-06-24 14:57:58 UTC
(In reply to comment #0)
> If at the input prompt I give a Ctrl-Z the while loop seems to go in an
> infinite loop:
> 
> >dmd -run temp.d
> Input: ^Z
> Input: Input: Input: Input: Input: Input: Input: Input: Input: Input: Input:
> Input: Input: Input: Input: Input: ...

Just for the record, what *would* the correct behavior be? The stream is at eof, so stdin has no need to wait for user input, and if you ask for a line, you get "" ...

A "correct" program would have either:
while (stdin.isOpen && !stdin.eof && !stdin.error) //Meh...
Or...
string s;
while ((s = stdin.readln(line)).length) //very good!
Or...
char[] line;
while (stdin.readln(line)) //very good!

For the record, I don't think readln can throw an exception, since the "common semantic" is "read lines until the read fails, then check it failed because you have reached eof"...

So... yeah... Doesn't seem like a bug to me, just a program that wasn't designed to handle stdin's eof...
Comment 2 bearophile_hugs 2013-06-24 15:20:18 UTC
(In reply to comment #1)

> Just for the record, what *would* the correct behavior be?

An equivalent Python2.6 program:


while True:
    guess = raw_input("Input: ").strip()
    if guess == "1":
        break




This is how Python behaves here (inputs are 1, CTRL-C and CTRL-Z):

>temp.py
Input: 1

>temp.py
Input: Traceback (most recent call last):
  File "\temp.py", line 2, in <module>
    guess = raw_input("Input: ").strip()
KeyboardInterrupt

>temp.py
Input: ^Z
Traceback (most recent call last):
  File "\temp.py", line 2, in <module>
    guess = raw_input("Input: ").strip()
EOFError
Comment 3 bearophile_hugs 2013-06-24 15:38:23 UTC
Elsewhere monarch_dodra adds some more comments on this topic and a link:

> I don't think this is a bug (I replied on the bug report): 
> terminating the stream doesn't mean terminating the program, 
> and if the program doesn't know how to handle a 
> closed/eof/error'd stdin, it will just loop...
>
> This FAQ link explains it pretty well for C++, which is pretty 
> much the same thing as in D:
> http://www.parashift.com/c++-faq/stream-input-failure.html
> (the next few points are relevant too).
>
> We could argue the design isn't optimal, yes, but it's not 
> bugged.
Comment 4 monarchdodra 2013-06-25 00:12:41 UTC
(In reply to comment #2)
> (In reply to comment #1)
> 
> > Just for the record, what *would* the correct behavior be?
> 
> An equivalent Python2.6 program:
> 
> 
> while True:
>     guess = raw_input("Input: ").strip()
>     if guess == "1":
>         break
> 
> 
> 
> 
> This is how Python behaves here (inputs are 1, CTRL-C and CTRL-Z):
> 
> >temp.py
> Input: 1
> 
> >temp.py
> Input: Traceback (most recent call last):
>   File "\temp.py", line 2, in <module>
>     guess = raw_input("Input: ").strip()
> KeyboardInterrupt
> 
> >temp.py
> Input: ^Z
> Traceback (most recent call last):
>   File "\temp.py", line 2, in <module>
>     guess = raw_input("Input: ").strip()
> EOFError

I'm noticing D's readln doesn't quite have the same semantics as C++'s:
In C++, getline will keep reading until it *can* read at least a single non empty line, trying again if it reads an empty line. This means that you can't know if you'll succeed unless you try. getline explicitly returns the state of the stream, so you can test the return status of getline.

D's getline, on the other hand, returns empty lines. This means there is no real way to check for success, unless an exception was thrown, or the stream is in an error state. This means the interface is kind of crummy, since it claims: "returns: 0 for end of file, otherwise number of characters read": which sucks, since it can succeed if 0 characters were read...

Also, D's readln says "StdioException on I/O error".

So after a second though, I think you are right, and Python's "throw for reads of EOF" is not only safer and better, but also works, and is the *documented behavior*... too bad we didn't have this for C++ :'(
Comment 5 berni44 2019-11-12 18:38:14 UTC
On linux, the program is just suspended when entering ^Z, as expected, and can be continued by entering "fg". Looks to me a windows only bug.
Comment 6 dlangBugzillaToGithub 2024-12-01 16:18:03 UTC
THIS ISSUE HAS BEEN MOVED TO GITHUB

https://github.com/dlang/phobos/issues/9986

DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB