diff --git a/include/gsocket/buf.h b/include/gsocket/buf.h index ba9f0cfe..906be879 100644 --- a/include/gsocket/buf.h +++ b/include/gsocket/buf.h @@ -14,9 +14,10 @@ typedef struct void GS_BUF_init(GS_BUF *gsb, size_t sz_min_free); void GS_BUF_free(GS_BUF *gsb); int GS_BUF_resize(GS_BUF *gsb, size_t sz_new); -int GS_BUF_add(GS_BUF *gsb, size_t len); +int GS_BUF_add_length(GS_BUF *gsb, size_t len); int GS_BUF_add_data(GS_BUF *gsb, void *data, size_t len); int GS_BUF_del(GS_BUF *gsb, size_t len); +int GS_BUF_memmove(GS_BUF *gsb, void *data, size_t len); #define GS_BUF_empty(gsb) (gsb)->sz_used = 0; #define GS_BUF_DATA(gsb) (gsb)->data diff --git a/lib/buf.c b/lib/buf.c index 373623da..e368b8d0 100644 --- a/lib/buf.c +++ b/lib/buf.c @@ -40,7 +40,7 @@ GS_BUF_resize(GS_BUF *gsb, size_t sz_new) } int -GS_BUF_add(GS_BUF *gsb, size_t len) +GS_BUF_add_length(GS_BUF *gsb, size_t len) { // Bail. There is sz_max_add space available but looks like caller wrote // more ata... @@ -65,6 +65,17 @@ GS_BUF_add_data(GS_BUF *gsb, void *data, size_t len) return 0; } +int +GS_BUF_memmove(GS_BUF *gsb, void *data, size_t len) +{ + GS_BUF_resize(gsb, len); + memmove((uint8_t *)gsb->data + gsb->sz_used, data, len); + + gsb->sz_used += len; + + return 0; +} + /* * Consume data from beginning. */ diff --git a/tools/common.h b/tools/common.h index fa2bab7b..4f0fc98a 100755 --- a/tools/common.h +++ b/tools/common.h @@ -339,7 +339,7 @@ extern struct _g_debug_ctx g_dbg_ctx; // declared in utils.c #ifdef DEBUG # define HEXDUMP(a, _len) do { \ size_t _n = 0; \ - xfprintf(gopt.err_fp, "%s:%d HEX ", __FILE__, __LINE__); \ + xfprintf(gopt.err_fp, "%s:%d HEX[%zd] ", __FILE__, __LINE__, _len); \ while (_n < (_len)) xfprintf(gopt.err_fp, "%2.2x", ((unsigned char *)a)[_n++]); \ xfprintf(gopt.err_fp, "\n"); \ } while (0) diff --git a/tools/console.c b/tools/console.c index e55afd43..d1cbecfd 100644 --- a/tools/console.c +++ b/tools/console.c @@ -47,6 +47,13 @@ static const char *sb_color = "\x1B[44m\x1B[30m"; // Black on Blue #define GS_CONSOLE_BUF_SIZE (1024) #define GS_CONDIS_ROWS (GS_CONSOLE_ROWS - 2) +enum _gs_ut_cursor_flags { + GS_UT_CURSOR_ON = 0x01, + GS_UT_CURSOR_OFF = 0x02 +}; +enum _gs_ut_cursor_flags ut_cursor; + + struct _console_info { char statusbar[512]; @@ -141,15 +148,24 @@ tty_write(void *src, size_t len) static int is_cursor_in_console; static void -console_cursor_off(void) +cursor_to_ut(void) { - tty_write("\x1B""8", 2); // Move cursor to upper tier + char buf[64]; + char *end = buf + sizeof (buf); + char *ptr = buf; + + // If Upper Tier disabled the cursor then do NOT show it. + if (ut_cursor == GS_UT_CURSOR_OFF) + SXPRINTF(ptr, end - ptr, "\x1B[?25l"); + + SXPRINTF(ptr, end - ptr, "\x1B""8"); + tty_write(buf, ptr - buf); is_cursor_in_console = 0; } static void -console_cursor_on(void) +cursor_to_lt(void) { char buf[64]; char *end = buf + sizeof (buf); @@ -158,15 +174,20 @@ console_cursor_on(void) int row = gopt.winsize.ws_row; int col = 1 + GS_CONSOLE_PROMPT_LEN + MIN(rl.pos, rl.visible_len); - DEBUGF_W("Console Cursor ON (%d:%df)\n", row, col); - // ESC[?2004l = Reset bracketed paste mode + // DEBUGF_W("Cursor to CONSOLE (Lower Tier) (%d:%df)\n", row, col); SXPRINTF(ptr, end - ptr, "\x1B[%d;%df", row, col); + // ESC[?2004l = Reset bracketed paste mode if (is_console_cursor_needs_reset) { SXPRINTF(ptr, end - ptr, "\x1B[?2004l"); is_console_cursor_needs_reset = 0; } + // If Upper Tier disabled the cursor then show it in console + // DEBUGF_R("ut-cursor = %d\n", ut_cursor); + if (ut_cursor == GS_UT_CURSOR_OFF) + SXPRINTF(ptr, end - ptr, "\x1B[?25h"); + tty_write(buf, ptr - buf); is_cursor_in_console = 1; @@ -551,9 +572,7 @@ CONSOLE_check_esc(uint8_t c, uint8_t *submit) if (gopt.is_console == 0) return 0; // Ignore if no console - console_cursor_off(); - // console_stop(); - // gopt.is_console = 0; + cursor_to_ut(); return 0; case 'B': // DOWN if (esc == 0) @@ -561,7 +580,7 @@ CONSOLE_check_esc(uint8_t c, uint8_t *submit) if (gopt.is_console == 0) return 0; // Ignore if no console // Arrow Down - console_cursor_on(); + cursor_to_lt(); return 0; case GS_CONSOLE_ESC_CHR: case GS_CONSOLE_ESC_LCHR: @@ -685,19 +704,19 @@ ansi_until_end(uint8_t *src, size_t src_sz, int *ignore) if ((*src >= 'a') && (*src <= 'z')) { src++; - break; + return src - src_orig; } if ((*src >= 'A') && (*src <= 'Z')) { src++; - break; + return src - src_orig; } src++; } - return src - src_orig; + return 0; // Not enough data // src - src_orig; } static size_t @@ -722,7 +741,6 @@ ansi_until_esc(uint8_t *src, size_t src_sz, int *in_esc) static int in_esc; - // Parse 'src' for an ansi sequence that we might be interested in. // *tail_len contains a number of bytes if there is an incomplete ansi-sequence (and we // do not have enough data yet) @@ -735,6 +753,7 @@ ansi_parse(uint8_t *src, size_t src_sz, GS_BUF *dst, size_t *tail_len, int *cls_ size_t len; int ignore; + *tail_len = 0; while (src < src_end) { if (in_esc) @@ -744,12 +763,26 @@ ansi_parse(uint8_t *src, size_t src_sz, GS_BUF *dst, size_t *tail_len, int *cls_ if (len == 0) { // Not enough data + DEBUGF_R("Not Enough Data. TAIL %zd\n", src_end - src); + DEBUGF("esc len=%zd, ignore=%d, dst=%zd, left=%zd\n", len, ignore, GS_BUF_USED(dst), src_end - src); + HEXDUMP(src, src_end - src); *tail_len = src_end - src; - break; + return; //break; } in_esc = 0; - DEBUGF_B("ANSI %.*s\n", (int)len -1, src+1); +#ifdef DEBUG + // Output some ANSI but ignore some often re-occuring codes: + while (1) + { + if (len <= 4) + break; // Ignore short ones...like [1m + if ((len == 8) && (src[7] == 'm')) + break; // Ingore [39;49m to debug 'top' + DEBUGF_B("ANSI %.*s\n", (int)len -1, src+1); + break; + } +#endif if (ignore) { GS_BUF_add_data(dst, src, len); @@ -757,7 +790,43 @@ ansi_parse(uint8_t *src, size_t src_sz, GS_BUF *dst, size_t *tail_len, int *cls_ continue; } - // If console is not open then we do not have to check or substitute any ansi sequences + // Check if the Upper Tier (ut) wants the cursor prompt ON or OFF + // Check for this even if the console is closed so that when we open the console + // that the right cursor can be displayed + int is_substitute = 0; + while (len == 6) + { + if (memcmp(src + 1, "[?25l", 5) == 0) + ut_cursor = GS_UT_CURSOR_OFF; // OFF + else if (memcmp(src + 1, "[?25h", 5) == 0) + ut_cursor = GS_UT_CURSOR_ON; // ON + else + break; + + // DEBUGF_R("ut_cursor=%d, in-console=%d\n", ut_cursor, is_cursor_in_console); + // If cursor is in console then ignore all requests + if (is_cursor_in_console) + { + is_substitute = 1; + src += len; + break; + } + break; + } + if (is_substitute) + continue; + + // Check for Bracketed paste mode [?2004l + if (len == 8) + { + if (memcmp(src + 1, "[?2004l", 7) == 0) + is_console_cursor_needs_reset = 0; + else if (memcmp(src + 1, "[?2004h", 7) == 0) + is_console_cursor_needs_reset = 1; + } + + + // If console is not open then we do not have to check any other ansi symboles if (gopt.is_console == 0) { GS_BUF_add_data(dst, src, len); @@ -768,7 +837,7 @@ ansi_parse(uint8_t *src, size_t src_sz, GS_BUF *dst, size_t *tail_len, int *cls_ // Check if this was a cursor-position request that moved the course // outside its boundary (and into our console, like debian's top does (!)) // '\x1b' + '[1;1h' - int is_substitute = 0; + is_substitute = 0; while (1) { if (len < 6) @@ -828,168 +897,6 @@ ansi_parse(uint8_t *src, size_t src_sz, GS_BUF *dst, size_t *tail_len, int *cls_ } } -#if 0 - -static size_t -ansi_parse(uint8_t *src, size_t src_sz, uint8_t *dst, size_t dst_sz, size_t *amount, int *cls_code) -{ - static int in_esc; - static int in_esc_pos; - static int is_semicolon; - uint8_t *src_orig = src; - uint8_t *src_end = src + src_sz; - uint8_t *src_last = src; - uint8_t *dst_orig = dst; - uint8_t *dst_end = dst + dst_sz; - int rv = 0; - - while (src < src_end) - { - if (*src == '\x1B') - { - if (in_esc) - { - // Encountered a \x1b while inside an escape? Oops. - DEBUGF_R("SHOULD NOT HAPPEN\n"); - cls_pos = 0; - in_esc = 0; - goto skip; - } - in_esc = 1; - src_esc = src; - *amount = src - src_orig; - in_esc_pos = 0; - /* Start of pattern */ - cls_pos = 0; - - size_t len = src - src_last; - if (len > 1) - { - // Found an ESC-sequence. Copy all data until now into dst. - XASSERT(dst + len < dst_end, "Buffer to small!\n"); - memcpy(dst, src_last, len); - dst += len; - src_last = src; - } - - } else { - if (in_esc == 0) - goto skip; // ESC not yet encountered - } - - // Check when escape finishes - while (in_esc != 0) - { - if (*src == '\x1B') - break; - in_esc_pos++; - - if (in_esc_pos == 1) - { - // Check if multi character esc sequence - if (*src == '[') - break; - if (*src == '(') - break; - if (*src == ')') - break; - if (*src == '#') - break; // Esc-#2 - if (*src == '6') - break; // Esc-6n - if (*src == '5') - break; - if (*src == '0') - break; - if (*src == '3') - break; - } - - if (in_esc_pos >= 2) - { - if ((*src >= '0') && (*src <= '9')) - break; - if (*src == ';') - { - is_semicolon = 1; - break; - } - if (*src == '?') - break; - } - - // *src is last character of escape sequence - in_esc = 0; - break; - } - - - - // None of our sequences is longer than this. - if (cls_pos >= sizeof cls_buf) - goto skip; - - /* Record sequence */ - cls_buf[cls_pos] = *src; - cls_pos++; - - // Any sequence we are interested in is at least 2 chars long - if (cls_pos < 2) - goto skip; - - // Check if app tried to move out of screen area (into console) and drop - // such commands (like debian's TOP sends [26;1H] on exit even if scrolling area is - // is 25;80) - DEBUGF("is-semi = %d %c\n", is_semicolon, *src); - if ((in_esc == 0) && (cls_pos > 4) && (is_semicolon)) - { - is_semicolon = 0; - - if ((*src == 'H') || (*src == 'h')) - { - cls_buf[7] = '\0'; - DEBUGF("'%s'\n", (char *)(cls_buf + 1)); - char *ptr = strchr((char *)cls_buf, ';'); - if (ptr != NULL) - { - *ptr = '\0'; - int row = atoi((char *)(cls_buf + 2)); // skipe \x1b[ - DEBUGF("WANTS ROW %d %d\n", row, gopt.winsize.ws_row - GS_CONSOLE_ROWS); - if (row > gopt.winsize.ws_row - GS_CONSOLE_ROWS) - { - DEBUGF_R("DENIED\n"); - cls_pos = 0; - in_esc = 0; - goto skip; - } - } - } - - } - - //Check if any ESC sequence matches - int i; - for (i = 0; i < sizeof cls_pattern / sizeof *cls_pattern; i++) - { - if (cls_pattern[i].len != cls_pos) - continue; - if (memcmp(cls_pattern[i].data, cls_buf, cls_pos) != 0) - continue; - rv = cls_pattern[i].type; - cls_pos = 0; - } -skip: - src++; - } - // Not stuck inside esc sequence. - if (in_esc == 0) - *amount = len; - - *cls_code = rv; - - return dst - dst_orig; -} -#endif GS_BUF g_dst; GS_BUF g_ansi; @@ -1028,29 +935,34 @@ ansi_write(int fd, void *src, size_t src_len, int *cls_code) GS_BUF_init(&g_ansi, 1024); } - // HEXDUMP(src, src_len); if (GS_BUF_USED(&g_ansi) > 0) { GS_BUF_add_data(&g_ansi, src, src_len); src = GS_BUF_DATA(&g_ansi); src_len = GS_BUF_USED(&g_ansi); } + + // HEXDUMP(src, src_len); ansi_parse(src, src_len, &g_dst, &tail_len, cls_code); if (GS_BUF_USED(&g_dst) > 0) { if (write(fd, GS_BUF_DATA(&g_dst), GS_BUF_USED(&g_dst)) != GS_BUF_USED(&g_dst)) + { + DEBUGF_R("Failed to write() all data...\n"); // SHOULD NOT HAPPEN return -1; + } } GS_BUF_empty(&g_dst); + GS_BUF_empty(&g_ansi); if (tail_len > 0) { - GS_BUF_empty(&g_ansi); - GS_BUF_add_data(&g_ansi, src + src_len - tail_len, tail_len); - DEBUGF_W("TAIL length %zd\n", GS_BUF_USED(&g_ansi)); + // Use memmove() here because src might be pointing to same data but further along + GS_BUF_memmove(&g_ansi, src + src_len - tail_len, tail_len); } + // From the caller's perspective this function has processed all data // and this function will buffer (if needed) any data not yet passed // to 'write()'. Thus return 'len' here to satisfy caller that all supplied @@ -1087,7 +999,12 @@ CONSOLE_write(int fd, void *data, size_t len) /* Move cursor to upper tier if cursor inside console */ if (is_cursor_in_console) - tty_write("\x1B""8", 2); // Restore cursor to upper tier + { + if (ut_cursor == GS_UT_CURSOR_OFF) + tty_write("\x1B[?25l\x1B""8", 6+2); // Restore cursor to upper tier + else + tty_write("\x1B""8", 2); // Restore cursor to upper tier + } ssize_t sz; sz = ansi_write(fd, data, len, &is_detected_clearscreen); @@ -1096,8 +1013,8 @@ CONSOLE_write(int fd, void *data, size_t len) // The write() to upper tier may have set some funky paste modes // and we need to reset this for console input. - if (sz > 0) - is_console_cursor_needs_reset = 1; + // if (sz > 0) + // is_console_cursor_needs_reset = 1; // if (len > 16) // HEXDUMP(data, MIN(16, len)); @@ -1144,8 +1061,7 @@ CONSOLE_write(int fd, void *data, size_t len) if (is_cursor_in_console) { - DEBUGF("is_cursor_in_console is true\n"); - console_cursor_on(); + cursor_to_lt(); } return sz; @@ -1230,8 +1146,11 @@ console_start(void) SXPRINTF(ptr, end - ptr, "\x1b[1;%dr", row); // Restore cursor to saved location SXPRINTF(ptr, end - ptr, "\x1B""8"); - tty_write(buf, ptr - buf); + + gopt.is_console = 1; + + cursor_to_lt(); // Start with cursor in console } /* @@ -1249,10 +1168,15 @@ console_stop(void) SXPRINTF(ptr, end - ptr, "\x1B[J"); // Reset scroll size SXPRINTF(ptr, end - ptr, "\x1B[r"); + // Upper Tier wants cursor OFF + if (ut_cursor == GS_UT_CURSOR_OFF) + SXPRINTF(ptr, end - ptr, "\x1B[?25l"); // Restore cursor to upper tier (shell) SXPRINTF(ptr, end - ptr, "\x1B""8"); + tty_write(buf, ptr - buf); is_cursor_in_console = 0; + gopt.is_console = 0; } /* @@ -1298,12 +1222,10 @@ CONSOLE_action(struct _peer *p, uint8_t key) { // Close console and restore cursor console_stop(); - gopt.is_console = 0; return 0; } console_start(); - gopt.is_console = 1; GS_condis_pos(&gs_condis, (gopt.winsize.ws_row - GS_CONSOLE_ROWS) + 1 + 1, gopt.winsize.ws_col); if (is_console_welcome_msg == 0) @@ -1315,7 +1237,6 @@ CONSOLE_action(struct _peer *p, uint8_t key) GS_condis_add(&gs_condis, 0, "Type 'help' for a list of commands."); is_console_welcome_msg = 1; } - console_cursor_on(); // Start with cursor in console // Draw console needed? Resizing remote will trigger a CLEAR (=> re-draw) mk_statusbar(); console_draw(p->fd_out, 1); diff --git a/tools/filetransfer-test.c b/tools/filetransfer-test.c index 4ad3e443..c9bd374e 100644 --- a/tools/filetransfer-test.c +++ b/tools/filetransfer-test.c @@ -241,7 +241,7 @@ mk_packet(void) // DEBUGF("Packet type=%u length %zu + %zu\n", hdr->type, sizeof *hdr, sz); XASSERT(sz + sizeof *hdr <= GS_BUF_UNUSED(&gsb), "Oops, GS_FT_packet() to long. sz=%zu, unusued=%zu.\n", sz, GS_BUF_UNUSED(&gsb)); - GS_BUF_add(&gsb, sizeof *hdr + sz); + GS_BUF_add_length(&gsb, sizeof *hdr + sz); return 0; } diff --git a/tools/filetransfer.c b/tools/filetransfer.c index 676b748e..3bb63fa6 100644 --- a/tools/filetransfer.c +++ b/tools/filetransfer.c @@ -698,7 +698,7 @@ GS_FT_list_add_files(GS_FT *ft, uint32_t globbing_id, const char *pattern, size_ if (cwd_fd >= 0) { // Change back to original CWD. - fchdir(cwd_fd); + if (fchdir(cwd_fd) == 0) {} // ignore results close(cwd_fd); } XFREE(ptr); diff --git a/tools/filetransfer_mgr.c b/tools/filetransfer_mgr.c index 142be185..675b2633 100644 --- a/tools/filetransfer_mgr.c +++ b/tools/filetransfer_mgr.c @@ -233,7 +233,7 @@ GS_FTM_mk_packet(GS_FT *ft, uint8_t *dst, size_t dlen) // DEBUGF_G("TYPE NONE\n"); return 0; case GS_FT_TYPE_DONE: - DEBUGF_W("GS_FT_TYPE_DONE\n"); + // DEBUGF_W("GS_FT_TYPE_DONE\n"); // CLIENT only: done with all files. // FIXME: for a 'get' request this is triggered very late and not as soon // as the filetransfer is done (because select() only waits for reading