From: Kirill A. Korinsky Subject: Re: bin/ksh: fix emacs/vi mode using wrong terminal width To: tech@openbsd.org Date: Sat, 23 May 2026 01:14:20 +0200 On Sun, 25 Jan 2026 17:23:12 +0100, Kirill A. Korinsky wrote: > > On Thu, 22 Jan 2026 16:59:21 +0100, > Kirill A. Korinsky wrote: > > > > tech@, > > > > this is clear re-submission of my patch to ksh to fix terminal width > > detecion. > > > > xterm and other terminal emulators resize the pty after starting the > > shell. x_init() queries the terminal size at startup before the resize > > occurs, so x_cols stays at the default 80 columns. > > > > SIGWINCH arrives, but check_sigwinch() isn't called until after command > > entry. This makes the line editor think the screen is 80 columns, which > > makes it show '<' and truncate input before the actual terminal edge on > > large windows, and causes weird behavior on small ones. > > > > Call check_sigwinch() when entering edit mode and on EINTR in x_getc(). > > The callback lets emacs/vi mode recalculate display width and redraw. > > > > Here reworked version which addressed an edge case when prompt is duplicated > when resize had happened when curses application is used which was reported > by Walter Alejandro Iglesias, and suggestion from chris@ to honor curses > when it possible. > > Also, I had introduced a regression tests with one note: the vi mode resize > path is multi stage and can interleave with pending input, so the byte > stream varies even when the final screen is correct; to make the test > deterministic, edit.c waits for two consecutive quiet poll timeouts to > ensure the child is idle before sending TIOCSWINSZ and SIGWINCH. > Anyone? diff --git bin/ksh/edit.c bin/ksh/edit.c index a510b5f9915..09254c1e8c4 100644 --- bin/ksh/edit.c +++ bin/ksh/edit.c @@ -26,7 +26,8 @@ X_chars edchars; static void x_sigwinch(int); volatile sig_atomic_t got_sigwinch; -static void check_sigwinch(void); +void check_sigwinch(void); +void (*x_resize_cb)(void); /* callback for resize during edit */ static int x_file_glob(int, const char *, int, char ***, int); static char *x_tilde_expand(const char *); @@ -64,7 +65,7 @@ x_sigwinch(int sig) got_sigwinch = 1; } -static void +void check_sigwinch(void) { if (got_sigwinch) { @@ -81,11 +82,16 @@ check_sigwinch(void) * see the change cause the environ doesn't change. */ if (ws.ws_col) { + int old_cols = x_cols; + x_cols = ws.ws_col < MIN_COLS ? MIN_COLS : ws.ws_col; if ((vp = typeset("COLUMNS", 0, 0, 0, 0))) setint(vp, (int64_t) ws.ws_col); + + if (x_cols != old_cols && x_resize_cb) + (*x_resize_cb)(); } if (ws.ws_row && (vp = typeset("LINES", 0, 0, 0, 0))) setint(vp, (int64_t) ws.ws_row); @@ -126,12 +132,14 @@ x_getc(void) char c; int n; - while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR) + while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR) { + check_sigwinch(); if (trap) { x_mode(false); runtraps(0); x_mode(true); } + } if (n != 1) return -1; return (int) (unsigned char) c; diff --git bin/ksh/edit.h bin/ksh/edit.h index e1d200b9c93..e2978f3c7de 100644 --- bin/ksh/edit.h +++ bin/ksh/edit.h @@ -39,6 +39,8 @@ extern X_chars edchars; #define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE) /* edit.c */ +void check_sigwinch(void); +extern void (*x_resize_cb)(void); int x_getc(void); void x_flush(void); int x_putc(int); diff --git bin/ksh/emacs.c bin/ksh/emacs.c index 399ef59ce83..e3664df112f 100644 --- bin/ksh/emacs.c +++ bin/ksh/emacs.c @@ -105,6 +105,7 @@ static int xlp_valid; /* end from 4.9 edit.h } */ static int x_tty; /* are we on a tty? */ static int x_bind_quiet; /* be quiet when binding keys */ +static void x_emacs_resize(void); static int (*x_last_command)(int); static char **x_histp; /* history position */ @@ -286,6 +287,9 @@ x_emacs(char *buf, size_t len) xmp = NULL; x_histp = histptr + 1; + x_resize_cb = NULL; + check_sigwinch(); + x_resize_cb = x_emacs_resize; xx_cols = x_cols; x_col = promptlen(prompt, &p); prompt_skip = p - prompt; @@ -316,8 +320,11 @@ x_emacs(char *buf, size_t len) x_last_command = NULL; while (1) { x_flush(); - if ((at = x_e_getu8(line, at)) < 0) + if ((at = x_e_getu8(line, at)) < 0) { + x_resize_cb = NULL; return 0; + } + ntries++; if (x_arg == -1) { @@ -378,6 +385,7 @@ x_emacs(char *buf, size_t len) x_last_command = NULL; break; case KEOL: + x_resize_cb = NULL; ret = xep - xbuf; return (ret); break; @@ -2166,4 +2174,51 @@ x_lastcp(void) return (xlp); } +static void +x_emacs_resize(void) +{ + int cr_done = 0, cleared = 0; + +#ifndef SMALL + if (cur_term != NULL) { + if (carriage_return != NULL && + tputs(carriage_return, 1, x_putc) != ERR) + cr_done = 1; + if (clr_eos != NULL && + tputs(clr_eos, 1, x_putc) != ERR) + cleared = 1; + } +#endif + if (!cr_done) + x_putc('\r'); + if (!cleared) { + x_putc('\033'); + x_putc('['); + x_putc('J'); + } + + xx_cols = x_cols; + x_col = promptlen(prompt, NULL); + if (x_col > xx_cols) + x_col = x_col - (x_col / xx_cols) * xx_cols; + x_displen = xx_cols - 2 - x_col; + if (x_displen < 1) { + x_col = 0; + x_displen = xx_cols - 2; + prompt_redraw = 0; + } else { + xbp = xbuf; + xlp_valid = false; + if (xcp <= x_lastcp()) { + x_redraw(0); + x_flush(); + return; + } + } + + x_col = 0; + x_displen = xx_cols - 2; + x_adjust(); +} + #endif /* EMACS */ diff --git bin/ksh/vi.c bin/ksh/vi.c index e386fa1e65f..9ac55404ade 100644 --- bin/ksh/vi.c +++ bin/ksh/vi.c @@ -75,6 +75,8 @@ static void vi_error(void); static void vi_macro_reset(void); static int x_vi_putbuf(const char *, size_t); static int isu8cont(unsigned char); +static void x_vi_resize(void); +static void calc_winsize(void); #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */ #define M_ 0x2 /* movement command (h, l, etc.) */ @@ -200,6 +202,9 @@ x_vi(char *buf, size_t len) { int c; + x_resize_cb = NULL; + check_sigwinch(); + x_resize_cb = x_vi_resize; vi_reset(buf, len > LINE ? LINE : len); vi_pprompt(1); x_flush(); @@ -242,6 +247,7 @@ x_vi(char *buf, size_t len) x_flush(); } + x_resize_cb = NULL; x_putc('\r'); x_putc('\n'); x_flush(); if (c == -1 || len <= (size_t)es->linelen) @@ -1425,8 +1431,6 @@ free_edstate(struct edstate *old) static void edit_reset(char *buf, size_t len) { - const char *p; - es = &ebuf; es->cbuf = buf; es->cbufsize = len; @@ -1437,6 +1441,17 @@ edit_reset(char *buf, size_t len) es->cursor = undo->cursor = 0; es->winleft = undo->winleft = 0; + calc_winsize(); + win = 0; + morec = ' '; + holdlen = 0; +} + +static void +calc_winsize(void) +{ + const char *p; + cur_col = pwidth = promptlen(prompt, &p); prompt_skip = p - prompt; if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) { @@ -1453,9 +1468,6 @@ edit_reset(char *buf, size_t len) (void) memset(wbuf[0], ' ', wbuf_len); (void) memset(wbuf[1], ' ', wbuf_len); winwidth = x_cols - pwidth - 3; - win = 0; - morec = ' '; - holdlen = 0; } /* @@ -2305,4 +2317,42 @@ isu8cont(unsigned char c) { return !Flag(FVISHOW8) && (c & (0x80 | 0x40)) == 0x80; } + +static void +x_vi_resize(void) +{ + int cur = 0, col = 0; + int cr_done = 0, cleared = 0; + +#ifndef SMALL + if (cur_term != NULL) { + if (carriage_return != NULL && + tputs(carriage_return, 1, x_putc) != ERR) + cr_done = 1; + if (clr_eos != NULL && + tputs(clr_eos, 1, x_putc) != ERR) + cleared = 1; + } +#endif + if (!cr_done) + x_putc('\r'); + if (!cleared) { + x_putc('\033'); + x_putc('['); + x_putc('J'); + } + + calc_winsize(); + + while (cur < es->linelen) + col = newcol((unsigned char) es->cbuf[cur++], col); + if (col <= winwidth) + es->winleft = 0; + else if (outofwin()) + rewindow(); + + redraw_line(0, 0); + refresh_line(insert != 0); + x_flush(); +} #endif /* VI */ diff --git regress/bin/ksh/edit/edit.c regress/bin/ksh/edit/edit.c index 7b49f371ab6..785dc5a251b 100644 --- regress/bin/ksh/edit/edit.c +++ regress/bin/ksh/edit/edit.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -49,6 +50,7 @@ main(int argc, char *argv[]) ssize_t n; size_t nin, nprompt, nread, nwrite; int c, nready, ptyfd, readprompt, ret, status, timeout; + int pending_resize, pending_cols, pending_quiet, pending_post; while ((c = getopt(argc, argv, "p:")) != -1) { switch (c) { @@ -96,14 +98,20 @@ main(int argc, char *argv[]) } nprompt = nread = nwrite = ret = 0; + pending_resize = pending_cols = pending_quiet = pending_post = 0; readprompt = 1; while (!gotsig) { pfd.fd = ptyfd; - if (!readprompt && nwrite < nin) + if (pending_resize || pending_post) { + pfd.events = POLLIN; + timeout = WRTIM; + } else if (!readprompt && nwrite < nin) { pfd.events = POLLOUT; - else + timeout = WRTIM; + } else { pfd.events = POLLIN; - timeout = readprompt ? PRTIM : WRTIM; + timeout = readprompt ? PRTIM : WRTIM; + } nready = poll(&pfd, 1, timeout); if (nready == -1) { if (errno == EINTR) @@ -111,6 +119,22 @@ main(int argc, char *argv[]) err(1, "poll"); } if (nready == 0) { + if (pending_resize) { + if (++pending_quiet >= 2) { + ws.ws_col = pending_cols; + if (ioctl(ptyfd, TIOCSWINSZ, &ws) == -1) + err(1, "ioctl TIOCSWINSZ"); + kill(pid, SIGWINCH); + pending_resize = pending_cols = 0; + pending_quiet = 0; + pending_post = 1; + } + continue; + } + if (pending_post) { + pending_post = 0; + continue; + } if (timeout == PRTIM) { warnx("timeout waiting from prompt"); ret = 1; @@ -136,6 +160,28 @@ main(int argc, char *argv[]) readprompt = 0; } } else if (pfd.revents & POLLOUT) { + if (in[nwrite] == '\0' && nwrite + 3 < nin && + in[nwrite + 1] == 'R' && + in[nwrite + 2] == 'S' && + in[nwrite + 3] >= '0' && in[nwrite + 3] <= '9') { + size_t i = nwrite + 3; + int cols = 0, nd = 0; + + while (i < nin && nd < 3 && + in[i] >= '0' && in[i] <= '9') { + cols = cols * 10 + (in[i] - '0'); + i++; + nd++; + } + if (nd > 0 && (i == nin || + in[i] < '0' || in[i] > '9') && + cols > 0) { + pending_resize = 1; + pending_cols = cols; + nwrite = i; + continue; + } + } if (strchr(linefeed, in[nwrite]) != NULL) readprompt = 1; @@ -143,6 +189,8 @@ main(int argc, char *argv[]) if (n == -1) err(1, "write"); nwrite += n; + if (pending_resize) + pending_quiet = 0; } } close(ptyfd); diff --git regress/bin/ksh/edit/emacs.sh regress/bin/ksh/edit/emacs.sh index abd664664bb..6ae4184e928 100644 --- regress/bin/ksh/edit/emacs.sh +++ regress/bin/ksh/edit/emacs.sh @@ -52,6 +52,30 @@ testseq "z\0002\0363\0277\0277\0277" " # z\b\0363\0277\0277\0277z\b" testseq "z\0002\0364\0200\0200\0200" " # z\b\0364\0200\0200\0200z\b" testseq "z\0002\0364\0217\0277\0277" " # z\b\0364\0217\0277\0277z\b" +# ASCII resize: cols=1/11/12 +testseq "aaaaaaaaaabbbbbbbbbb\0001\000RS1" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaa>\b\b\b\b\b\b\b\b" +testseq "aaaaaaaaaabbbbbbbbbb\003310\0002\000RS11" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\r\033[J\raaaaabbbbb*\b\b\b\b\b\b" +testseq "aaaaaaaaaabbbbbbbbbb\000RS12" \ + " # aaaaaaaaaabbbbbbbbbb\r\033[J\rbbbbb <\b\b\b\b\b\b" + +# ASCII resize: cols=13, cursor -1/0/+1 +testseq "aaaaaaaaaabbbbbbbbbb\003313\0002\000RS13" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaa>\b\b" +testseq "aaaaaaaaaabbbbbbbbbb\003312\0002\000RS13" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaa>\b" +testseq "aaaaaaaaaabbbbbbbbbb\003311\0002\000RS13" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\r\033[J\raaaaaabbbbb*\b\b\b\b\b\b\b" + +# ASCII resize: cols=79/80/81 +testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS79" \ + " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc>\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" +testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS80" \ + " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" +testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS81" \ + " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + # insertion of incomplete UTF-8 testseq "z\0002\0302\0006" " # z\b\0302z\bz" testseq "z\0002\0377\0006" " # z\b\0377z\bz" diff --git regress/bin/ksh/edit/vi.sh regress/bin/ksh/edit/vi.sh index d9ae7967908..eef5886f999 100644 --- regress/bin/ksh/edit/vi.sh +++ regress/bin/ksh/edit/vi.sh @@ -233,3 +233,27 @@ testseq "(x)\00330%hrc" " # (x)\b\b\b(x\bc\b" # ^R: Redraw. testseq "test\0033h\0022" " # test\b\b\r\r\n # test\b\b" + +# ASCII resize: cols=1/11/12 +testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhhhhhhhhhh\000RS1" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J# aaaaaaa >\r# " +testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhh\000RS11" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\r\033[J# aaabbbb +\b\b\b\b\b\b" +testseq "aaaaaaaaaabbbbbbbbbb\0033\000RS12" \ + " # aaaaaaaaaabbbbbbbbbb\b\r\033[J# bbbb <\b\b\b\b\b\b" + +# ASCII resize: cols=13, cursor -1/0/+1 +testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhhhh\000RS13" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaa >\b\b\b" +testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhhh\000RS13" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaab +\b\b\b\b\b\b" +testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhh\000RS13" \ + " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaabb +\b\b\b\b\b\b" + +# ASCII resize: cols=79/80/81 +testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS79" \ + " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc >\r # " +testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS80" \ + " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" +testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS81" \ + " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc >\r # " -- wbr, Kirill