能否移除is_set变量,通过检查termios结构判断终端是否已配置?
不能直接移除is_set变量,靠检查old_terminal来实现相同逻辑,核心问题出在重复调用configure_terminal时的备份破坏:
原始终端属性不可能全为0:
struct termios的各个字段(比如控制本地模式的c_lflag)在正常终端环境下都有默认值,比如默认开启规范模式(ICANON位)和回显(ECHO位),初始化为{0}的old_terminal和真实的终端原始属性完全不同,没法用“是否为全0”来判断是否已经保存过原始设置。重复调用会破坏原始配置备份:如果去掉
is_set,第二次调用configure_terminal时,会重新执行tcgetattr(STDIN_FILENO, &old_terminal)——此时终端已经是你修改后的非规范模式,这会把当前的异常属性覆盖掉之前保存的原始old_terminal。等到程序退出时,restore_terminal会把终端恢复成这个被覆盖的异常属性,而不是最初的正常状态,导致终端一直处于不回显、非规范的异常模式,用户得手动执行stty sane才能恢复。
如果实在想去掉单独的is_set变量,也可以用一个小技巧:比如利用old_terminal的某个字段做标记,比如初始化时把old_terminal.c_cc[VMIN]设成一个超出正常范围的值(比如255,因为VMIN的有效值通常是0到127),然后判断这个字段是否被tcgetattr修改过。但这种做法依赖终端的默认属性细节,可靠性远不如用独立的is_set布尔标记,反而画蛇添足。
所以最稳妥的方案还是保留is_set变量,确保原始终端属性只被保存一次,避免重复执行配置逻辑导致的备份丢失问题。
内容的提问来源于stack exchange,提问作者cj-2307

