Newbie C programming question - "Press any key to continue"

A

Andrew Robert

Hi Everyone,

I am trying to develop a simple menu using switch/case statements and I
want to be able to have the user "Press any key to continue".

It seems that the main problem is with the initial scanf choice part of
the menu (see code below).

I first looked at scanf and gets but they delete the terminal \n and
that would mess up pressing the carraige return key.

The function fgets seems to do what I need but man pages and on-line
references to fgets seem to indicate it should be used strictily for files

The idea of a "Press any key to continue" should be pretty common but
the logic seems to be elusive.

Does anyone have any ideas about this?

Any help you can provide would be greatly appreciated.


Thanks



/*

program: stu_menu.c

Function: Display student registration menu to screen, retrieve/validate
choice and pass control to sub-program. Code includes standard
elements to comply as c++ code.

Modification History

Date Programmer Modification
11/06/03 AAR Initial creation
Added statement to initialize choice variable
Added scanf/flushf statement to get user choice
Added switch case handles to determine user
choice
Added mode to clear screen and warn of bad
choice

11/06/03 AAR Test compiled on Fedora Linux CORE 9.1 using
gcc 2.96 compiler

11/07/03 AAR Ported to Alpha OpenVMS v7.3-1 and compiled
using Compaq C v6.5-001. Code successfully
tested

11/07/03 AAR Ported to Solaris v5.8 and compiled


*/

#include <stdio.h>
#include <stdlib.h>

