GUAC-1452: Echo to pipe stream if open (buffered).

This commit is contained in:
Michael Jumper 2016-01-13 17:38:13 -08:00
parent 01edd8ccd8
commit ff557a7f01
3 changed files with 156 additions and 33 deletions

View File

@ -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.");
}
}

View File

@ -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

View File

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