Merge pull request #103 from glyptodon/terminal-pipe-streams
GUAC-1452: Implement OSC sequence for redirecting output to pipe streams
This commit is contained in:
commit
f49540f436
231
bin/guacctl
231
bin/guacctl
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013 Glyptodon LLC
|
# Copyright (C) 2016 Glyptodon LLC
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@ -25,20 +25,25 @@
|
|||||||
# guacctl
|
# guacctl
|
||||||
# -------
|
# -------
|
||||||
#
|
#
|
||||||
# Utility for sending Guacamole-specific console codes for controlling the SSH
|
# Utility for sending Guacamole-specific console codes for controlling a
|
||||||
# session, such as:
|
# terminal session, such as:
|
||||||
#
|
#
|
||||||
# * Downloading files
|
# * Downloading files (SSH only)
|
||||||
# * Setting the destination directory for uploads
|
# * Setting the destination directory for uploads (SSH only)
|
||||||
|
# * Redirecting output to a named pipe stream (SSH or telnet)
|
||||||
#
|
#
|
||||||
# This script may also be run as "guacget", in which case the script accepts
|
# This script may also be run as "guacget", in which case the script accepts
|
||||||
# no options and assumes anything given on the commandline is a file to be
|
# no options and assumes anything given on the commandline is a file to be
|
||||||
# downloaded.
|
# downloaded.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
##
|
||||||
# Given the name of a file, which may be a relative path, produce the full,
|
## Given the name of a file, which may be a relative path, produce the full,
|
||||||
# real, non-relative path for that same file.
|
## real, non-relative path for that same file.
|
||||||
|
##
|
||||||
|
## @param FILENAME
|
||||||
|
## The name of the file to produce the full path of.
|
||||||
|
##
|
||||||
fullpath() {
|
fullpath() {
|
||||||
FILENAME="$1"
|
FILENAME="$1"
|
||||||
DIR=`dirname "$FILENAME"`
|
DIR=`dirname "$FILENAME"`
|
||||||
@ -46,44 +51,98 @@ fullpath() {
|
|||||||
(cd "$DIR" && echo "$PWD/$FILE")
|
(cd "$DIR" && echo "$PWD/$FILE")
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sends the Guacamole-specific console code for initiating a download.
|
##
|
||||||
|
## Sends the Guacamole-specific console code for initiating a download.
|
||||||
|
##
|
||||||
|
## @param FILENAME
|
||||||
|
## The full path of the file to download.
|
||||||
|
##
|
||||||
send_download_file() {
|
send_download_file() {
|
||||||
FILENAME="$1"
|
FILENAME="$1"
|
||||||
printf "\033]482200;%s\007" "$FILENAME"
|
printf "\033]482200;%s\007" "$FILENAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Sends the Guacamole-specific console code for setting the upload directory.
|
##
|
||||||
|
## Sends the Guacamole-specific console code for setting the upload directory.
|
||||||
|
##
|
||||||
|
## @param FILENAME
|
||||||
|
## The full path to the directory which should receive uploads.
|
||||||
|
##
|
||||||
send_set_directory() {
|
send_set_directory() {
|
||||||
FILENAME="$1"
|
FILENAME="$1"
|
||||||
printf "\033]482201;%s\007" "$FILENAME"
|
printf "\033]482201;%s\007" "$FILENAME"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Prints the given error text to STDERR.
|
##
|
||||||
|
## Sends the Guacamole-specific console code for redirecting output to a named
|
||||||
|
## pipe stream (instead of the terminal emulator)
|
||||||
|
##
|
||||||
|
## @param NAME
|
||||||
|
## The name of the pipe stream to open.
|
||||||
|
##
|
||||||
|
send_open_pipe_stream() {
|
||||||
|
NAME="$1"
|
||||||
|
printf "\033]482202;%s\007" "$NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Sends the Guacamole-specific console code for redirecting output back to the
|
||||||
|
## terminal emulator
|
||||||
|
##
|
||||||
|
send_close_pipe_stream() {
|
||||||
|
printf "\033]482203;\007"
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Prints the given error text to STDERR.
|
||||||
|
##
|
||||||
|
## @param ...
|
||||||
|
## The text to print as an error message.
|
||||||
|
##
|
||||||
error() {
|
error() {
|
||||||
echo "$NAME:" "$@" >&2
|
echo "$NAME:" "$@" >&2
|
||||||
}
|
}
|
||||||
|
|
||||||
# Prints usage documentation for this script.
|
##
|
||||||
|
## Prints usage documentation for this script.
|
||||||
|
##
|
||||||
usage() {
|
usage() {
|
||||||
cat >&2 <<END
|
cat >&2 <<END
|
||||||
guacctl 0.8.0, Guacamole SSH session control utility.
|
guacctl 0.9.9, Guacamole terminal session control utility.
|
||||||
Usage: guacctl [OPTION] [FILE]...
|
Usage: guacctl [OPTION] [FILE or NAME]...
|
||||||
|
|
||||||
-d, --download download each of the files listed.
|
-d, --download download each of the files listed.
|
||||||
-s, --set-directory set the destination directory for future uploaded
|
-s, --set-directory set the destination directory for future uploaded
|
||||||
files.
|
files.
|
||||||
|
-o, --open-pipe redirect output to a new pipe stream with the given
|
||||||
|
name.
|
||||||
|
-c, --close-pipe close any existing pipe stream and redirect output
|
||||||
|
back to the terminal emulator.
|
||||||
END
|
END
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initiates a download for each of the specified files
|
##
|
||||||
|
## Initiates a download for each of the specified files.
|
||||||
|
##
|
||||||
|
## @param ...
|
||||||
|
## The name of each file that should be downloaded, as originally
|
||||||
|
## provided to guacctl.
|
||||||
|
##
|
||||||
download_files() {
|
download_files() {
|
||||||
|
|
||||||
|
#
|
||||||
# Validate arguments
|
# Validate arguments
|
||||||
|
#
|
||||||
|
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then
|
||||||
error "No files specified."
|
error "No files specified."
|
||||||
return;
|
return;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send download code for each file given
|
||||||
|
#
|
||||||
|
|
||||||
for FILENAME in "$@"; do
|
for FILENAME in "$@"; do
|
||||||
if [ -e "$FILENAME" ]; then
|
if [ -e "$FILENAME" ]; then
|
||||||
send_download_file "`fullpath "$FILENAME"`"
|
send_download_file "`fullpath "$FILENAME"`"
|
||||||
@ -94,10 +153,18 @@ download_files() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Changes the upload path for future uploads to the given directory
|
##
|
||||||
|
## Changes the upload path for future uploads to the given directory.
|
||||||
|
##
|
||||||
|
## @param ...
|
||||||
|
## The name of the directory to use for uploads, as provided to guacctl.
|
||||||
|
##
|
||||||
set_directory() {
|
set_directory() {
|
||||||
|
|
||||||
|
#
|
||||||
# Validate arguments
|
# Validate arguments
|
||||||
|
#
|
||||||
|
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then
|
||||||
error "No destination directory specified."
|
error "No destination directory specified."
|
||||||
return;
|
return;
|
||||||
@ -108,6 +175,10 @@ set_directory() {
|
|||||||
return;
|
return;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send code for setting the upload directory
|
||||||
|
#
|
||||||
|
|
||||||
FILENAME="$1"
|
FILENAME="$1"
|
||||||
if [ -d "$FILENAME" ]; then
|
if [ -d "$FILENAME" ]; then
|
||||||
send_set_directory "`fullpath "$FILENAME"`"
|
send_set_directory "`fullpath "$FILENAME"`"
|
||||||
@ -117,20 +188,128 @@ set_directory() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Opens a new pipe stream having the given name and redirects terminal output
|
||||||
|
## to that stream.
|
||||||
|
##
|
||||||
|
## @param ...
|
||||||
|
## The name of the pipe stream to open, as provided to guacctl.
|
||||||
|
##
|
||||||
|
open_pipe_stream() {
|
||||||
|
|
||||||
|
#
|
||||||
|
# Validate arguments
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
error "No pipe name specified."
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $# -gt 1 ]; then
|
||||||
|
error "Only one pipe name may be given."
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send code for opening the named pipe stream
|
||||||
|
#
|
||||||
|
|
||||||
|
NAME="$1"
|
||||||
|
send_open_pipe_stream "$NAME"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Closes the currently-open pipe stream and redirects terminal output back to
|
||||||
|
## the terminal emulator
|
||||||
|
##
|
||||||
|
## @param ...
|
||||||
|
## The arguments provided to guacctl, which should be empty.
|
||||||
|
##
|
||||||
|
close_pipe_stream() {
|
||||||
|
|
||||||
|
#
|
||||||
|
# Validate arguments
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ $# -gt 0 ]; then
|
||||||
|
error "Closing an open pipe stream does not require any arguments."
|
||||||
|
return;
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Send code for closing the currently-open named pipe stream
|
||||||
|
#
|
||||||
|
|
||||||
|
send_close_pipe_stream
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
# Get script name
|
# Get script name
|
||||||
|
#
|
||||||
|
|
||||||
NAME=`basename "$0"`
|
NAME=`basename "$0"`
|
||||||
|
|
||||||
# Parse options
|
#
|
||||||
|
# Handle downloads directly if invoked as "guacget"
|
||||||
|
#
|
||||||
|
|
||||||
if [ "x$NAME" = "xguacget" ]; then
|
if [ "x$NAME" = "xguacget" ]; then
|
||||||
download_files "$@"
|
download_files "$@"
|
||||||
elif [ "x$1" = "x--download" -o "x$1" = "x-d" ]; then
|
exit 0;
|
||||||
shift
|
|
||||||
download_files "$@"
|
|
||||||
elif [ "x$1" = "x--set-directory" -o "x$1" = "x-s" ]; then
|
|
||||||
shift
|
|
||||||
set_directory "$@"
|
|
||||||
else
|
|
||||||
usage
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Parse options
|
||||||
|
#
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download files
|
||||||
|
#
|
||||||
|
|
||||||
|
"--download"|"-d")
|
||||||
|
shift
|
||||||
|
download_files "$@"
|
||||||
|
;;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Set upload directory
|
||||||
|
#
|
||||||
|
|
||||||
|
"--set-directory"|"-s")
|
||||||
|
shift
|
||||||
|
set_directory "$@"
|
||||||
|
;;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Redirect to pipe
|
||||||
|
#
|
||||||
|
|
||||||
|
"--open-pipe"|"-o")
|
||||||
|
shift
|
||||||
|
open_pipe_stream "$@"
|
||||||
|
;;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Redirect back to terminal
|
||||||
|
#
|
||||||
|
|
||||||
|
"--close-pipe"|"-c")
|
||||||
|
shift
|
||||||
|
close_pipe_stream "$@"
|
||||||
|
;;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Show usage info if options are invalid
|
||||||
|
#
|
||||||
|
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
@ -326,6 +326,9 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Init pipe stream (output to display by default) */
|
||||||
|
term->pipe_stream = NULL;
|
||||||
|
|
||||||
/* Init terminal lock */
|
/* Init terminal lock */
|
||||||
pthread_mutex_init(&(term->lock), NULL);
|
pthread_mutex_init(&(term->lock), NULL);
|
||||||
|
|
||||||
@ -369,6 +372,9 @@ guac_terminal* guac_terminal_create(guac_client* client,
|
|||||||
|
|
||||||
void guac_terminal_free(guac_terminal* term) {
|
void guac_terminal_free(guac_terminal* term) {
|
||||||
|
|
||||||
|
/* Close and flush any open pipe stream */
|
||||||
|
guac_terminal_pipe_stream_close(term);
|
||||||
|
|
||||||
/* Close terminal output pipe */
|
/* Close terminal output pipe */
|
||||||
close(term->stdout_pipe_fd[1]);
|
close(term->stdout_pipe_fd[1]);
|
||||||
close(term->stdout_pipe_fd[0]);
|
close(term->stdout_pipe_fd[0]);
|
||||||
@ -1728,3 +1734,80 @@ int guac_terminal_next_tab(guac_terminal* term, int column) {
|
|||||||
return tabstop;
|
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.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -149,6 +149,26 @@ struct guac_terminal {
|
|||||||
*/
|
*/
|
||||||
int stdin_pipe_fd[2];
|
int stdin_pipe_fd[2];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The currently-open pipe stream to which all terminal output should be
|
||||||
|
* written, if any. If no pipe stream is open, terminal output will be
|
||||||
|
* written to the terminal display, and this value will be NULL.
|
||||||
|
*/
|
||||||
|
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.
|
* Graphical representation of the current scroll state.
|
||||||
*/
|
*/
|
||||||
@ -656,5 +676,58 @@ void guac_terminal_clear_tabs(guac_terminal* term);
|
|||||||
*/
|
*/
|
||||||
int guac_terminal_next_tab(guac_terminal* term, int column);
|
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
|
#endif
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
#include <guacamole/protocol.h>
|
||||||
|
#include <guacamole/socket.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -57,6 +59,12 @@ int guac_terminal_echo(guac_terminal* term, unsigned char c) {
|
|||||||
|
|
||||||
const int* char_mapping = term->char_mapping[term->active_char_set];
|
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 using non-Unicode mapping, just map straight bytes */
|
||||||
if (char_mapping != NULL) {
|
if (char_mapping != NULL) {
|
||||||
codepoint = c;
|
codepoint = c;
|
||||||
@ -936,6 +944,52 @@ int guac_terminal_download(guac_terminal* term, unsigned char c) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int guac_terminal_open_pipe_stream(guac_terminal* term, unsigned char c) {
|
||||||
|
|
||||||
|
static char stream_name[2048];
|
||||||
|
static int length = 0;
|
||||||
|
|
||||||
|
/* Open pipe on ECMA-48 ST (String Terminator) */
|
||||||
|
if (c == 0x9C || c == 0x5C || c == 0x07) {
|
||||||
|
|
||||||
|
/* End stream name string */
|
||||||
|
stream_name[length++] = '\0';
|
||||||
|
length = 0;
|
||||||
|
|
||||||
|
/* Open new pipe stream */
|
||||||
|
guac_terminal_pipe_stream_open(term, stream_name);
|
||||||
|
|
||||||
|
/* Return to echo mode */
|
||||||
|
term->char_handler = guac_terminal_echo;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, store character within stream name */
|
||||||
|
else if (length < sizeof(stream_name)-1)
|
||||||
|
stream_name[length++] = c;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guac_terminal_close_pipe_stream(guac_terminal* term, unsigned char c) {
|
||||||
|
|
||||||
|
/* Handle closure on ECMA-48 ST (String Terminator) */
|
||||||
|
if (c == 0x9C || c == 0x5C || c == 0x07) {
|
||||||
|
|
||||||
|
/* Close any existing pipe */
|
||||||
|
guac_terminal_pipe_stream_close(term);
|
||||||
|
|
||||||
|
/* Return to echo mode */
|
||||||
|
term->char_handler = guac_terminal_echo;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore all other characters */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
int guac_terminal_osc(guac_terminal* term, unsigned char c) {
|
int guac_terminal_osc(guac_terminal* term, unsigned char c) {
|
||||||
|
|
||||||
static int operation = 0;
|
static int operation = 0;
|
||||||
@ -955,6 +1009,14 @@ int guac_terminal_osc(guac_terminal* term, unsigned char c) {
|
|||||||
else if (operation == 482201)
|
else if (operation == 482201)
|
||||||
term->char_handler = guac_terminal_set_directory;
|
term->char_handler = guac_terminal_set_directory;
|
||||||
|
|
||||||
|
/* Open and redirect output to pipe stream OSC */
|
||||||
|
else if (operation == 482202)
|
||||||
|
term->char_handler = guac_terminal_open_pipe_stream;
|
||||||
|
|
||||||
|
/* Close pipe stream OSC */
|
||||||
|
else if (operation == 482203)
|
||||||
|
term->char_handler = guac_terminal_close_pipe_stream;
|
||||||
|
|
||||||
/* Reset parameter for next OSC */
|
/* Reset parameter for next OSC */
|
||||||
operation = 0;
|
operation = 0;
|
||||||
|
|
||||||
|
@ -28,14 +28,140 @@
|
|||||||
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default mode of the terminal. This character handler simply echoes
|
||||||
|
* received characters to the terminal display, entering other terminal modes
|
||||||
|
* if control characters are received.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_echo(guac_terminal* term, unsigned char c);
|
int guac_terminal_echo(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles any characters which follow an ANSI ESC (0x1B) character.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_escape(guac_terminal* term, unsigned char c);
|
int guac_terminal_escape(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the G0 character mapping from the provided character mapping
|
||||||
|
* specifier (such as B, 0, U, or K).
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_g0_charset(guac_terminal* term, unsigned char c);
|
int guac_terminal_g0_charset(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the G1 character mapping from the provided character mapping
|
||||||
|
* specifier (such as B, 0, U, or K).
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_g1_charset(guac_terminal* term, unsigned char c);
|
int guac_terminal_g1_charset(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles characters within a CSI sequence. CSI sequences are most often
|
||||||
|
* introduced with "ESC [".
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_csi(guac_terminal* term, unsigned char c);
|
int guac_terminal_csi(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the remainder of the download initiation OSC specific to the
|
||||||
|
* Guacamole terminal emulator. A download will be initiated for the specified
|
||||||
|
* file once the OSC sequence is complete.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_download(guac_terminal* term, unsigned char c);
|
int guac_terminal_download(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the remainder of the set directory OSC specific to the Guacamole
|
||||||
|
* terminal emulator. The upload directory will be set to the specified path
|
||||||
|
* once the OSC sequence is complete.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_set_directory(guac_terminal* term, unsigned char c);
|
int guac_terminal_set_directory(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the remainder of the open pipe OSC specific to the
|
||||||
|
* Guacamole terminal emulator. Terminal output will be redirected to a new
|
||||||
|
* named pipe having the given name once the OSC sequence is complete.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
|
int guac_terminal_open_pipe_stream(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the remainder of the close pipe OSC specific to the Guacamole
|
||||||
|
* terminal emulator. Terminal output will be redirected back to the terminal
|
||||||
|
* display and any open named pipe will be closed once the OSC sequence is
|
||||||
|
* complete.
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
|
int guac_terminal_close_pipe_stream(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the remaining characters of an Operating System Code (OSC) sequence,
|
||||||
|
* typically initiated with "ESC ]".
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_osc(guac_terminal* term, unsigned char c);
|
int guac_terminal_osc(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles terminal control function sequences initiated with "ESC #".
|
||||||
|
*
|
||||||
|
* @param term
|
||||||
|
* The terminal that received the given character of data.
|
||||||
|
*
|
||||||
|
* @param c
|
||||||
|
* The character that was received by the given terminal.
|
||||||
|
*/
|
||||||
int guac_terminal_ctrl_func(guac_terminal* term, unsigned char c);
|
int guac_terminal_ctrl_func(guac_terminal* term, unsigned char c);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user