From: Maciej Sobczak on
Hi,

Consider:

with Ada.Text_IO;

procedure Hello is
use Ada.Text_IO;

Input_Line : String(1..100);
Last_Index : Integer range 0..100;
begin
loop
Put("What's your name? ");
exit when End_Of_File;
Get_Line(Input_Line, Last_Index);
if Last_Index >= Input_Line'First then
Put("Hi, ");
Put(Input_Line(1..Last_Index));
New_Line;
else
Put("You have funny empty name.");
New_Line;
end if;
end loop;
end Hello;

(please focus on the Get_Line problem here)

It should be obvious what the program does, except that the behaviour in
the case of empty input line is a bit strange.
Below, in the right-hand column I describe what keys were pressed:

$ ./hello
What's your name? Maciek -- M a c i e k ENTER
Hi, Maciek
What's your name? -- ENTER
-- ENTER
You have funny empty name.
What's your name? -- ENTER
You have funny empty name.
What's your name? -- EOF
$

As you see, the first ENTER was somehow "swollowed", creating an empty
line in the console (that's the echo of what user typed), but still
blocking in Get_Line. All subsequent ENTERs seem to be handled
correctly, which means that Get_Line returns with Last_Index <
Input_Line'First.

I was already suggested that it might be a GNAT feature. If yes, it
seems to be persistent, because I see it with two different versions.

Of course, I expect that empty lines are handled uniformly.

Any thoughts?

--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
From: Adam Beneschan on
Maciej Sobczak wrote:
> Hi,
>
> Consider:
>
> with Ada.Text_IO;
>
> procedure Hello is
> use Ada.Text_IO;
>
> Input_Line : String(1..100);
> Last_Index : Integer range 0..100;
> begin
> loop
> Put("What's your name? ");
> exit when End_Of_File;
> Get_Line(Input_Line, Last_Index);
> if Last_Index >= Input_Line'First then
> Put("Hi, ");
> Put(Input_Line(1..Last_Index));
> New_Line;
> else
> Put("You have funny empty name.");
> New_Line;
> end if;
> end loop;
> end Hello;
>
> (please focus on the Get_Line problem here)
>
> It should be obvious what the program does, except that the behaviour in
> the case of empty input line is a bit strange.
> Below, in the right-hand column I describe what keys were pressed:
>
> $ ./hello
> What's your name? Maciek -- M a c i e k ENTER
> Hi, Maciek
> What's your name? -- ENTER
> -- ENTER
> You have funny empty name.
> What's your name? -- ENTER
> You have funny empty name.
> What's your name? -- EOF
> $
>
> As you see, the first ENTER was somehow "swollowed", creating an empty
> line in the console (that's the echo of what user typed), but still
> blocking in Get_Line. All subsequent ENTERs seem to be handled
> correctly, which means that Get_Line returns with Last_Index <
> Input_Line'First.
>
> I was already suggested that it might be a GNAT feature. If yes, it
> seems to be persistent, because I see it with two different versions.
>
> Of course, I expect that empty lines are handled uniformly.
>
> Any thoughts?

It has something to do with the End_Of_File call. I got the same
behavior that you did, but when I commented out the line "exit when
End_Of_File;" [and used another method to determine when to exit the
loop---specifically, I exited when the input was equal to "quit"], I
did not get that behavior. Note that when "standard input" is a
terminal or the equivalent, the End_Of_File call has to perform an
input operation because it has to wait for the user to press control-D
(on Unix-like systems) before End_Of_File knows what to return. It may
be that that isn't being handled quite correctly.

I also found that if I took out "exit when End_Of_File" and added an
exception handler for End_Error, the erroneous behavior didn't occur.

-- Adam

From: Gautier on
Same behaviour on ObjectAda.
But Adam's solution works on both GNAT and ObjectAda.
Note that when you enter again a non-empty string after
an empty one you also get something strange with the End_of_File exit.
______________________________________________________________
Gautier -- http://www.mysunrise.ch/users/gdm/index.htm
Ada programming -- http://www.mysunrise.ch/users/gdm/gsoft.htm

NB: For a direct answer, e-mail address on the Web site!
From: Dmitry A. Kazakov on
On Wed, 06 Dec 2006 15:25:31 +0100, Maciej Sobczak wrote:

> Consider:
>
> with Ada.Text_IO;
>
> procedure Hello is
> use Ada.Text_IO;
>
> Input_Line : String(1..100);
> Last_Index : Integer range 0..100;
> begin
> loop
> Put("What's your name? ");
> exit when End_Of_File;
> Get_Line(Input_Line, Last_Index);
> if Last_Index >= Input_Line'First then
> Put("Hi, ");
> Put(Input_Line(1..Last_Index));
> New_Line;
> else
> Put("You have funny empty name.");
> New_Line;
> end if;
> end loop;
> end Hello;
>
> (please focus on the Get_Line problem here)
>
> It should be obvious what the program does, except that the behaviour in
> the case of empty input line is a bit strange.
> Below, in the right-hand column I describe what keys were pressed:
>
> $ ./hello
> What's your name? Maciek -- M a c i e k ENTER
> Hi, Maciek
> What's your name? -- ENTER
> -- ENTER
> You have funny empty name.
> What's your name? -- ENTER
> You have funny empty name.
> What's your name? -- EOF
> $
>
> As you see, the first ENTER was somehow "swollowed", creating an empty
> line in the console (that's the echo of what user typed), but still
> blocking in Get_Line. All subsequent ENTERs seem to be handled
> correctly, which means that Get_Line returns with Last_Index <
> Input_Line'First.
>
> I was already suggested that it might be a GNAT feature. If yes, it
> seems to be persistent, because I see it with two different versions.
>
> Of course, I expect that empty lines are handled uniformly.
>
> Any thoughts?

I think this is a correct behavior. Here is my explanation of what's going
on:

When End_Of_File meets a CR (Ctrl-M) it cannot know if this CR manifests
the end of the current line or both the line end and the file end. This is
because a file can ends with a CR, and this trailing CR is not counted as
an empty line. So eventually End_Of_File should attempt to read the
following character, i.e. block. This is what happens when you start your
program and promptly hit ENTER. When you hit ENTER again, End_Of_File sees,
aha, that CR wasn't the end and unblocks. This lets Get_Line to read the
*first* CR. The second one remains in the buffer. The next round will
block, but this time not on the ENTER you will hit (this would be a third
CR), but on the *second* one. So you will observe what appears a "correct"
behavior, which in fact is "incorrect", because Get_Line gives you the
*previous* empty string. Who cares, all empty strings are empty. (:-)) Now
when entered strings aren't empty everything works because End_Of_File
reacts on the first character of each line and happily returns.

The rules of thumb I suppose I and many other Ada programmers are using:

1. Never ever use End_Of_File with text files;
2. If you yet use End_Of_File then do it for *each* character of the file;
3. As Adam has suggested, End_Error exception is the right design;
4. End_Error is not only cleaner and correct, but also more efficient.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
From: Adam Beneschan on
Dmitry A. Kazakov wrote:

> I think this is a correct behavior. Here is my explanation of what's going
> on:
>
> When End_Of_File meets a CR (Ctrl-M) it cannot know if this CR manifests
> the end of the current line or both the line end and the file end. This is
> because a file can ends with a CR, and this trailing CR is not counted as
> an empty line. So eventually End_Of_File should attempt to read the
> following character, i.e. block.

No, I don't think this makes sense. If a disk file contained the
characters M a c i e k <CR> <CR> at the end of the file, then this
should be read in as two lines: one line containing the string
"Maciek", and one empty line. You're right that this should not be
followed by yet another empty line, but that isn't relevant. After the
first Get_Line has consumed the characters M a c i e k <CR>, then, when
End_Of_Line looks ahead and sees that the next character is <CR>, it
should be able to return False immediately without needing to look
ahead to see whether <CR> is immediately followed by EOF. And I don't
see why this wouldn't apply to an interactive file as well. (We
shouldn't be saying CR in any case; Unix uses LF (Ctrl-J) as the line
separator, and Windows uses a CR-LF combination. We really should just
be talking about "end-of-line" or EOL without referring to any specific
ASCII character.)

And, in any case, if somehow "a trailing CR is not counted as an empty
line" were true, it would be true regardless of whether the program
used End_Of_File to test for end of file, or simply used Get_Line and
relied on End_Error being raised. So the behavior would have to be the
same in both cases. But that's not what I observed.

-- Adam