Skip to main content

While translating an old Basic game to C, I found myself needing a function to get one character from the keyboard. This you You can't do this with common standard C library functions like getchar(3)getchar() because the standard input stream is line buffered. iline-buffered (i.e. it will store a whole line of input, including a \nthe terminating \n, in its internal buffer even though getchar()although getchar() only uses theits first character from it. Subsequent calls to getchar()getchar() will useconsume buffer's remaining characters from the buffer until it is exhausted and only then will it resume accepting input the user types from the keyboarduser's new inputs). This causes problems because if you need inputinputting twice, the second time you will get some unexpected value instead of giving theletting user a chance to enter aentering second character.

Now, I know the way around this is to use operating system calls such as ioctl(2)ioctl and read(2)read to set input to an unbuffered state and read a character (or better yet use a library like CursesCurses which abstracts away all this in a cross-platform way.) but I started wondering if it was possible to do this entirely via the standard C library.

My first attempt was to empty out the remaining characters in stdin'sstdin's buffer by adding a call to:

...after calling getchar()getchar() but this didn't do anything.

My next attempt was to try and take stdinstdin out of line buffer mode like this:

I had high hopes this would work but actually it seems calling setvbufsetvbuf on stdinstdin is undefined behavior. It certainly does not work on Linux.

While translating an old Basic game to C, I found myself needing a function to get one character from the keyboard. This you can't do with common standard C library functions like getchar(3) because the standard input stream is line buffered. i.e. it will store a whole line of input including a \n in its internal buffer even though getchar() only uses the first character from it. Subsequent calls to getchar() will use remaining characters from the buffer until it is exhausted and only then will it resume accepting input the user types from the keyboard. This causes problems because if you need input twice, the second time you will get some unexpected value instead of giving the user a chance to enter a character.

Now, I know the way around this is to use operating system calls such as ioctl(2) and read(2) to set input to an unbuffered state and read a character (or better yet use a library like Curses which abstracts away all this in a cross-platform way.) but I started wondering if it was possible to do this entirely via the standard C library.

My first attempt was to empty out the remaining characters in stdin's buffer by adding a call to:

...after calling getchar() but this didn't do anything.

My next attempt was to try and take stdin out of line buffer mode like this:

I had high hopes this would work but actually it seems calling setvbuf on stdin is undefined behavior. It certainly does not work on Linux.

While translating an old Basic game to C, I found myself needing a function to get one character from the keyboard. You can't do this with common standard C library functions like getchar() because the standard input stream is line-buffered (i.e. it will store whole line of input, including the terminating \n, in its internal buffer although getchar() only uses its first character. Subsequent calls to getchar() will consume buffer's remaining characters until exhausted and only then will it resume accepting user's new inputs). This causes problems because if you need inputting twice, the second time you will get some unexpected value instead of letting user entering second character.

Now, I know the way around this is to use operating system calls such as ioctl and read to set input to an unbuffered state and read a character (or better yet use a library like Curses which abstracts away all this in a cross-platform way.) but I started wondering if it was possible to do this entirely via the standard C library.

My first attempt was to empty out the remaining characters in stdin's buffer by adding a call to:

...after calling getchar() but this didn't do anything.

My next attempt was to try and take stdin out of line buffer mode like this:

I had high hopes this would work but actually it seems calling setvbuf on stdin is undefined behavior. It certainly does not work on Linux.

fixed typo
Source Link
Jaldhar
  • 409
  • 3
  • 11

While translating an old Basic game to C, I found myself needing a function to get one character from the keyboard. This you can't do with common standard C library functions like getchar(3) because the standard input stream is line buffered. i.e. it will store a whole line of input including a \n in its internal buffer even though getchar() only uses the first character from it. Subsequent calls to getchar() will use remaining characters from the buffer until it is exhausted and only then will it resume accepting input the user types from the keyboard. This causes problems because if you need input twice, the second time you will get some unexpected value instead of giving the user a chance to enter a character.

Now, I know the way around this is to use operating system calls such as ioctl(2) and read(2) to set input to an unbuffered state and read a character (or better yet use a library like Curses which abstracts away all this in a cross-platform way.) but I standardstarted wondering if it was possible to do this entirely via the standard C library.

My first attempt was to empty out the remaining characters in stdin's buffer by adding a call to:

fflush(stdin);

...after calling getchar() but this didn't do anything.

My next attempt was to try and take stdin out of line buffer mode like this:

setvbuf(stdin, NULL, _IONBF, 0);

I had high hopes this would work but actually it seems calling setvbuf on stdin is undefined behavior. It certainly does not work on Linux.

So finally I came up with this function. It works but I have a nagging feeling it could be improved. What do you think?

int getkey(const char* prompt = "") {
    /* Print the prompt message if there is one */
    if (strcmp(prompt, "") != 0) {
        puts(prompt);
    }

    /* Get a character and examine it.  If it is a newline from a previous
       call to this function eat it otherwise put it back in the buffer. */
    int c = getchar();
    if (c != '\n') {
        ungetc(c, stdin);
    }

    /* This is the character we really want. */
    c = getchar();

    /* Drain the input buffer so any extra characters which were pressed are
       discarded except for newline which is needed to actually send the input 
       to stdin. */
    int next;
    while(!feof(stdin) && next != '\n') {  
        next = getchar();
    }

    return c;
}

While translating an old Basic game to C, I found myself needing a function to get one character from the keyboard. This you can't do with common standard C library functions like getchar(3) because the standard input stream is line buffered. i.e. it will store a whole line of input including a \n in its internal buffer even though getchar() only uses the first character from it. Subsequent calls to getchar() will use remaining characters from the buffer until it is exhausted and only then will it resume accepting input the user types from the keyboard. This causes problems because if you need input twice, the second time you will get some unexpected value instead of giving the user a chance to enter a character.

Now, I know the way around this is to use operating system calls such as ioctl(2) and read(2) to set input to an unbuffered state and read a character (or better yet use a library like Curses which abstracts away all this in a cross-platform way.) but I standard wondering if it was possible to do this entirely via the standard C library.

My first attempt was to empty out the remaining characters in stdin's buffer by adding a call to:

fflush(stdin);

...after calling getchar() but this didn't do anything.

My next attempt was to try and take stdin out of line buffer mode like this:

setvbuf(stdin, NULL, _IONBF, 0);

I had high hopes this would work but actually it seems calling setvbuf on stdin is undefined behavior. It certainly does not work on Linux.

So finally I came up with this function. It works but I have a nagging feeling it could be improved. What do you think?

int getkey(const char* prompt = "") {
    /* Print the prompt message if there is one */
    if (strcmp(prompt, "") != 0) {
        puts(prompt);
    }

    /* Get a character and examine it.  If it is a newline from a previous
       call to this function eat it otherwise put it back in the buffer. */
    int c = getchar();
    if (c != '\n') {
        ungetc(c, stdin);
    }

    /* This is the character we really want. */
    c = getchar();

    /* Drain the input buffer so any extra characters which were pressed are
       discarded except for newline which is needed to actually send the input 
       to stdin. */
    int next;
    while(!feof(stdin) && next != '\n') {  
        next = getchar();
    }

    return c;
}

While translating an old Basic game to C, I found myself needing a function to get one character from the keyboard. This you can't do with common standard C library functions like getchar(3) because the standard input stream is line buffered. i.e. it will store a whole line of input including a \n in its internal buffer even though getchar() only uses the first character from it. Subsequent calls to getchar() will use remaining characters from the buffer until it is exhausted and only then will it resume accepting input the user types from the keyboard. This causes problems because if you need input twice, the second time you will get some unexpected value instead of giving the user a chance to enter a character.

Now, I know the way around this is to use operating system calls such as ioctl(2) and read(2) to set input to an unbuffered state and read a character (or better yet use a library like Curses which abstracts away all this in a cross-platform way.) but I started wondering if it was possible to do this entirely via the standard C library.

My first attempt was to empty out the remaining characters in stdin's buffer by adding a call to:

fflush(stdin);

...after calling getchar() but this didn't do anything.

My next attempt was to try and take stdin out of line buffer mode like this:

setvbuf(stdin, NULL, _IONBF, 0);

I had high hopes this would work but actually it seems calling setvbuf on stdin is undefined behavior. It certainly does not work on Linux.

So finally I came up with this function. It works but I have a nagging feeling it could be improved. What do you think?

int getkey(const char* prompt = "") {
    /* Print the prompt message if there is one */
    if (strcmp(prompt, "") != 0) {
        puts(prompt);
    }

    /* Get a character and examine it.  If it is a newline from a previous
       call to this function eat it otherwise put it back in the buffer. */
    int c = getchar();
    if (c != '\n') {
        ungetc(c, stdin);
    }

    /* This is the character we really want. */
    c = getchar();

    /* Drain the input buffer so any extra characters which were pressed are
       discarded except for newline which is needed to actually send the input 
       to stdin. */
    int next;
    while(!feof(stdin) && next != '\n') {  
        next = getchar();
    }

    return c;
}
Source Link
Jaldhar
  • 409
  • 3
  • 11
Loading