diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 9ee56812..99ae54d6 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -1731,3 +1731,80 @@ int guac_terminal_next_tab(guac_terminal* term, int column) { return tabstop; } +void guac_terminal_pipe_stream_open(guac_terminal* term, const char* name) { + + guac_client* client = term->client; + guac_socket* socket = client->socket; + + /* Close existing stream, if any */ + guac_terminal_pipe_stream_close(term); + + /* Allocate and assign new pipe stream */ + term->pipe_stream = guac_client_alloc_stream(client); + term->pipe_buffer_length = 0; + + /* Open new pipe stream */ + guac_protocol_send_pipe(socket, term->pipe_stream, "text/plain", name); + + /* Log redirect at debug level */ + guac_client_log(client, GUAC_LOG_DEBUG, + "Terminal output now redirected to pipe '%s'.", name); + +} + +void guac_terminal_pipe_stream_write(guac_terminal* term, char c) { + + /* Append byte to buffer only if pipe is open */ + if (term->pipe_stream != NULL) { + + /* Flush buffer if no space is available */ + if (term->pipe_buffer_length == sizeof(term->pipe_buffer)) + guac_terminal_pipe_stream_flush(term); + + /* Append single byte to buffer */ + term->pipe_buffer[term->pipe_buffer_length++] = c; + + } + +} + +void guac_terminal_pipe_stream_flush(guac_terminal* term) { + + guac_client* client = term->client; + guac_socket* socket = client->socket; + guac_stream* pipe_stream = term->pipe_stream; + + /* Write blob if data exists in buffer */ + if (pipe_stream != NULL && term->pipe_buffer_length > 0) { + guac_protocol_send_blob(socket, pipe_stream, + term->pipe_buffer, term->pipe_buffer_length); + term->pipe_buffer_length = 0; + } + +} + +void guac_terminal_pipe_stream_close(guac_terminal* term) { + + guac_client* client = term->client; + guac_socket* socket = client->socket; + guac_stream* pipe_stream = term->pipe_stream; + + /* Close any existing pipe */ + if (pipe_stream != NULL) { + + /* Write end of stream */ + guac_terminal_pipe_stream_flush(term); + guac_protocol_send_end(socket, pipe_stream); + + /* Destroy stream */ + guac_client_free_stream(client, pipe_stream); + term->pipe_stream = NULL; + + /* Log redirect at debug level */ + guac_client_log(client, GUAC_LOG_DEBUG, + "Terminal output now redirected to display."); + + } + +} + diff --git a/src/terminal/terminal.h b/src/terminal/terminal.h index 3b244ae0..91bebfa9 100644 --- a/src/terminal/terminal.h +++ b/src/terminal/terminal.h @@ -156,6 +156,19 @@ struct guac_terminal { */ guac_stream* pipe_stream; + /** + * Buffer of data pending write to the pipe_stream. Data within this buffer + * will be flushed to the pipe_stream when either (1) the buffer is full + * and another character needs to be written or (2) the pipe_stream is + * closed. + */ + char pipe_buffer[6048]; + + /** + * The number of bytes currently stored within the pipe_buffer. + */ + int pipe_buffer_length; + /** * Graphical representation of the current scroll state. */ @@ -663,5 +676,58 @@ void guac_terminal_clear_tabs(guac_terminal* term); */ int guac_terminal_next_tab(guac_terminal* term, int column); +/** + * Opens a new pipe stream, redirecting all output from the given terminal to + * that pipe stream. If a pipe stream is already open, that pipe stream will + * be flushed and closed prior to opening the new pipe stream. + * + * @param term + * The terminal which should redirect output to a new pipe stream having + * the given name. + * + * @param name + * The name of the pipe stream to open. + */ +void guac_terminal_pipe_stream_open(guac_terminal* term, const char* name); + +/** + * Writes a single byte of data to the pipe stream currently open and + * associated with the given terminal. The pipe stream must already have been + * opened via guac_terminal_pipe_stream_open(). If no pipe stream is currently + * open, this function has no effect. Data written through this function may + * be buffered. + * + * @param term + * The terminal whose currently-open pipe stream should be written to. + * + * @param c + * The byte of data to write to the pipe stream. + */ +void guac_terminal_pipe_stream_write(guac_terminal* term, char c); + +/** + * Flushes any data currently buffered for the currently-open pipe stream + * associated with the given terminal. The pipe stream must already have been + * opened via guac_terminal_pipe_stream_open(). If no pipe stream is currently + * open or no data is in the buffer, this function has no effect. + * + * @param term + * The terminal whose pipe stream buffer should be flushed. + */ +void guac_terminal_pipe_stream_flush(guac_terminal* term); + +/** + * Closes the currently-open pipe stream associated with the given terminal, + * redirecting all output back to the terminal display. Any data currently + * buffered for output to the pipe stream will be flushed prior to closure. The + * pipe stream must already have been opened via + * guac_terminal_pipe_stream_open(). If no pipe stream is currently open, this + * function has no effect. + * + * @param term + * The terminal whose currently-open pipe stream should be closed. + */ +void guac_terminal_pipe_stream_close(guac_terminal* term); + #endif diff --git a/src/terminal/terminal_handlers.c b/src/terminal/terminal_handlers.c index 7ad23208..ad943b00 100644 --- a/src/terminal/terminal_handlers.c +++ b/src/terminal/terminal_handlers.c @@ -59,6 +59,12 @@ int guac_terminal_echo(guac_terminal* term, unsigned char c) { const int* char_mapping = term->char_mapping[term->active_char_set]; + /* Echo to pipe stream if open and not starting an ESC sequence */ + if (term->pipe_stream != NULL && c != 0x1B) { + guac_terminal_pipe_stream_write(term, c); + return 0; + } + /* If using non-Unicode mapping, just map straight bytes */ if (char_mapping != NULL) { codepoint = c; @@ -940,9 +946,6 @@ int guac_terminal_download(guac_terminal* term, unsigned char c) { int guac_terminal_open_pipe_stream(guac_terminal* term, unsigned char c) { - guac_client* client = term->client; - guac_socket* socket = client->socket; - static char stream_name[2048]; static int length = 0; @@ -951,25 +954,14 @@ int guac_terminal_open_pipe_stream(guac_terminal* term, unsigned char c) { /* End stream name string */ stream_name[length++] = '\0'; - term->char_handler = guac_terminal_echo; length = 0; - /* Close existing stream, if any */ - if (term->pipe_stream != NULL) { - guac_protocol_send_end(socket, term->pipe_stream); - guac_client_free_stream(client, term->pipe_stream); - } - - /* Allocate and assign new pipe stream */ - term->pipe_stream = guac_client_alloc_stream(client); - /* Open new pipe stream */ - guac_protocol_send_pipe(socket, term->pipe_stream, - "text/plain", stream_name); + guac_terminal_pipe_stream_open(term, stream_name); + + /* Return to echo mode */ + term->char_handler = guac_terminal_echo; - /* Log redirect at debug level */ - guac_client_log(client, GUAC_LOG_DEBUG, - "Terminal output now redirected to pipe '%s'.", stream_name); } /* Otherwise, store character within stream name */ @@ -982,26 +974,14 @@ int guac_terminal_open_pipe_stream(guac_terminal* term, unsigned char c) { int guac_terminal_close_pipe_stream(guac_terminal* term, unsigned char c) { - guac_client* client = term->client; - guac_socket* socket = client->socket; - /* Handle closure on ECMA-48 ST (String Terminator) */ if (c == 0x9C || c == 0x5C || c == 0x07) { - term->char_handler = guac_terminal_echo; /* Close any existing pipe */ - if (term->pipe_stream != NULL) { - guac_protocol_send_end(socket, term->pipe_stream); - guac_client_free_stream(client, term->pipe_stream); - term->pipe_stream = NULL; - guac_client_log(client, GUAC_LOG_DEBUG, - "Terminal output now redirected to display."); - } + guac_terminal_pipe_stream_close(term); - /* Warn if OSC is inappropriate */ - else - guac_client_log(client, GUAC_LOG_DEBUG, - "Cannot handle pipe close OSC - no open pipe exists."); + /* Return to echo mode */ + term->char_handler = guac_terminal_echo; }