非规范模式下终端输出换行异常,如何用Termcap实现类bash自适应换行
问题根因
你遇到的长文本调整窗口大小后换行异常问题核心有两个:
- 没有缓存完整的用户输入字符串,窗口大小变化时无法基于新的宽度重新排版输出全部内容
- 现有的
SIGWINCH信号处理仅更新了内部变量值,没有触发终端内容的重绘;同时你手动实现的满行硬换行逻辑会被终端识别为固定换行,窗口放大时不会自动合并上下行内容
修复思路
- 新增输入缓冲区存储所有用户输入的可打印字符
- 取消手动硬换行逻辑,依赖终端原生的软换行能力:只要不主动输出换行符,终端会自动对连续的可打印字符做自适应换行,调整窗口大小时也会自动重排
- 完善
SIGWINCH信号处理逻辑:窗口大小变化时清空当前输入行的所有内容,基于新的窗口宽度重新输出缓冲区的全部输入,再把光标定位到正确位置
修复后完整代码
编译时需添加-ltermcap参数:
#include <ctype.h> #include <termcap.h> #include <termios.h> #include <sys/ioctl.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define INPUT_BUF_SIZE 4096 int g_screen_width = 0; int g_cursor_colm = 0; int g_printed = 0; char g_input_buf[INPUT_BUF_SIZE] = {0}; // 输入缓冲区 int ft_putchar(int c) { int len = write(1, &c, 1); return (len); } int ft_isprint(int c) { return (c >= 32 && c < 127); } void update_cursor_position() { g_cursor_colm = g_printed % g_screen_width; } void update_screen_width() { struct winsize w; ioctl(STDIN_FILENO, TIOCGWINSZ, &w); g_screen_width = w.ws_col; } // 清空当前输入的所有显示内容 void clear_input_display() { int lines = (g_printed + g_screen_width - 1) / g_screen_width; // 向上移动对应行数,清空到屏幕末尾 for (int i = 0; i < lines; i++) { tputs(tgetstr("up", NULL), 1, ft_putchar); } tputs(tgoto(tgetstr("ch", NULL), 0, 0), 1, ft_putchar); tputs(tgetstr("cd", NULL), 1, ft_putchar); } // 重绘全部输入内容 void redraw_input() { clear_input_display(); write(1, g_input_buf, g_printed); update_cursor_position(); } void sigwinch_handler(int signo) { if (signo == SIGWINCH) { update_screen_width(); redraw_input(); // 窗口大小变化时触发重绘 } } void move_cursor_to_colum(int col) { char *ch_cap; ch_cap = tgetstr("ch", NULL); tputs(tgoto(ch_cap, 0, col), 1, ft_putchar); } void move_cursor_down_vertically() { char *do_cap; do_cap = tgetstr("do", NULL); tputs(do_cap, 1, ft_putchar); } void move_cursor_to_next_line() { move_cursor_to_colum(0); move_cursor_down_vertically(); } void enable_raw_mode() { struct termios raw; tcgetattr(STDIN_FILENO, &raw); raw.c_lflag &= ~(ECHO | ICANON); tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); } void disable_raw_mode(struct termios old_termios_state) { tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_termios_state); } int main() { struct termios original_termios_state; char *term_type = getenv("TERM"); char c; tgetent(NULL, term_type); tcgetattr(STDIN_FILENO, &original_termios_state); enable_raw_mode(); update_screen_width(); signal(SIGWINCH, sigwinch_handler); while (read(STDIN_FILENO, &c, 1)) { if (ft_isprint(c) && g_printed < INPUT_BUF_SIZE - 1) { g_input_buf[g_printed] = c; // 字符存入缓冲区 ft_putchar(c); g_printed++; g_cursor_colm++; // 取消手动硬换行,依赖终端自动软换行能力 } else if (c == 27) // ESC键退出 { disable_raw_mode(original_termios_state); exit(0); } } disable_raw_mode(original_termios_state); return (0); }
内容的提问来源于stack exchange,提问作者Holy semicolon
相关产品推荐
相关产品推荐

