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

Discussion in 'C Programming' started by Andrew Robert, Nov 9, 2003.

  1. 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;
    }
     
    Andrew Robert, Nov 9, 2003
    #1
    1. Advertisements

  2. 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.
    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?
    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.
    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.
    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.
     
    Joona I Palaste, Nov 9, 2003
    #2
    1. Advertisements

  3. Andrew Robert

    Malcolm Guest

    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.
    stdin is a FILE *, as is stdout and stderr. stdin is usually the keyboard.
    This looks like you don't understand what you are doing. Why an array of
    only one element ?
    fflush is only defined for output files.
    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);
     
    Malcolm, Nov 9, 2003
    #3
  4. 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;
    }
     
    Andrew Robert, Nov 9, 2003
    #4
  5. Since 'fflush(stdin);' has no meaning, you could improve your code by
    replacing it with whitespace.
    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 */
    /* ... */
    }
     
    Martin Ambuhl, Nov 9, 2003
    #5
  6. Oh, it has: "Make the demons fly!" ;-)
    Which would be a great improvement, as it avoids undefined behaviour.

    <snip>
     
    Irrwahn Grausewitz, Nov 9, 2003
    #6
  7. I am trying to develop a simple menu using switch/case statements and I
    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.
    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.
    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 self-destruct button next to the one to turn on the headlights
    should be pretty commonly considered a BAD idea.

    Gordon L. Burditt
     
    Gordon Burditt, Nov 10, 2003
    #7
  8. Andrew Robert

    Alan Connor Guest

    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 :).
     
    Alan Connor, Nov 10, 2003
    #8
  9. Andrew Robert

    Richard Bos Guest

    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
     
    Richard Bos, Nov 10, 2003
    #9
  10. 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.
     
    Nils Petter Vaskinn, Nov 10, 2003
    #10
  11. Andrew Robert

    Ed Morton Guest

    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.
     
    Ed Morton, Nov 10, 2003
    #11
  12. Andrew Robert

    Alan Connor Guest


    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.
     
    Alan Connor, Nov 10, 2003
    #12
  13. Andrew Robert

    Alan Connor Guest

    Okay. Got ya.
     
    Alan Connor, Nov 10, 2003
    #13
  14. 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!!
     
    Andrew Robert, Nov 10, 2003
    #14
  15. [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.]

    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.
     
    Richard Heathfield, Nov 10, 2003
    #15
  16. 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.
     
    Joona I Palaste, Nov 10, 2003
    #16
  17. Andrew Robert

    Alan Connor Guest

    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.
     
    Alan Connor, Nov 10, 2003
    #17
  18. Andrew Robert

    CBFalconer Guest

    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.
     
    CBFalconer, Nov 10, 2003
    #18
  19. Andrew Robert

    Alan Connor Guest

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

    Gracias.
     
    Alan Connor, Nov 10, 2003
    #19
  20. Andrew Robert

    Sidney Cadot Guest

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


    Sidney
     
    Sidney Cadot, Nov 11, 2003
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.