[PATCH v2] libmisc/shell: Work around tmux bug in row and column
Chris Johns
chrisj at rtems.org
Wed Dec 13 07:10:03 UTC 2023
On 13/12/2023 5:58 pm, chrisj at rtems.org wrote:
> From: Chris Johns <chrisj at rtems.org>
>
> - Extend the timeout to 150 msec for long remote sessions
>
> - Improve the performance of the detection
>
> Closes #4975
> Closes #4977
> ---
> cpukit/libmisc/shell/shell.c | 150 +++++++++++++++++++++++++++++------
> 1 file changed, 125 insertions(+), 25 deletions(-)
>
> diff --git a/cpukit/libmisc/shell/shell.c b/cpukit/libmisc/shell/shell.c
> index cd83aa56d1..d8853d74a4 100644
> --- a/cpukit/libmisc/shell/shell.c
> +++ b/cpukit/libmisc/shell/shell.c
> @@ -805,6 +805,109 @@ void rtems_shell_print_env(
> }
> #endif
>
> +/*
> + * Wait for the string to return or timeout.
> + */
> +static bool rtems_shell_term_wait_for(const int fd, const char* str, const int timeout)
> +{
> + int msec = timeout;
> + int i = 0;
> + while (msec-- > 0 && str[i] != '\0') {
> + char ch[2];
> + if (read(fd, &ch[0], 1) == 1) {
> + fflush(stdout);
> + if (ch[0] != str[i++]) {
> + return false;
> + }
> + msec = timeout;
> + } else {
> + usleep(1000);
> + }
> + }
> + if (msec == 0) {
> + return false;
> + }
> + return true;
> +}
> +
> +/*
> + * Buffer a string up to the end string
> + */
> +static int rtems_shell_term_buffer_until(const int fd,
> + char* buf,
> + const int size,
> + const char* end,
> + const int timeout)
> +{
> + int msec = timeout;
> + int i = 0;
> + int e = 0;
> + memset(&buf[0], 0, size);
> + while (msec-- > 0 && i < size && end[e] != '\0') {
> + char ch[2];
> + if (read(fd, &ch[0], 1) == 1) {
> + fflush(stdout);
> + buf[i++] = ch[0];
> + if (ch[0] == end[e]) {
> + e++;
> + } else {
> + e = 0;
> + }
> + msec = timeout;
> + } else {
> + usleep(1000);
> + }
> + }
> + if (msec == 0 || end[e] != '\0') {
> + return -1;
> + }
> + i -= e;
> + if (i < size) {
> + buf[i] = '\0';
> + }
> + return i;
> +}
> +
> +/*
> + * Determine if the terminal has the row and column values
> + * swapped
> + *
> + * https://github.com/tmux/tmux/issues/3457
> + *
> + * Tmux has a bug where the lines and cols are swapped. There is a lag
> + * in the time it takes to get the fix into code so see if tmux is
> + * running and which version and work around the bug.
> + *
> + * The terminal device needs to have VMIN=0, and VTIME=0
> + */
> +static bool rtems_shell_term_row_column_swapped(const int fd, const int timeout) {
> + char buf[64];
> + memset(&buf[0], 0, sizeof(buf));
> + /*
> + * CSI > Ps q
> + * Ps = 0 => DCS > | text ST
> + */
> + fputs("\033[>0q", stdout);
> + fflush(stdout);
> + if (rtems_shell_term_wait_for(fd, "\033P>|", timeout)) {
> + int len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), "\033\\", timeout);
> + if (len > 0) {
> + if (memcmp(buf, "tmux ", 5) == 0) {
> + static const char* bad_versions[] = {
> + "3.2", "3.2a", "3.3", "3.3a"
> + };
> + size_t i;
> + for (i = 0; i < RTEMS_ARRAY_SIZE(bad_versions); ++i) {
> + if (strcmp(bad_versions[i], buf + 5) == 0) {
> + return true;
> + }
> + }
> + }
> + }
> + }
> + return false;
> +}
> +
> /*
> * Direct method to get the size of an XTERM window.
> *
> @@ -814,6 +917,7 @@ static void rtems_shell_winsize( void )
> {
> const int fd = fileno(stdin);
> struct winsize ws;
> + const int timeout = 150;
> char buf[64];
> bool ok = false;
> int lines = 0;
> @@ -831,9 +935,6 @@ static void rtems_shell_winsize( void )
> term.c_cc[VMIN] = 0;
> term.c_cc[VTIME] = 0;
> if (tcsetattr (fd, TCSADRAIN, &term) >= 0) {
> - int msec = 50;
> - int len = 0;
> - int i = 0;
> memset(&buf[0], 0, sizeof(buf));
> /*
> * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Miscellaneous
> @@ -842,39 +943,38 @@ static void rtems_shell_winsize( void )
> */
> fputs("\033[18t", stdout);
> fflush(stdout);
> - while (msec-- > 0 && len < sizeof(buf)) {
> - char ch[2];
> - if (read(fd, &ch[0], 1) == 1) {
> - buf[len++] = ch[0];
> - msec = 50;
> - } else {
> - usleep(1000);
> - }
> - }
> - while (i < len) {
> - static const char resp[] = "\033[8;";
> - if (memcmp(resp, &buf[i], sizeof(resp) - 1) == 0) {
> - i += sizeof(resp) - 1;
> - while (i < len && buf[i] != ';') {
> + if (rtems_shell_term_wait_for(fd, "\033[8;", timeout)) {
> + int len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), ";", timeout);
> + if (len > 0) {
> + int i;
> + lines = 0;
> + i = 0;
> + while (i < len) {
> lines *= 10;
> lines += buf[i++] - '0';
> }
> - cols = 0;
> - ++i;
> - while (i < len && buf[i] != 't') {
> - cols *= 10;
> - cols += buf[i++] - '0';
> + len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), "t", timeout);
> + if (len > 0) {
> + cols = 0;
> + i = 0;
> + while (i < len) {
> + cols *= 10;
> + cols += buf[i++] - '0';
> + }
> + ok = true;
> }
> - } else {
> - i++;
> }
> - ok = true;
> }
> }
> tcsetattr (fd, TCSADRAIN, &cterm);
> }
> }
> if (ok) {
> + if (rtems_shell_term_row_column_swapped(fd, timeout)) {
This here is wrong as the call has to be in the isatty() block. I will send v3.
Chris
> + int tmp = lines;
> + lines = cols;
> + cols = tmp;
> + }
> snprintf(buf, sizeof(buf) - 1, "%d", lines);
> setenv("LINES", buf, 1);
> snprintf(buf, sizeof(buf) - 1, "%d", cols);
More information about the devel
mailing list