int main()
{
int choice;
char string[1];
system("clear");

while (1)
{
system("clear");
printf("\n\t\tSTUDENT REGISTRATION/COURSE GRADE\n");
printf("\t\t DATABASE MANAGEMENT SYSTEM\n\n");
printf("\t\t1. Register a new student\n");
printf("\t\t2. Delete a student\n");
printf("\t\t3. Enter grades for a student\n");
printf("\t\t4. Display student grade report\n");
printf("\t\t5. Display class grade averages\n");
printf("\t\t6. Exit\n\n\n");
printf("\t\t\tChoice: ");
scanf("%d",&choice);
fflush(stdin);




switch (choice)
{
case 1: system("clear");
printf ("\n\n\t\tOption Selected 1.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);
break;

case 2: system("clear");
printf ("\n\n\t\tOption Selected 2.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);
break;

case 3: system("clear");
printf ("\n\n\t\tOption Selected 3.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);
break;

case 4: system("clear");
printf ("\n\n\t\tOption Selected 4.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);
break;

case 5: system("clear");
printf ("\n\n\t\tOption Selected 5.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);
break;

case 6: printf ("Program Over.\n");
exit (0);

default: system("clear");
printf ("\n\n\t\t Invalid choice.\n");
printf ("\n\n\t\t Press any key to continue");
scanf("%s",string);
system("clear");
break;

} /* end switch */
} /* end while */
return 0;
}
 
J

Joona I Palaste

Andrew Robert said:
Hi Everyone,
I am trying to develop a simple menu using switch/case statements and I
want to be able to have the user "Press any key to continue".
It seems that the main problem is with the initial scanf choice part of
the menu (see code below).
I first looked at scanf and gets but they delete the terminal \n and
that would mess up pressing the carraige return key.
The function fgets seems to do what I need but man pages and on-line
references to fgets seem to indicate it should be used strictily for files

Those man pages and on-line references are wrong. fgets, despite its
name, works on streams, not files. You can pass any input stream to it.
Try passing stdin.
The idea of a "Press any key to continue" should be pretty common but
the logic seems to be elusive.
Does anyone have any ideas about this?
Any help you can provide would be greatly appreciated.





(snip)


#include <stdio.h>
#include <stdlib.h>
int main()
{
int choice;
char string[1];
system("clear");

This command "clear" is non-portable. (Pretty much any command passed
to system() is.) Why do you want to clear the screen in the first
place?
while (1)
{
system("clear");
printf("\n\t\tSTUDENT REGISTRATION/COURSE GRADE\n");
printf("\t\t DATABASE MANAGEMENT SYSTEM\n\n");
printf("\t\t1. Register a new student\n");
printf("\t\t2. Delete a student\n");
printf("\t\t3. Enter grades for a student\n");
printf("\t\t4. Display student grade report\n");
printf("\t\t5. Display class grade averages\n");
printf("\t\t6. Exit\n\n\n");
printf("\t\t\tChoice: ");
scanf("%d",&choice);
fflush(stdin);

Undefined behaviour. This might do anything from segfaulting your
program to calling up George W Bush and telling him you're hiding
those elusive WMDs.
switch (choice)
{
case 1: system("clear");
printf ("\n\n\t\tOption Selected 1.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);

It's a bad idea to scanf() into a 1-character array this way. If
the user types more than one character, you overflow the buffer,
causing undefined behaviour. Try the "%c" specifier for scanf()
or switch to fgets(). fgets() does work on stdin.
break;
(snip)

case 6: printf ("Program Over.\n");
exit (0);
default: system("clear");
printf ("\n\n\t\t Invalid choice.\n");
printf ("\n\n\t\t Press any key to continue");
scanf("%s",string);
system("clear");
break;
} /* end switch */
} /* end while */
return 0;
}

It's true that when you type for example '1' in response to your
prompt, you have to press Return, and such your program receives
"1\n". You need some "draining" logic to get rid of the '\n'. This
is very simple, the comp.lang.c FAQ contains some sample code.
Generally, once you get your character, you want to discard any
subsequent characters up to and including the first '\n'. Then process
the rest as normal.
 
M

Malcolm

Andrew Robert said:
I am trying to develop a simple menu using switch/case statements and > I
want to be able to have the user "Press any key to continue".ANSI C is the wrong language for this. It provides extremely limited
facilities for the user to input, a line at time, into stdin. If the user
types 'x' followed by 'return' you can read the 'x' with scanf() or similar.
However there is no way of detecting the actual keypress of the 'x'.

What you need to do is use some extension. There is often a library provided
called conio (#include conio.h) with PCs, or curses with UNIX. This provides
functions for scanning the keyborad and printing nice screens. Obviously the
program is then not portable, but few real programs are.
The function fgets seems to do what I need but man pages and on-line
references to fgets seem to indicate it should be used strictily for files
stdin is a FILE *, as is stdout and stderr. stdin is usually the keyboard.
#include <stdio.h>
#include <stdlib.h>

int main()
{
int choice;
char string[1];
This looks like you don't understand what you are doing. Why an array of
only one element ?
system("clear");

while (1)
{
system("clear");
printf("\n\t\tSTUDENT REGISTRATION/COURSE GRADE\n");
printf("\t\t DATABASE MANAGEMENT SYSTEM\n\n");
printf("\t\t1. Register a new student\n");
printf("\t\t2. Delete a student\n");
printf("\t\t3. Enter grades for a student\n");
printf("\t\t4. Display student grade report\n");
printf("\t\t5. Display class grade averages\n");
printf("\t\t6. Exit\n\n\n");
printf("\t\t\tChoice: ");
scanf("%d",&choice);
fflush(stdin);
fflush is only defined for output files.
switch (choice)
{
case 1: system("clear");
printf ("\n\n\t\tOption Selected 1.\n");
printf ("\n\n\t\tPress any key to continue");
scanf("%s",string);
string is only one element long, and C strings are NUL-terminated. This
means that you will always overflow your buffer when the user enters a
character. You mean char ch; scanf("%c", &ch);
 
A

Andrew Robert

Hi Joona,

I appreciate your response to my questions.

The system clear statements are understood to be non-portable but they
do work on Linux, Solaris v5.8, and VMS 7.3-1 which are the environments
I am concerned about so I left it in strictly for neatness.

In response to your statement about drainage logic, I developed the
function listed below:

I guess my primary concern is in proper usage of fgets from stdin and
translation of the return value to integer so that the switch/case
statement functions correctly.

Should I replace the code statements

scanf("%d",&choice);
fflush(stdin);

with

char c[10];

...
code to display menu
...

fgets(c, 10, stdin) !NULL;
choice=atoi(c);


or should I do something else entirely?




Drainage function:


char *getsafe(char *buffer, int count)
{
char *result = buffer, *np;
printf("count=%d.\n",count);
printf("buffer=%x.\n",buffer);

if ((buffer == NULL) || (count < 1))
{
result = NULL;
}
else if (count == 1)
{
*result = '\0';
}
else if ((result = fgets(buffer, count, stdin)) != NULL)
{
printf ("Got here 1.\n");
}

if (np = strchr(buffer, '\n'))
{
*np = '\0';
printf ("Got here 2.\n");
}

return result;
}
 
M

Martin Ambuhl

Andrew said:
Should I replace the code statements

scanf("%d",&choice);
fflush(stdin);

Since 'fflush(stdin);' has no meaning, you could improve your code by
replacing it with whitespace.
with

char c[10];

...
code to display menu
...

fgets(c, 10, stdin) !NULL;
choice=atoi(c);

char c[NUMBER_MUCH_BIGGER_THAN_10], *endp;
while (fgets(c, sizeof c, stdin))
{
choice = strtol(c, &endp, 10);
/* check *endp, etc. for successful operation */
/* ... */
}
 
I

Irrwahn Grausewitz

Martin Ambuhl said:
Since 'fflush(stdin);' has no meaning,

Oh, it has: "Make the demons fly!" ;-)
you could improve your code by
replacing it with whitespace.

Which would be a great improvement, as it avoids undefined behaviour.

<snip>
 
G

Gordon Burditt

I am trying to develop a simple menu using switch/case statements and I
want to be able to have the user "Press any key to continue".

I hope YOU have to pay for the tech support calls when they call
and ask where the "Any" key is. No, I'm not kidding.

Also be aware that "any key" includes a number of things that
do not generally generate characters:

- Shift (either one)
- Alt (either one)
- Control (either one)
- RESET
- Caps Lock
- Power Off, Standby, or Sleep
- The eject button on the cup holder, er, CD-ROM drive
- A house key, car key, etc. in the user's pocket.
- A PGP encryption key.
- A key on a telephone

and even those who eventually press an appropriate key may spend
half an hour doing "eeney, meeney, miney, moe" to decide which key
to press while on hold with tech support.
It seems that the main problem is with the initial scanf choice part of
the menu (see code below).

The main problem is that "press any key" is a poor user interface,
no matter how it is implemented. And there's NO WAY to do it
in standard ANSI C, unless you define "any key" as the one with
the label "Return" or "Enter" or the corresponding funny-looking arrow.
The function fgets seems to do what I need but man pages and on-line
references to fgets seem to indicate it should be used strictily for files

There is nothing wrong with using fgets on stdin. I consider it
the FIRST function to think about when getting input from what's
likely to be an actual user with a keyboard.

gets() suffers from unfixable buffer overflow problems. scanf()
and fscanf(stdin, ...) suffer from poor behavior when a user enters
data in a wrong format or leaves some out. getchar() and fgetc()
are possible, but if you're using it to parse as you read, you often
cannot back out of a syntax error easily. They are useful if you
really have to accept unlimited-length lines (although the OS is
likely to intrude here with a limit). fread() expects the user to
enter something in a fixed size, which is usually unreasonable to
expect.

The idea of a "Press any key to continue" should be pretty common but
the logic seems to be elusive.

The idea of a self-destruct button next to the one to turn on the headlights
should be pretty commonly considered a BAD idea.

Gordon L. Burditt
 
A

Alan Connor

Your funny, and right I'm sure, yet I run into the "press any key to continue"
option fairly often. All the time in mc. (that would be Norton Commander on
Windows). I've even played around with testing it and so far have not
encountered a key that would not work. Havent't been systematic, though.

What gives?

I thought there was just a function that looked for ANY response from the
keyboard, ANY scancode, and then executed another function.

In sh it could be written like so:

input ()

{

read x

case "$x" in

* ) do foo ;;

esac

}


In fact, I have used that very function in scripts for just this purpose.

Hard to imagine that C doesn't have the same functionality (sorry :).
 
R

Richard Bos

Alan Connor said:
Your funny, and right I'm sure, yet I run into the "press any key to continue"
option fairly often. All the time in mc. (that would be Norton Commander on
Windows). I've even played around with testing it and so far have not
encountered a key that would not work. Havent't been systematic, though.

What gives?

You are not an averagely stupid user, that gives. Believe you me, there
_are_ people who look for a key labeled "any".
I thought there was just a function that looked for ANY response from the
keyboard, ANY scancode, and then executed another function.

Not in ISO C. On many systems (sometimes including an otherwise standard
desktop computer being spoken to through a modem line), looking for any
scancode isn't possible, or indeed even a plausible thing to ask for
(Modem: "Scan code? What's that, a scan code? I can give you a line
typed at the other end of the phone line, is that OK?").

Richard
 
N

Nils Petter Vaskinn

Your funny, and right I'm sure, yet I run into the "press any key to
continue" option fairly often. All the time in mc. (that would be Norton
Commander on Windows). I've even played around with testing it and so
far have not encountered a key that would not work. Havent't been
systematic, though.

What gives?

I thought there was just a function that looked for ANY response from
the keyboard, ANY scancode, and then executed another function.

A program (C or script) doesn't see the scancodes (usually), they see
characters after the OS has translated the scancodes. And some key doesn't
generate characters when pressed alone.

The problem will be (in programs as well as in your script) when you
encounter the mythical "completely clueless user" that thinks the shift
key is suitable when they say to press ANY key. (or perhaps that fn key
that's so common on laptops)

"Press enter to continue" is less likely to fail due to clueless users.
 
E

Ed Morton

Alan said:
On 10 Nov 2003 03:20:23 GMT, Gordon Burditt <[email protected]> wrote:
I thought there was just a function that looked for ANY response from the
keyboard, ANY scancode, and then executed another function.

In sh it could be written like so:

input ()

{

read x

case "$x" in

* ) do foo ;;

esac

}


In fact, I have used that very function in scripts for just this purpose.

Hard to imagine that C doesn't have the same functionality (sorry :).

The above shell script has the same limitations as a C program wrt user
input plus more. In fact, while a C program can be written to accept a
single appropriate key-stroke, the above script will ONLY work if the
user enters a terminating newline. Try it with any of the keys listed in
Gordon's posting.

Ed.
 
A

Alan Connor

The above shell script has the same limitations as a C program wrt user
input plus more. In fact, while a C program can be written to accept a
single appropriate key-stroke, the above script will ONLY work if the
user enters a terminating newline. Try it with any of the keys listed in
Gordon's posting.

Ed.


Never-the-less, many programs say "press any key" (mc for one) and it
works. The newline is apparently added after the keystroke by the
application itself.
 
A

Alan Connor

You are not an averagely stupid user, that gives. Believe you me, there
_are_ people who look for a key labeled "any".


Not in ISO C. On many systems (sometimes including an otherwise standard
desktop computer being spoken to through a modem line), looking for any
scancode isn't possible, or indeed even a plausible thing to ask for
(Modem: "Scan code? What's that, a scan code? I can give you a line
typed at the other end of the phone line, is that OK?").

Richard

Okay. Got ya.
 
A

Andrew Robert

Hi Gordon,


This is a simple class project and not production level code.

My attempts to port the code from Linux, to VMS, and then to Solaris are
more an exercise than anything else.

As a veteran Unix/VMS System Admin (15+ years), what a user does or
doesn't do is seldom surprising.

Funny as #$!#$, but not surprising.

Your point about needing to map the whole keyboard, and the car keys :),
is well taken though.

I've taken valuable feedback from several posters here and modified it
so it strickly behaves when <return> is pressed.

On a funny side note, Compaq came to our rescue and solved the ANY key
mystery.

See http://web14.compaq.com/falco/detail.asp?FAQnum=FAQ2859

I'm glad they finally cleared this up.

I was so lost!!
 
R

Richard Heathfield

[In common with half of Usenet, I am in Alan Connor's killfile. Therefore,
he won't see this correction of his misunderstanding of the "press any key"
question. Nevertheless, some people may be misled by his error, so this
reply is for their benefit, not his.]

Alan said:
Never-the-less, many programs say "press any key" (mc for one) and it
works. The newline is apparently added after the keystroke by the
application itself.

No, such programs use extensions to the C language to provide unbuffered
input. These extensions are not universally available, alas. In MS-DOS
programs, the getch() function is commonly used for unbuffered input. In
Windows... well, let's not go there. Ask in the appropriate newsgroup -
comp.os.ms-windows.programmer.win32 - if you really need to know. In *nix
operating systems, the [n]curses library is typically (but not universally)
used for this kind of functionality.
 
J

Joona I Palaste

Richard Heathfield said:
[In common with half of Usenet, I am in Alan Connor's killfile. Therefore,
he won't see this correction of his misunderstanding of the "press any key"
question. Nevertheless, some people may be misled by his error, so this
reply is for their benefit, not his.]

Alan Connor is in my killfile but I'm not sure if I'm in his. Therefore
I only saw Alan's misunderstanding because Richard quoted it.

This is quite a misunderstanding in concepts. The newline character is
actually pretty much irrelevant here.
If the C standard input stream (stdin) were directly connected to the
keyboard, "press (almost) any key" would actually work in completely
ISO standard C without having to press Return or Enter to enter a
newline character. (By "(almost) any key" I mean any key generating a
character value code, such as any letter or any digit, but not Shift,
Ctrl or Caps Lock, for example.)
This would then have the rather exciting side-effect of losing all
chance of preformatting your data from stdin. Suppose you are typing
the following line:

"Now is the time for all good men"

But instead manage to type:

"Now is the time fro"

When you realise you have typed 'r' before 'o', you press Backspace
to delete the 'r' and continue from where you left. Your C program
should now receive:

"Now is the time for all good men"

shouldn't it? No. It actually receives:

"Now is the time fro^H^Hor all good men"

where ^H is the Backspace character. This is because C received what
you typed, and it doesn't want to go guessing at what you really
meant.

Because of this, most implementations connect stdin to a line buffer
instead, which in turn is connected to the keyboard. With the help of
this line buffer, pressing Backspace two times causes the "ro" to
vanish from the line buffer's memory to the bit bucket without C ever
getting a chance to see it, let alone worry its little head about it.

Now, when Alan speaks of OS-specific functions that can do "press any
key" without need for a newline, they don't read from the standard
input stream at all! Instead they read directly from the keyboard,
bypassing any streams in between. If the keyboard handler in the OS
doesn't have its own buffer algorithm, calling a standard input
function just after having called an OS-specific one might cause the
same character(s) to be returned twice!

The keyboard, and the keyboard handler, don't have any concept of a
line. They just return key scancodes as they are pressed. They don't
assign any special meaning to the Return or Enter keys. That's the
OS's job. If the OS wished, it might even use the 'Z' key in the
bottom left corner to terminate a line and flush the buffer.
 
A

Alan Connor

Richard Heathfield said:
[In common with half of Usenet, I am in Alan Connor's killfile.

Yes Richard, you are in my killfile.

Precisely because of you are so comfortable with posting statements like
the above.

Either you know that this is false and are lying, or believe it is true
and are an idiot.

30 more days added to your term in my killfile.
 
C

CBFalconer

Alan said:
Never-the-less, many programs say "press any key" (mc for one)
and it works. The newline is apparently added after the keystroke
by the application itself.

Not any program written purely in portable ISO C without special
extensions and with the ability to line edit input lines. Which
is the type of coding discussed here.

If you want to give up the interactive line editing abilities,
usually implemented with bs, rub, ^X, ^U, etc. control characters,
you can probably have that ability (subject to the limitations
against the shift keys, etc. detailed elsethread). Such
capabilities are OS and implementation dependent, not C language
dependent.
 
A

Alan Connor

Not any program written purely in portable ISO C without special
extensions and with the ability to line edit input lines. Which
is the type of coding discussed here.

If you want to give up the interactive line editing abilities,
usually implemented with bs, rub, ^X, ^U, etc. control characters,
you can probably have that ability (subject to the limitations
against the shift keys, etc. detailed elsethread). Such
capabilities are OS and implementation dependent, not C language
dependent.

Now THAT is the answer I have been waiting for, and suspecting was
the case.

Gracias.
 
S

Sidney Cadot

Alan said:
Richard Heathfield said:
[In common with half of Usenet, I am in Alan Connor's killfile.


Yes Richard, you are in my killfile.

Precisely because of you are so comfortable with posting statements like
the above.

Either you know that this is false and are lying, or believe it is true
and are an idiot.

30 more days added to your term in my killfile.

"I really don't like watching people masturbate in public" ...


Sidney
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top