• Uncategorized

About c : How-to-execute-the-key-corresponding-option-without-an-Enter-C-programming-duplicate

Question Detail

I’m trying to write a “more”(just like “more” instruction in Linux) program in C.
Quit when “q” is pressed and show one more page when ” ” is pressed.
But everytime I have to press Enter after the command.
I tried to use setbuf() and setvbuf() but it doesn’t work.Why?How should I do?

FILE *fp_tty; //read from keyboard
fp_tty = fopen("/dev/tty", "r");
setbuf(fp_tty, NULL);
see_more(fp_tty);
......
int see_more(FILE *cmd) {
    int c;
    printf("\033[7m more? \033[m"); 
    while ((c = getc(cmd)) != EOF) {
        if (c == 'q') {
            return 0; //quit
        }
        if (c == ' ') {
            return PAGELEN; //next page
        }
        if (c == '\n') {
            return 1; //1 line
        }
    }
    return 0;
}

Question Answer

Linux shares the tty rules of most Unix systems. When a device is seen as a terminal input meaning here something that receive key presses from a human being, the input is by default line oriented. The rationale is that the user is allowed to press a wrong key, cancel it, and finaly press the correct key. In that case, the tty driver handles the fix and only present the final string to the application.

That works fine for line oriented application, but would be foolish for plain screen ones like emacs or vi (yeah, they are from the 70’s too…), so the tty driver can be set in raw mode to immediately pass any character to the application (other options exist…). That what the curses library use under the hood to be able to immediately react to any key press.

TL/DR: you must put the tty driver in raw mode to be able to immediately process a character. The details are on the tcflush man page.

As pointed out by @rici in comments, termios can help switching the terminal to non-canonical mode.

In your case you can handle those keys using:

#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#define KEY_ESCAPE  0x001b
#define KEY_ENTER   0x000a

static struct termios term, oterm;

static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);

static int getch(void)
{
    int c = 0;

    tcgetattr(0, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &term);
    c = getchar();
    tcsetattr(0, TCSANOW, &oterm);
    return c;
}

static int kbhit(void)
{
    int c = 0;

    tcgetattr(0, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 1;
    tcsetattr(0, TCSANOW, &term);
    c = getchar();
    tcsetattr(0, TCSANOW, &oterm);
    if (c != -1) ungetc(c, stdin);
    return ((c != -1) ? 1 : 0);
}

static int kbesc(void)
{
    int c;

    if (!kbhit()) return KEY_ESCAPE;
    c = getch();
    while (kbhit()) getch();
    return c;
}

static int kbget(void)
{
    int c;

    c = getch();
    return (c == KEY_ESCAPE) ? kbesc() : c;
}

int main(void)
{
    int c;

    while (1) {
        c = kbget();
        if (c == 'q')
        {
            return 0; //quit
        }
        if (c == ' ') {
            return PAGELEN; //next page
        }
        if (c == '\n') {
            return 1; //1 line
        }
    }
    return 0;
}

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.