B
BartC
Ben Bacarisse said:The logic is bit odd here. If the user hits return the next line is
consumed and discarded.
That's true. But it's another argument against using character-at-a-time
input, as this variation of an off-by-one bug can creep in.
Anyway, I'd write it like this:
int confirm(void)
{
char reply = 0;
do {
int c;
if (reply != 0) puts("Y or N must be the first character.");
printf("Enter Y or N (not case-sensitive): ");
reply = c = toupper(getchar());
while (c != EOF && c != '\n') c = getchar();
} while (reply != 'Y' && reply != 'N');
return reply;
}
That's fine, but reads a little odd because the error message code appears
before the lines that read any input.
But switch to a line-based version:
int confirm(void) {
char str[5];
int c;
while (1) {
printf("Enter Y or N (not case-sensitive): ");
if (fgets(str,sizeof(str),stdin)) {
c=toupper(str[0]);
if ((c=='Y' || c=='N') && str[1]=='\n') break;
}
puts("Error.");
}
return c;
}
And the sequencing is more intuitive.
But notice I've made the line buffer very small, this makes it easy to see
what goes long with long lines (I printed out the str[] contents, as a byte
array after each fgets). Effectively, a long line is broken up and presented
as a series of shorter lines, none of which end with \n, only the last line.
This needs dealing with, but now however it's easy to change the call to
fgets() with a call to your own line-reading function, eg. mygets():
* This keeps the untidy newline and EOF logic outside of your main routine.
* If there are other functions that need short input, they can use the same
mygets() function; they don't need to worry about newline and EOF.
* mygets() can use a variety of ways to deal with long lines; one way is, if
the first line doesn't end with '\n', to keep requesting more input, and to
skip that extra data (and if necessary, to signal that to the caller by some
error character). (And of course it will throw in whatever EOF checks are
needed. The writer of confirm() doesn't need to care anymore; it's the
headache of mygets())
* By skipping the rest of the superfluously long lines, it is now behaving
in a similar way to the code that repeatedly calls getchar() looking for the
end of a line (in fact, mygets() could do the same!).
* Now that confirm() is based on a line buffer, it is easy to modify it to
also allow, for example, 'yes' and 'no' input. (For that purpose, it would
be expeditious to remove the untidy '\n' character from the line buffer, and
just have a normal string.)
Example (returns 1 for success, 0 for error):
int mygets(char* dest,int length,FILE* f) {
int n,c;
if (fgets(dest,length,f)==NULL)
return 0;
n=strlen(dest); /* (will n always be>=1? */
if (dest[n-1]!='\n') {
do
c=fgetc(f);
while (c!=EOF && c!='\n');
return 0;
}
dest[n-1]=0; /* change '\n' to normal string ending */
return 1;
}