cterminaltermiostermcap

Non canonical terminal mode buffer stdout in c program


I am working a school project (building a very basic shell).

The idea is to be able to do line edition like in bash. For this, I change the terminal mode to non canonical and I stop echo.

I made a very simple code to expose my issue (please note, I do check for functions returns etc... I just made it as short as possible for this post)

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

int main()
{
    int     ret;
    char    buff;
    char    *term_name;
    char    *termcap;
    struct termios  termios_new;
    struct termios  termios_backup;

    /*
    ** Init termcap library
    */
    term_name = getenv("TERM");
    tgetent(NULL, term_name);

    /*
    ** Get the terminal mode to non canonical and shut down echo
    */
    bzero(&termios_new, sizeof(struct termios));
    tcgetattr(STDIN_FILENO, &termios_backup);
    termios_new = termios_backup;

    termios_new.c_lflag &= ~(ICANON);
    termios_new.c_lflag &= ~(ECHO);
    termios_new.c_cc[VMIN] = 1;
    termios_new.c_cc[VTIME] = 0;

    /*
    **  Set the change
    */
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_new);

    /*
    ** Get the termcap for clearing screen on the particular terminal
    */
    termcap = tgetstr("cl", NULL);

    /*
    ** Loop read to get user entries and clear screen for 'c', output char for 'b', break for 'q'
    */
    while((ret = read(STDIN_FILENO, &buff, 1)) > 0)
    {
        if (buff == 'c')
            tputs(termcap, 1, putchar);
        else if (buff == 'b')
            putchar(buff);
        else if (buff == 'q')
            break ;
        buff = 0;
    }

    /*
    ** Put back the terminal mode as found before
    */
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_backup);
    return (0);
}

So basically it's a loop on read to catch user entries. It clears the screen for 'c', output char for 'b', break and restore the original terminal mode for 'q'.

Issue is :

Whenever I type anything, it seems to be buffered because nothing happens until I break the loop with 'q'. At this moment, the output show on screen, if I typed 5 times b, I'll get the 5 b's and if I types 'c', the screen will be cleared. BUT, only after typing 'q'. The behaviour is the same while restoring or not the original terminal mode. (the last line before return)

What I suspect :

After making the code very short and checking all returns, I tend to think there could only be an issue with the way I change the terminal mode ? I tries with the flags TCSAFLUSH and TCSADRAIN for the tcsetattr function with same result.

Thanks ! :)


Solution

  • āœ… SOLVED :

    Ok so for anyone who encounter this situation, it's quite interesting because it made me learn many stuffs (well it's a school project so...).

    Using putchar in the tputs parameters was the issue, because putchar is buffered, juste like printf. I ended up trying two things that led me to the realisation: fflush() after the tputs function call would work so obviously the issue was buffer related. Then tried to output to STDERR_FILENO and it kinda solved it too, and in fact the stderr is the only one _IONBF, so not buffered.

    So I ended up creating a putchar function with only write in it, and that was it.

    Here are more info about the three buffer modes: