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 <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

#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; 
}
