Programs (such as vi) which automatically adjust to screen resizing are responding to the SIGWINCH signal, and using a system call to obtain the system's information about the screen-size. See for example Get width/height of a terminal window in c++?. By the way, though widely implemented, it does not appear to be documented in POSIX signal.h. Without taking SIGWINCH into account, a program could ask the terminal about its screensize. The resize program does this, by sending the terminal control sequences to move the cursor to the lower-right corner (actually, to row/column 999/999, which is good enough), and asking the terminal where the cursor really is. The behavior of ls and vi (and other programs) regarding ANSI control sequences which would be embedded in their output depends upon the design of the program. They likely detect the redirection of their output to a file using the isatty function, and do something different depending on whether the output is to a terminal, or to a file. if (write_eintr(STDOUT_FILENO, "\x1b[999C\x1b[999B", len) == len) { if (get_cursor_pos(wi) == TERM_SUCCESS) { return TERM_SUCCESS; } } ****************** // Source - https://codereview.stackexchange.com/a/292637 // Posted by Madagascar, modified by community. See post 'Timeline' for change history // Retrieved 2026-05-28, License - CC BY-SA 4.0 #undef _POSIX_C_SOURCE #undef _XOPEN_SOURCE #define _POSIX_C_SOURCE 200819L #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include "io.h" #include "sequences.h" #include "term.h" static bool parse_long(const char s[static 1], int base, long val[static 1]) { char *endptr; errno = 0; const long i = strtol(s, &endptr, base); if (endptr == s || *endptr != '\0' || errno != 0) { return false; } *val = i; return true; } static TermCodes get_cursor_pos(WinInfo wi[static 1]) { /* The cursor is positioned at the bottom right of the window. We can learn * how many rows and columns there must be on the screen by querying the * position of the cursor. */ ssize_t len = (ssize_t) strlen("\x1b[6n"); if (write_eintr(STDOUT_FILENO, "\x1b[6n", len) != len) { return TERM_FAILURE; } /* The reply is an escape sequence of the form '\x1b[24;80R', we will * read it into a buffer until read() returns EOF or until we get to * the 'R' character. */ char buf[32]; /* Should be more than enough. */ for (size_t i = 0; i < sizeof buf - 1; ++i) { if (read_eintr(STDIN_FILENO, &buf[i], 1) != 1 || buf[i] == 'R') { break; } } *buf = '\0'; /* Skip the escape character and the left square brace. */ return memcmp(buf, "\x1b[", 2) != 0 || sscanf(&buf[2], "%u;%u", &wi->rows, &wi->cols) != 2 ? TERM_FAILURE : TERM_SUCCESS; } TermCodes term_get_winsize(WinInfo wi[static 1]) { #if defined(TIOCGWINSZ) struct winsize ws; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { wi->rows = ws.ws_row; wi->cols = ws.ws_col; return TERM_SUCCESS; } #elif defined(TIOCGSIZE) struct ttysize ts; if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) == 0) { wi->rows = ts.ts_row; wi->cols = ts.ts_col; return TERM_SUCCESS; } #endif /* defined(TIOCGWINSZ) */ /* ioctl() failed. Fallback to VT100/ANSI escape sequences. */ ssize_t len = (ssize_t) strlen("\x1b[999C\x1b[999B"); if (write_eintr(STDOUT_FILENO, "\x1b[999C\x1b[999B", len) == len) { if (get_cursor_pos(wi) == TERM_SUCCESS) { return TERM_SUCCESS; } } /* write() or get_cursor_pos() failed as well. Now as a last resort, check * LINES and COLUMNS environment variables. * Though note that these variables are not reliable, are not guaranteed * to exist, and might not be up to date if the user changes the terminal * size. If set, the sh, ash, dash, csh shells do not update LINES and * COLUMNS, but bash, fish, zsh, ksh, ksh93u+m, and tcsh handle SIGWINCH to * update these variables. */ const char *const rows = getenv("LINES"); const char *const cols = getenv("COLUMNS"); if (rows != nullptr && cols != nullptr) { long r; long c; bool res = parse_long(rows, 10, &r) && parse_long(cols, 10, &c); if (!res || r > UINT_MAX || c > UINT_MAX) { return TERM_FAILURE; } wi->rows = (unsigned int) r; wi->cols = (unsigned int) c; return TERM_SUCCESS; } return TERM_FAILURE; }