0

I wrote th following code, to set the terminal to the non-canonical mode:

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

static struct termios old_terminal = {0};
static bool is_set = false;

static void restore_terminal(void) {
    tcsetattr(STDIN_FILENO, TCSANOW, &old_terminal);
}

static inline void configure_terminal(void) {
    if (!is_set) {
        tcgetattr(STDIN_FILENO, &old_terminal);
        struct termios new_terminal = old_terminal;
        new_terminal.c_lflag &= ~ICANON;  // Disable canonical mode
        new_terminal.c_lflag &= ~ECHO;    // Disable echo
        tcsetattr(STDIN_FILENO, TCSANOW, &new_terminal);
        atexit(restore_terminal);  // Even if the application crashes, the terminal must be restored
        is_set = true;
    }
}

I use the auxiliary variable is_set to garantee that if the user calls the function configure_terminal twice, it doesn't screw the terminal. My question is: Is there a way to remove variable is_set? For instante, checking if the variable old_terminal was set already? Thanks!

cj-2307
  • 259
  • 3
  • 14
  • 1
    You would have to find a field of `struct termios` that cannot possibly be set to zero by `tcgetattr`, and I'm not sure there is one. It's probably better to keep the separate `is_set` variable. Incidentally, `tcgetattr` and `tcsetattr` can fail. – zwol Oct 01 '22 at 22:21
  • Agreed. There is no reason not to have the `is_set` variable. (Though I would name it something like `is_terminal_configured`.) You should _also_ use it in `restore_terminal`! – Dúthomhas Oct 02 '22 at 17:34
  • "*I wrote th following code, to set the terminal to the non-canonical mode*" -- That code is incomplete and insufficient for reliable *reading* in raw mode. Beware that you only set non-canonical mode for *input*. The *output* mode is left undisturbed. Regardless, there are many other termios flags that need to be enabled and disabled for reliable non-canonical mode. For starters see https://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read/26006680#26006680 – sawdust Oct 03 '22 at 23:43
  • Your assumptions are suspect. There is always some sort of termios configuration for each terminal. The dilemma is that the termios config should be reconfigured by each user of the terminal. That typically only needs to be done once (by each user), usually after (exclusively) opening the terminal device. Do it once, and it is done (for that user). There should be no need to check if the termios was configured, and no need to configure it again. Use of a `if (is_set) ...` would make more sense in **restore_terminal()**. – sawdust Oct 03 '22 at 23:52

1 Answers1

0

You could first call tcgetattr to get the current modes of the terminal and check for the status of ICANON and ECHO bits.

If the bits are already disabled than you don't call a function to disable the canonical mode. The test could look like this: if (terminal.c_lflag & (ICANON | ECHO))...

pmatkov
  • 144
  • 1
  • 5
  • This assumes stuff about the initial state of the terminal. – Dúthomhas Oct 02 '22 at 17:31
  • — which is fine if you aren’t done dinking with the terminal, but when terminating your terminal state modifications it is not. – Dúthomhas Oct 02 '22 at 17:37
  • Your scheme is fallible rather than robust. The OP's **restore_terminal()** could in theory zero-out the entire termios configuration. On the next execution of the program, an inspection of the terminal's termios config would satisfy your simplistic criteria. There are many other termios flags that need to be enabled and disabled for *reliable* non-canonical mode. A robust program needs to always perform a full termios config to meet all of its requirements, and not rely on prior conditions. – sawdust Oct 04 '22 at 21:12
  • @sawdust My suggestion was given only in relation to the question of replacing a variable `is_set` with a different kind of test. I didn't presume that the original code is adequate for enabling non-canonical mode (so I used the same flags as in the original code). I also didn't suggest that I gave a complete solution and that no other changes were necessary. – pmatkov Oct 05 '22 at 13:49
  • Here is an article describing how to set non-canonical mode (I think reliable) https://www.gnu.org/software/libc/manual/html_node/Noncanon-Example.html – pmatkov Oct 05 '22 at 14:00
  • "*My suggestion was given only ..*" -- You offered a generic solution to a specific problem that ends up being unsuitable. The OP is a lot like an XY problem: a direct answer is often a wrong/inadequate solution to the actual underlying problem. – sawdust Oct 06 '22 at 06:46