Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can i make getchar() function hold backspace

Tags:

c

#include<stdio.h>
int main(void)
{
    int c;
    while((c=getchar())!=EOF)
    {
        if(c==8)       // 8 is ASCII value of backspace
          printf("\\b");
    }
}

Now I want to enter backspace and want getchar() function to return ASCII of backspace to c(int variable)

Note- I am not asking about getch function i know that getch command is able to read backspace I only want to know whether getchar is able to read backspace or not If yes,How?

How to do it please explain me I am new to C programming

like image 747
Jay Patel Avatar asked Jan 02 '23 10:01

Jay Patel


2 Answers

If the stream stdin reads from a file, getchar() will handle backspace characters ('\b' or 8 in ASCII) like any other regular character and return it to the caller.

The reason it does not do that when reading from the console (aka terminal or tty) is related to the configuration of the console itself. The console is in cooked mode by default, so as to handle echo, backspace and line buffering, but also signals such as SIGINT sent for Ctrl-C and many more subtile settings.

The C Standard does not provide any way to change the terminal modes, but POSIX systems have the stty command and the termios system calls available to C programs to do this.

Once you configure the console in raw mode, also set stdin to unbuffered mode with setvbuf(stdin, NULL, _IONBF, 0) so getchar() reads each byte from the console as it is typed.

Configuring the console is a complex issue, you may want to read this first: http://www.linusakesson.net/programming/tty/

like image 53
chqrlie Avatar answered Jan 04 '23 22:01

chqrlie


If your system supports termios as standardized in POSIX.1-2001, then you can manipulate the standard input terminal to not buffer your input. Consider the following example:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* SIGINT handler */

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    if (!done)
        done = signum;
}

static int install_done(const int signum)
{
    struct sigaction  act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;
    return 0;
}

/* Reverting terminal back to original settings */

static struct termios  terminal_config;

static void revert_terminal(void)
{
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal_config);
}


int main(void)
{
    int  c;

    /* Set up INT (Ctrl+C), TERM, and HUP signal handlers. */
    if (install_done(SIGINT) ||
        install_done(SIGTERM) ||
        install_done(SIGHUP)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Make terminal input noncanonical; not line buffered. Also disable echo. */
    if (isatty(STDIN_FILENO)) {
        struct termios  config;
        if (tcgetattr(STDIN_FILENO, &terminal_config) == 0 &&
            tcgetattr(STDIN_FILENO, &config) == 0) {
            config.c_lflag &= ~(ICANON | ECHO);
            config.c_cc[VMIN] = 1; /* Blocking input */
            config.c_cc[VTIME] = 0;
            tcsetattr(STDIN_FILENO, TCSANOW, &config);
            atexit(revert_terminal);
        }
    }

    /* Set standard input unbuffered. */
    setvbuf(stdin, NULL, _IONBF, 0);

    printf("Press Ctrl+C to exit.\n");
    fflush(stdout);

    while (!done) {
        c = fgetc(stdin);
        if (c == EOF)
            printf("Read EOF%s\n", ferror(stdin) ? " as an error occurred" : "");
        else
            printf("Read %d = 0x%02x\n", c, (unsigned int)c);
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

The #define line tells your C library headers to expose POSIX.1 features for GNU-based systems.

The done flag is set whenever an INT (Ctrl+C), TERM, or HUP signal is received. (HUP signal is sent if you disconnect from the terminal, for example by closing the terminal window.)

The terminal_config structure will contain the original terminal settings, used by revert_terminal() registered as an at-exit function, to revert the terminal settings back to the original ones read at program startup.

The function isatty(STDIN_FILENO) returns 1 if standard input is a terminal. If so, we obtain the current terminal settings, and modify them for non-canonical mode, and ask that each read blocks until at least one character is read. (The I/O functions tend to get a bit confused if you set .c_cc[VMIN]=0 and .c_cc[VTIME]=0, so that if no input is pending, fgetc() returns 0. Typically it looks like an EOF to stdio.h I/O functions.)

Next, we tell the C library to not internally buffer standard input, using setvbuf(). Normally, the C library uses an input buffer for standard input, for efficiency. However, for us, it would mean the C library would buffer characters typed, and our program might not see them immediately when typed.

Similarly, standard output is also buffered for efficiency. The C library should flush all complete lines to the actual standard output, but we can use the fflush(stdout) call to ensure everything we've written to stdout is flushed to the actual standard output at that point.

In main(), we then have a simple loop, that reads keypresses, and prints them in decimal and hexadecimal.

Note that when a signal is delivered, for example the INT signal because you typed Ctrl+C, the delivery of the signal to our handle_done() signal handler interrupts the fgetc() call if one is pending. This is why you see Read EOF when you press Ctrl+C; if you check ferror(stdin) afterwards, you'll see it returns nonzero (which indicates an error occurred). The "error" in this case is EINTR, "interrupted by a signal".

Also note that when you press some certain keys, like cursor or function keys, you'll see multiple characters generated, usually beginning with 27 and 91 (decimal; 0x1B 0x5B in hexadecimal; "\033[" if expressed as a C string literal). These are usually, but not always, ANSI escape sequences. In general, they are terminal-specific codes that one can obtain via tigetstr(), tigetnum(), and tigetflag() using the terminfo database.


A much more portable way to do this, is to use a Curses library; either ncurses on most systems, or PDCurses on Windows machines. Not only do they provide a much easier interface, but it does it in a terminal-specific way, for maximum compatibility across systems.

C programs using the Curses functions can be compiled against any Curses library, so the same C source file can be compiled and run on Linux, Mac, and Windows machines. However, ncurses does contain quite a few extensions, which may not be provided by other Curses libraries like PDCurses.

like image 42
Nominal Animal Avatar answered Jan 04 '23 23:01

Nominal Animal