summaryrefslogtreecommitdiffstats
path: root/lib/keystroke.c
diff options
context:
space:
mode:
authorChris Hall <chris.hall@highwayman.com>2011-07-21 19:53:02 +0100
committerChris Hall <chris.hall@highwayman.com>2011-07-21 19:53:02 +0100
commit56da2a1c9b6361e302b7a39fe2740561a9012d88 (patch)
tree6b6543532133a0c618d0f4ec70a87cf3f96caf30 /lib/keystroke.c
parente535bc959729262480a9702e71334002edee3f8c (diff)
downloadquagga-56da2a1c9b6361e302b7a39fe2740561a9012d88.tar.bz2
quagga-56da2a1c9b6361e302b7a39fe2740561a9012d88.tar.xz
Update pipework and improve memory reporting.
Improve error handling for all new pipework inputs and outputs. Change behaviour of ^C from VTY Terminal, so that will interrupt output and terminate all running pipes -- including running shell commands. In pipe commands, recognise "~/..." and "~user/..." home directory forms. Changed "~/" to mean the usual home for the current user. "~~/" now means the configuration file directory. Introduced "shdir DIR" command to show what is (currently) what. Changed "<|" so that if the command has a path, it is expanded using Quagga's rules (including "~~/" and "~./") and the "here" directory is set to that path. Fixed collection of stderr output from all pipes so that is separate from stdout output, and is always sent to the base output (eg VTY Terminal). Increase amount of information about the heap that "show mem" shows -- particularly if the "memory_tracker" is enabled. Tested and applied resulting fixes.
Diffstat (limited to 'lib/keystroke.c')
-rw-r--r--lib/keystroke.c1093
1 files changed, 562 insertions, 531 deletions
diff --git a/lib/keystroke.c b/lib/keystroke.c
index 2b5d8128..e4dc2540 100644
--- a/lib/keystroke.c
+++ b/lib/keystroke.c
@@ -250,15 +250,17 @@
enum stream_state
{
- kst_null, /* nothing special (but see iac) */
+ kst_between, /* between keystrokes */
kst_char, /* collecting a multi-byte character */
kst_cr, /* collecting '\r''\0' or '\r''\n' */
kst_esc, /* collecting an ESC sequence */
kst_csi, /* collecting an ESC '[' or CSI sequence */
+ kst_iac, /* seen an IAC -- previous state pushed. */
kst_iac_option, /* waiting for option (just seen IAC X) */
kst_iac_sub, /* waiting for IAC SE */
+ kst_iac_sub_iac, /* seen an IAC in iac_sub state */
} ;
typedef enum stream_state stream_state_t ;
@@ -270,26 +272,37 @@ struct keystroke_state
} ;
typedef struct keystroke_state keystroke_state_t ;
+typedef enum
+{
+ steal_none = 0, /* not stealing */
+ steal_next, /* steal next complete kestroke */
+ steal_this, /* steal this keystroke when complete */
+ steal_done /* have a stolen keystroke in hand */
+} steal_state_t ;
+
+enum { max_cntrl = 0x1F } ;
+
struct keystroke_stream
{
vio_fifo fifo ; /* the keystrokes */
- keystroke_callback* iac_callback ;
- void* iac_callback_context ;
+ keystroke_callback* callback ;
+ void* callback_context ;
uint8_t CSI ; /* CSI character value (if any) */
bool eof_met ; /* nothing more to come */
bool timed_out ; /* eof because timed out */
- bool steal_this ; /* steal current keystroke when complete */
-
- bool iac ; /* last character was an IAC */
+ steal_state_t steal ; /* whether/how/if stealing */
+ keystroke_t stolen ; /* what has been stolen */
keystroke_state_t in ; /* current keystroke being collected */
keystroke_state_t pushed_in ;
/* keystroke interrupted by IAC */
+
+ bool interrupt[max_cntrl + 1] ;
} ;
/* Buffering of keystrokes */
@@ -299,28 +312,26 @@ enum { keystroke_buffer_len = 2000 } ; /* should be plenty ! */
/*------------------------------------------------------------------------------
* Prototypes
*/
-static void keystroke_in_push(keystroke_stream stream, uint8_t u) ;
+static void keystroke_in_push(keystroke_stream stream) ;
static void keystroke_in_pop(keystroke_stream stream) ;
-inline static bool keystroke_set_null(keystroke_stream stream,
- keystroke stroke) ;
-inline static uint8_t keystroke_get_byte(keystroke_stream stream) ;
+inline static void keystroke_set_null(keystroke stroke,
+ keystroke_stream stream) ;
+inline static void keystroke_set_char(keystroke stroke, uint8_t u) ;
+inline static void keystroke_set_esc(keystroke stroke, keystroke_type_t type,
+ keystroke_stream stream) ;
+
+
inline static void keystroke_add_raw(keystroke_stream stream, uint8_t u) ;
-static void keystroke_put_char(keystroke_stream stream, uint32_t u) ;
-inline static void keystroke_put_esc(keystroke_stream stream, uint8_t u,
- int len) ;
-inline static void keystroke_put_csi(keystroke_stream stream, uint8_t u) ;
-static void keystroke_put_iac_one(keystroke_stream stream, uint8_t u);
-static void keystroke_put_iac_long(keystroke_stream stream, bool broken) ;
-static void keystroke_clear_iac(keystroke_stream stream) ;
-static void keystroke_steal_char(keystroke steal, keystroke_stream stream,
- uint8_t u) ;
-static void keystroke_steal_esc(keystroke steal, keystroke_stream stream,
- uint8_t u) ;
-static void keystroke_steal_csi(keystroke steal, keystroke_stream stream,
- uint8_t u) ;
-static void keystroke_put(keystroke_stream stream, keystroke_compound_t type,
- bool broken, uint8_t* bytes, int len) ;
+
+static void keystroke_put_char(keystroke_stream stream, uint8_t u) ;
+static void keystroke_put_esc(keystroke_stream stream, uint8_t u,
+ keystroke_flags_t flags) ;
+static void keystroke_put_iac(keystroke_stream stream, keystroke_flags_t flags);
+
+static void keystroke_put(keystroke_stream stream,
+ keystroke_type_t type, keystroke_flags_t flags,
+ uint8_t* bytes, uint len) ;
/*==============================================================================
* Creating and freeing keystroke streams and keystroke stream buffers.
@@ -331,20 +342,23 @@ static void keystroke_put(keystroke_stream stream, keystroke_compound_t type,
*
* Can set CSI character value. '\0' => none. (As does '\x1B' !)
*
- * The callback function is called when an IAC sequence is seen, the callback
- * is:
+ * Can set any control character (other than '\0', '\r', '\n', ESC or CSI)
+ * to be an "interrupt" character.
+ *
+ * The callback function is called when an IAC sequence or an "interrupt"
+ * control character is seen. The callback is:
*
* bool callback(void* context, keystroke stroke)
*
* see: #define keystroke_iac_callback_args
* and: typedef for keystroke_callback
*
- * The callback must return true iff the IAC sequence has been dealt with, and
+ * The callback must return true iff the keystroke has been dealt with, and
* should NOT be stored for later processing.
*/
extern keystroke_stream
-keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
- void* iac_callback_context)
+keystroke_stream_new(uint8_t csi_char, const char* interrupts,
+ keystroke_callback* callback, void* callback_context)
{
keystroke_stream stream ;
@@ -352,29 +366,51 @@ keystroke_stream_new(uint8_t csi_char, keystroke_callback* iac_callback,
/* Zeroising the structure sets:
*
- * iac_callback = NULL -- none
- * iac_callback_context = NULL -- none
+ * fifo = NULL -- see below
+ *
+ * callback = NULL -- see below
+ * callback_context = NULL -- see below
+ *
+ * CSI = '\0' -- see below
*
* eof_met = false -- no EOF yet
* timed_out = false -- not timed out yet
- * steal = false -- no stealing set
- * iac = false -- last character was not an IAC
*
- * in.state = kst_null
+ * steal = steal_none -- no stealing going on
+ * stolen = all zeros not significant until steal_done
+ *
+ * in.state = kst_between
* in.len = 0 -- nothing in the buffer
*
* pushed_in.state ) ditto
* pushed_in.len )
+ *
+ * interrupt = all false
*/
- confirm(kst_null == 0) ;
+ confirm(kst_between == 0) ;
+ confirm(steal_none == 0) ;
- stream->iac_callback = iac_callback ;
- stream->iac_callback_context = iac_callback_context ;
+ stream->callback = callback ;
+ stream->callback_context = callback_context ;
stream->fifo = vio_fifo_new(keystroke_buffer_len) ;
stream->CSI = (csi_char != '\0') ? csi_char : 0x1B ;
+ if (interrupts != NULL)
+ {
+ while (*interrupts != '\0')
+ {
+ if ((*interrupts > max_cntrl) || (*interrupts == '\r')
+ || (*interrupts == '\n')
+ || (*interrupts == '\x1B')
+ || (*interrupts == stream->CSI))
+ zabort("invalid 'interrupt' character") ;
+
+ stream->interrupt[(uint8_t)*interrupts++] = true ;
+ } ;
+ } ;
+
return stream ;
} ;
@@ -403,6 +439,7 @@ keystroke_stream_free(keystroke_stream stream)
/*------------------------------------------------------------------------------
* See if given keystroke stream is empty
*
+ * case kst_iac_option:
* May or may not be at "EOF", see below.
*
* Returns: true <=> is empty
@@ -418,14 +455,12 @@ keystroke_stream_empty(keystroke_stream stream)
*
* * keystroke stream is empty
*
- * * there is no partial keystroke in construction
- *
* * EOF has been signalled by a suitable call of keystroke_input().
*
- * Returns: true <=> is at EOF
+ * Returns: true <=> is at EOF (or is NULL !)
*/
extern bool
-keystroke_stream_eof(keystroke_stream stream)
+keystroke_stream_at_eof(keystroke_stream stream)
{
/* Note that when EOF is signalled, any partial keystroke in construction
* is converted to a broken keystroke and placed in the stream.
@@ -435,6 +470,21 @@ keystroke_stream_eof(keystroke_stream stream)
} ;
/*------------------------------------------------------------------------------
+ * See if given keystroke stream has seen "EOF" yet, that is:
+ *
+ * * EOF has been signalled by a suitable call of keystroke_input().
+ *
+ * NB: the keystroke stream may not be empty.
+ *
+ * Returns: true <=> is at EOF (or is NULL !)
+ */
+extern bool
+keystroke_stream_met_eof(keystroke_stream stream)
+{
+ return (stream == NULL) || stream->eof_met ;
+} ;
+
+/*------------------------------------------------------------------------------
* Set keystroke stream to "EOF", that is:
*
* * discard contents of the stream, including any partial keystroke.
@@ -449,42 +499,184 @@ keystroke_stream_set_eof(keystroke_stream stream, bool timed_out)
stream->eof_met = true ; /* essential information */
stream->timed_out = timed_out ; /* variant of eof */
- stream->steal_this = false ; /* keep tidy */
- stream->iac = false ;
- stream->in.state = kst_null ;
- stream->pushed_in.state = kst_null ;
+ stream->steal = steal_none ; /* keep tidy */
+ stream->in.state = kst_between ;
+ stream->pushed_in.state = kst_between ;
} ;
/*==============================================================================
- * Input raw bytes to given keyboard stream.
- *
- * To steal the next keystroke, pass 'steal' = address of a keystroke structure.
- * Otherwise, pass NULL.
- *
- * Note: when trying to steal, will complete any partial keystroke before
- * stealing the next one. May exit from here:
- *
- * a. without having completed the partial keystroke.
+ * Getting and stealing keystrokes.
+ */
+
+/*------------------------------------------------------------------------------
+ * Get next keystroke from keystroke stream
*
- * b. without having completed the keystroke to be stolen.
+ * Returns: true => have a stroke type != ks_null
+ * false => stroke type is ks_null (may be EOF).
+ */
+extern bool
+keystroke_get(keystroke_stream stream, keystroke stroke)
+{
+ int b ;
+
+ /* Get first byte and deal with FIFO empty response */
+ b = vio_fifo_get_byte(stream->fifo) ;
+
+ if (b < 0)
+ {
+ keystroke_set_null(stroke, stream) ;
+ return false ;
+ } ;
+
+ /* Fetch first byte and deal with the simple character case */
+ if ((b & kf_compound) == 0) /* Simple character ? */
+ {
+ keystroke_set_char(stroke, b) ;
+ return true ;
+ } ;
+
+ /* Sex the compound keystroke */
+
+ stroke->type = b & kf_type_mask ;
+ stroke->value = 0 ;
+ stroke->flags = b & kf_flag_mask ;
+ stroke->len = vio_fifo_get_byte(stream->fifo) ; /* -1 <=> end */
+
+ /* Fetch what we need to the stroke buffer */
+ if (stroke->len > 0)
+ {
+ uint get ;
+
+ assert(stroke->len <= keystroke_max_len) ;
+
+ get = vio_fifo_get_bytes(stream->fifo, stroke->buf, stroke->len) ;
+
+ assert(get == stroke->len) ;
+ }
+ else
+ assert(stroke->len == 0) ;
+
+ /* Complete the process, depending on the type */
+ switch (stroke->type)
+ {
+ case ks_null:
+ zabort("ks_null found in FIFO") ;
+
+ case ks_char:
+ /* If character is well formed, set its value */
+ if (stroke->flags == 0)
+ {
+ uint i ;
+ assert((stroke->len > 0) && (stroke->len <= 4)) ;
+ for (i = 0 ; i < stroke->len ; ++i)
+ stroke->value = (stroke->value << 8) + stroke->buf[i] ;
+
+ /* NB: to do UTF-8 would need to create UTF form here */
+
+ } ;
+ break ;
+
+ case ks_esc:
+ /* If have ESC X, set value = X */
+ if (stroke->len == 1)
+ stroke->value = stroke->buf[0] ;
+ else
+ assert(stroke->len == 0) ;
+ break ;
+
+ case ks_csi:
+ /* If have the final X, set value = X */
+ /* Null terminate the parameters */
+ if (stroke->len != 0)
+ {
+ --stroke->len ;
+ stroke->value = stroke->buf[stroke->len] ;
+ } ;
+ stroke->buf[stroke->len] = '\0' ;
+ break ;
+
+ case ks_iac:
+ /* If have the command byte after IAC, set value */
+ if (stroke->len > 0)
+ stroke->value = stroke->buf[0] ;
+ break ;
+
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Steal keystroke from keystroke stream
*
- * State (b) is remembered by the keystroke_stream.
+ * The first time this is called, sets the stream into "steal_next" state. As
+ * keystroke input is read -- see keystroke_input() -- the next keystroke to
+ * arrive will be stolen. The first time that this is called after a keystroke
+ * has been stolen, the stolen keystroke will be returned, and the stealing
+ * state reset.
*
- * Caller may have to call several times with steal != NULL to get a
- * keystroke.
+ * Returns: true => have a stolen keystroke type (may be EOF)
+ * false => nothing yet
+ */
+extern bool
+keystroke_steal(keystroke_stream stream, keystroke stroke)
+{
+ /* At EOF we immediately "steal" an eof or timed out ks_null.
+ */
+ if (stream->eof_met)
+ {
+ keystroke_set_null(stroke, stream) ;
+ return true ;
+ } ;
+
+ /* If have a stolen keystroke in hand -- return that and clear
+ * stealing state.
+ */
+ if (stream->steal == steal_done)
+ {
+ *stroke = *stream->stolen ;
+ stream->steal = steal_none ;
+ return true ;
+ } ;
+
+ /* Otherwise, if not already stealing, set stealing state.
+ *
+ * In any event, do not have a stolen keystroke, yet.
+ */
+ if (stream->steal == steal_none)
+ stream->steal = steal_next ;
+
+ return false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Clear any keystroke stealing state.
*
- * If steal != NULL the keystroke will be set to the stolen keystroke. That
- * will be type ks_null if nothing was available, and may be knull_eof.
+ * If keystroke has been stolen, it will be lost.
+ */
+extern void
+keystroke_steal_clear(keystroke_stream stream)
+{
+ stream->steal = steal_none ;
+} ;
+
+/*==============================================================================
+ * Reading of bytes into keystroke stream.
+ */
+
+/*------------------------------------------------------------------------------
+ * Input raw bytes to given keystroke stream.
*
* Note that never steals broken or truncated keystrokes, or IAC.
*
- * Passing len == 0 and ptr == NULL signals EOF to the keystroke_stream.
+ * Passing len < 0 signals EOF to the keystroke_stream.
*
* Updates the stream and returns updated raw
*/
extern void
-keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
- keystroke steal)
+keystroke_input(keystroke_stream stream, uint8_t* ptr, int len)
{
uint8_t* end ;
@@ -493,14 +685,12 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
* Any partial keystroke is converted into a broken keystroke and placed
* at the end of the stream.
*
- * Note that this occurs before any attempt to steal a keystroke -- so can
- * never steal a broken keystroke.
+ * Note that neither steals no calls back broken keystrokes.
*/
- if ((len == 0) && (ptr == NULL))
+ if (len < 0)
{
stream->eof_met = true ;
stream->timed_out = false ;
- stream->steal_this = false ;
/* Loop to deal with any pending IAC and partial keystroke.
*
@@ -510,85 +700,49 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
* A partial IAC sequence may have pushed a partial real keystroke
* sequence -- so loop until have dealt with that.
*/
- do
+ while (stream->in.state != kst_between)
{
switch (stream->in.state)
{
- case kst_null: /* not expecting anything, unless iac */
- keystroke_clear_iac(stream) ;
- break ;
-
- case kst_cr: /* expecting something after CR */
- keystroke_clear_iac(stream) ;
-
- stream->in.len = 0 ;
+ case kst_cr: /* expecting something after CR */
keystroke_add_raw(stream, '\r') ;
- keystroke_put(stream, ks_char, true,
+ keystroke_put(stream, ks_char, kf_broken,
stream->in.raw, stream->in.len) ;
break ;
- case kst_esc: /* expecting rest of escape */
- keystroke_clear_iac(stream) ;
-
- keystroke_put_esc(stream, '\0', 0) ;
- break ;
-
+ case kst_esc: /* expecting rest of escape */
case kst_csi:
- keystroke_clear_iac(stream) ;
-
- keystroke_put_csi(stream, '\0') ;
+ keystroke_put_esc(stream, '\0', kf_broken) ;
break ;
- case kst_iac_option: /* expecting rest of IAC */
- assert(!stream->iac) ;
- /* fall through */
+ /* For incomplete IAC sequences, insert a broken sequence.
+ *
+ * Note that where have just seen an IAC itself, we insert that
+ * now at the end of the sequence.
+ */
+ case kst_iac: /* expecting something after IAC */
+ case kst_iac_sub_iac:
+ keystroke_add_raw(stream, tn_IAC) ;
+ fall_through ;
+
+ case kst_iac_option: /* expecting rest of IAC XX */
case kst_iac_sub:
- keystroke_put_iac_long(stream, true) ;
+ keystroke_put_iac(stream, kf_broken) ;
- /* For kst_iac_sub, an incomplete IAC could be anything, so
- * don't include in the broken IAC, but don't lose it
- * either.
- */
- keystroke_clear_iac(stream) ;
break ;
- case kst_char: /* TBD */
+ case kst_between: /* Cannot be this */
+ case kst_char: /* TBD */
zabort("impossible keystroke stream state") ;
default:
zabort("unknown keystroke stream state") ;
} ;
- assert(!stream->iac) ; /* must have dealt with this */
-
- keystroke_in_pop(stream) ; /* pops kst_null, when all done */
-
- } while (stream->in.state != kst_null) ;
+ keystroke_in_pop(stream) ; /* pops kst_between, when all done */
+ } ;
} ;
- /* Update the stealing state
- *
- * steal != NULL => want to steal a keystroke
- *
- * If do not wish to steal now, must clear any
- * remembered steal_this state.
- *
- * stream->steal_this => steal the next keystroke to complete.
- *
- * If want to steal a keystroke, this is set if
- * currently "between" keystrokes, or later when
- * reach that condition.
- *
- * Once set, this is remembered across calls to
- * keystroke_input(), while still wish to steal.
- */
- if (steal == NULL)
- stream->steal_this = false; /* not now required */
- else
- stream->steal_this = (stream->in.state == kst_null) ;
- /* want to and can can steal the next
- keystroke that completes */
-
/* Once EOF has been signalled, do not expect to receive any further input.
*
* However, keystroke_stream_set_eof() can set the stream artificially at
@@ -612,192 +766,219 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
*
* stream->iac means that the last thing seen was an IAC.
*
- * First IAC sets the flag. On the next byte:
- *
- * * if is IAC, clears stream->iac, and lets the escaped IAC value
- * through for further processing -- NOT in IAC state.
- *
- * * if is not IAC, will be let through for further processing, in
- * IAC state.
+ * First IAC changes state as required to process the next byte in
+ * suitable (sub) state.
*/
- if ((u == tn_IAC) && (stream->in.state != kst_iac_option))
+ if (u == tn_IAC)
{
- if (stream->iac)
- stream->iac = false ; /* IAC IAC => single IAC byte value */
- else
- {
- stream->iac = true ; /* seen an IAC */
- continue ; /* wait for next character */
- } ;
- } ;
-
- /* If stream->iac, then need to worry about IAC XX
- *
- * Note that IAC sequences are entirely invisible to the general
- * stream of keystrokes. So... IAC sequences may appear in the middle
- * of multi-byte general keystroke objects.
- *
- * If this is not a simple 2 byte IAC, then must put whatever was
- * collecting to one side, and deal with the IAC.
- *
- * Note: not interested in stealing an IAC object.
- */
- if (stream->iac)
- {
- stream->iac = false ; /* expect will eat the IAC XX */
-
switch (stream->in.state)
{
- case kst_null:
+ /* An IAC may interrupt any ordinary keystroke sequence.
+ *
+ * Switch to kst_iac state and continue by fetching the next byte,
+ * if any.
+ */
+ case kst_between:
case kst_cr:
case kst_esc:
case kst_csi:
- if (u < tn_SB)
- /* This is a simple IAC XX, one byte IAC */
- keystroke_put_iac_one(stream, u) ;
- else
- /* This is a multi-byte IAC, so push whatever real
- * keystroke sequence is currently on preparation, and
- * set into kst_iac_option state.
- */
- keystroke_in_push(stream, u) ;
- break ;
+ keystroke_in_push(stream) ; /* enter kst_iac state */
+ continue ; /* loop back */
- case kst_iac_sub:
- assert(stream->in.raw[0] == tn_SB) ;
+ /* An IAC in kst_iac state is an escaped IAC value, so pop the
+ * state and proceed with the tn_IAC value.
+ */
+ case kst_iac:
+ keystroke_in_pop(stream) ;
+ break ;
- if (u != tn_SE)
- {
- --ptr ; /* put back the XX */
- stream->iac = true ; /* put back the IAC */
- } ;
+ /* An IAC in kst_iac_option state is actually an EXOPL, so
+ * proceed with the EXOPL value.
+ */
+ case kst_iac_option:
+ confirm((uint)tn_IAC == (uint)to_EXOPL) ;
+ break ;
- keystroke_put_iac_long(stream, (u != tn_SE)) ;
- keystroke_in_pop(stream) ;
+ /* An IAC in kst_iac_sub state may be IAC IAC or IAC SE below.
+ *
+ * Switch to kst_iac_sub_iac state and continue by fetching the
+ * next byte, if any.
+ */
+ case kst_iac_sub:
+ stream->in.state = kst_iac_sub_iac ;
+ continue ; /* loop back */
+
+ /* An IAC in kst_iac state is an escaped IAC value, so return
+ * to kst_iac_sub state and proceed with the tn_IAC value.
+ */
+ case kst_iac_sub_iac:
+ stream->in.state = kst_iac_sub ;
break ;
case kst_char: /* TBD */
- case kst_iac_option:
zabort("impossible keystroke stream state") ;
default:
zabort("unknown keystroke stream state") ;
} ;
-
- continue ;
} ;
- /* No IAC complications... proceed per current state */
+ /* Dealt with an IAC, proceed with byte per current state.
+ */
switch (stream->in.state)
{
- case kst_null: /* Expecting anything */
- stream->steal_this = (steal != NULL) ;
-
- if (u == '\r')
- stream->in.state = kst_cr ;
- else if (u == 0x1B)
- stream->in.state = kst_esc ;
- else if (u == stream->CSI) /* NB: CSI == 0x1B => no CSI */
+ /* In kst_between state we are between keystrokes, and may get anything.
+ *
+ * If waiting to steal the next keystroke, this is the (start of the)
+ * next keystroke.
+ */
+ case kst_between:
+ if (stream->steal == steal_next)
+ stream->steal = steal_this ;
+
+ if (u <= max_cntrl)
{
- stream->in.len = 0 ;
- stream->in.state = kst_csi ;
- }
- else
- {
- /* Won't steal NUL */
- if (!stream->steal_this || (u == '\0'))
- keystroke_put_char(stream, u) ;
- else
+ stream->in.len = 0 ; /* make sure */
+
+ if (u == '\0') /* discard NUL */
+ break ;
+
+ if (u == '\r')
+ {
+ stream->in.state = kst_cr ;
+ break ;
+ } ;
+
+ if (u == 0x1B)
+ {
+ stream->in.state = kst_esc ;
+ break ;
+ } ;
+
+ if (u == stream->CSI) /* NB: CSI == 0x1B => no CSI */
{
- keystroke_steal_char(steal, stream, u) ;
- stream->steal_this = false ;
- steal = NULL ;
+ stream->in.state = kst_csi ;
+ break ;
} ;
- stream->in.state = kst_null ;
+ if (stream->interrupt[u] && (stream->callback != NULL))
+ {
+ keystroke_t stroke ;
+
+ keystroke_set_char(stroke, u) ;
+
+ if ((*stream->callback)(stream->callback_context, stroke))
+ break ;
+ } ;
} ;
+
+ /* Simple single byte character: put or steal -- no state change.
+ */
+ keystroke_put_char(stream, u) ;
+
break ;
- case kst_char: /* TBD */
+ /* Multi-byte character handling -- TBD
+ */
+ case kst_char:
zabort("impossible keystroke stream state") ;
+ /* Byte following a '\r'
+ */
case kst_cr: /* expecting something after CR */
- if ((u != '\n') && (u != '\r'))
- {
- if (u != '\0')
- {
- --ptr ; /* put back the duff XX */
- stream->iac = (u == tn_IAC) ;
- /* re=escape if is IAC */
- } ;
- u = '\r' ;
- } ;
+ stream->in.state = kst_between ;
- if (!stream->steal_this)
- keystroke_put_char(stream, u) ;
- else
+ switch (u)
{
- keystroke_steal_char(steal, stream, u) ;
- stream->steal_this = false ;
- steal = NULL ;
+ case '\0':
+ u = '\r' ; /* treat '\r''\0' as '\r' (as expected) */
+
+ case '\r': /* treat '\r''\r' as '\r' (why not) */
+ case '\n': /* treat '\r''\n' as '\n' */
+ break ;
+
+ default:
+ --ptr ; /* put back the duff XX */
+ if (u == tn_IAC)
+ keystroke_in_push(stream) ;
+ /* re-enter kst_iac state */
+ u = '\r' ; /* treat '\r'XX as '\r' */
} ;
- stream->in.state = kst_null ;
+ keystroke_put_char(stream, u) ; /* put or steal */
+
break ;
- case kst_esc: /* Expecting XX after ESC */
+ /* Byte following ESC
+ */
+ case kst_esc:
if (u == '[')
- {
- stream->in.len = 0 ;
- stream->in.state = kst_csi ;
- }
+ stream->in.state = kst_csi ;
else
{
- if (!stream->steal_this)
- keystroke_put_esc(stream, u, 1) ;
- else
- {
- keystroke_steal_esc(steal, stream, u) ;
- stream->steal_this = false ;
- steal = NULL ;
- } ;
-
- stream->in.state = kst_null ;
+ keystroke_put_esc(stream, u, kf_null) ;
+ stream->in.state = kst_between ;
} ;
break ;
- case kst_csi: /* Expecting ... after ESC [ or CSI */
+ /* Byte(s) following CSI or ESC '['
+ */
+ case kst_csi:
if ((u >= 0x20) && (u <= 0x3F))
keystroke_add_raw(stream, u) ;
else
{
- bool ok ;
+ keystroke_flags_t flags = kf_null ;
- ok = stream->in.len < keystroke_max_len ;
- /* have room for terminator */
+ stream->in.state = kst_between ; /* done */
+
+ if (stream->in.len >= keystroke_max_len)
+ {
+ flags |= kf_truncated ;
+ stream->in.len = keystroke_max_len - 1 ;
+ } ;
if ((u < 0x40) || (u > 0x7E))
{
--ptr ; /* put back the duff XX */
- stream->iac = (u == tn_IAC) ;
- /* re=escape if is IAC */
+ if (u == tn_IAC)
+ keystroke_in_push(stream) ;
+ /* re-enter kst_iac state */
u = '\0' ;
- ok = false ; /* broken */
+ flags |= kf_broken ;
} ;
- if (!stream->steal_this || !ok)
- keystroke_put_csi(stream, u) ;
- else
- {
- keystroke_steal_csi(steal, stream, u) ;
- stream->steal_this = false ;
- steal = NULL ;
- } ;
- stream->in.state = kst_null ;
+ keystroke_put_esc(stream, u, flags) ;
} ;
break ;
- case kst_iac_option: /* Expecting <option> after IAC XX */
+ /* Byte following IAC -- where that byte is not itself IAC
+ */
+ case kst_iac: /* IAC XX -- process the XX */
+ keystroke_add_raw(stream, u) ;
+
+ if (u < tn_SB)
+ {
+ /* This is a simple IAC XX, one byte IAC
+ */
+ keystroke_put_iac(stream, kf_null) ;
+ keystroke_in_pop(stream) ;
+ }
+ else
+ {
+ /* This is a multi-byte IAC, so set into kst_iac_option state.
+ */
+ stream->in.state = kst_iac_option ;
+ } ;
+ break ;
+
+ /* Byte following IAC XX -- expecting <option>
+ *
+ * If the XX was SB -> kst_iac_sub state: IAC SB <option> .... IAC SE
+ *
+ * Otherwise, this is a 3 byte IAC: IAC XX <option>
+ */
+ case kst_iac_option:
assert(stream->in.len == 1) ;
keystroke_add_raw(stream, u) ;
@@ -805,28 +986,55 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
stream->in.state = kst_iac_sub ;
else
{
- keystroke_put_iac_long(stream, false) ;
+ keystroke_put_iac(stream, kf_null) ;
keystroke_in_pop(stream) ;
} ;
break ;
- case kst_iac_sub: /* Expecting sub stuff */
- assert(stream->in.raw[0]== tn_SB) ;
+ /* Bytes following IAC SB <option> ...
+ *
+ * This IAC is terminated by IAC SE. If an IAC is seen, the next byte
+ * is dealt with in kst_iac_sub_iac state.
+ */
+ case kst_iac_sub:
+ assert(stream->in.raw[0] == tn_SB) ;
keystroke_add_raw(stream, u) ;
break ;
+ /* in kst_iac_sub_iac state, expect:
+ *
+ * IAC -- escaped 0xFF value -- dealt with already.
+ *
+ * SE -- end of kst_iac_sub state
+ *
+ * Nothing else.
+ *
+ * So here the IAC is terminated, broken if don't have an SE, and the
+ * stack popped.
+ *
+ * Then if was broken, put back the XX to be processed in kst_iac state.
+ */
+ case kst_iac_sub_iac:
+ assert(stream->in.raw[0] == tn_SB) ;
+
+ keystroke_put_iac(stream, ((u == tn_SE) ? kf_null : kf_broken)) ;
+ keystroke_in_pop(stream) ;
+
+ if (u != tn_SE)
+ {
+ --ptr ; /* put back the XX */
+ keystroke_in_push(stream) ; /* enter kst_iac state */
+ } ;
+ break ;
+
+
default:
zabort("unknown keystroke stream state") ;
}
} ;
- assert(ptr == end) ;
- /* If did not steal a keystroke, return a ks_null -- which may be
- * a knull_eof.
- */
- if (steal != NULL)
- keystroke_set_null(stream, steal) ;
+ assert(ptr == end) ;
} ;
/*------------------------------------------------------------------------------
@@ -834,141 +1042,36 @@ keystroke_input(keystroke_stream stream, uint8_t* ptr, size_t len,
* sequences transparently.
*/
-/* Push current state and set new current state for start of IAC option
- * sequence.
+/* Push current state and set new current state to process byte after IAC.
*/
static void
-keystroke_in_push(keystroke_stream stream, uint8_t u)
+keystroke_in_push(keystroke_stream stream)
{
- assert(stream->pushed_in.state == kst_null) ;
-
- stream->pushed_in = stream->in ;
+ assert(stream->pushed_in.state == kst_between) ;
- stream->in.len = 1 ;
- stream->in.raw[0] = u ;
+ stream->pushed_in = stream->in ;
- stream->in.state = kst_iac_option ;
+ stream->in.state = kst_iac ;
+ stream->in.len = 0 ;
} ;
-/* Pop the pushed state and clear the pushed state to kst_null */
+/* Pop the pushed state and clear the pushed state to kst_between (so further
+ * pop will return that)
+ */
static void
keystroke_in_pop(keystroke_stream stream)
{
stream->in = stream->pushed_in ;
- stream->pushed_in.state = kst_null ;
+ stream->pushed_in.state = kst_between ;
stream->pushed_in.len = 0 ;
} ;
-/*==============================================================================
- * Fetch next keystroke from keystroke stream
- *
- * Returns: true => have a stroke type != ks_null
- * false => stroke type is ks_null (may be EOF).
- */
-extern bool
-keystroke_get(keystroke_stream stream, keystroke stroke)
-{
- int b ;
- uint8_t* p ;
- uint8_t* e ;
-
- /* Get first byte and deal with FIFO empty response */
- b = vio_fifo_get_byte(stream->fifo) ;
-
- if (b < 0)
- return keystroke_set_null(stream, stroke) ;
-
- /* Fetch first byte and deal with the simple character case */
- if ((b & kf_compound) == 0) /* Simple character ? */
- {
- stroke->type = ks_char ;
- stroke->value = b ;
-
- stroke->flags = 0 ;
- stroke->len = 1 ;
- stroke->buf[0] = b ;
-
- return true ;
- } ;
-
- /* Sex the compound keystroke */
-
- stroke->type = b & kf_type_mask ;
- stroke->value = 0 ;
- stroke->flags = b & kf_flag_mask ;
- stroke->len = keystroke_get_byte(stream) ;
-
- assert(stroke->len <= keystroke_max_len) ;
-
- /* Fetch what we need to the stroke buffer */
- p = stroke->buf ;
- e = p + stroke->len ;
-
- while (p < e)
- *p++ = keystroke_get_byte(stream) ;
-
- p = stroke->buf ;
-
- /* Complete the process, depending on the type */
- switch (stroke->type)
- {
- case ks_null:
- zabort("ks_null found in FIFO") ;
-
- case ks_char:
- /* If character is well formed, set its value */
- if (stroke->flags == 0)
- {
- assert((stroke->len > 0) && (stroke->len <= 4)) ;
- while (p < e)
- stroke->value = (stroke->value << 8) + *p++ ;
-
- /* NB: to do UTF-8 would need to create UTF form here */
-
- } ;
- break ;
-
- case ks_esc:
- /* If have ESC X, set value = X */
- if (stroke->len == 1)
- stroke->value = *p ;
- else
- assert(stroke->len == 0) ;
- break ;
-
- case ks_csi:
- /* If have the final X, set value = X */
- /* Null terminate the parameters */
- if (stroke->len != 0)
- {
- --e ;
- stroke->value = *e ;
- --stroke->len ;
- } ;
- *e = '\0' ;
- break ;
-
- case ks_iac:
- /* If have the command byte after IAC, set value */
- if (stroke->len > 0)
- stroke->value = *p ;
- break ;
-
- default:
- zabort("unknown keystroke type") ;
- } ;
-
- return true ;
-} ;
-
/*------------------------------------------------------------------------------
* Set given keystroke to ks_null -- and set to knull_eof if stream->eof_met
- *
- * Returns: 0
*/
-inline static bool
-keystroke_set_null(keystroke_stream stream, keystroke stroke)
+inline static void
+keystroke_set_null(keystroke stroke, keystroke_stream stream)
{
stroke->type = ks_null ;
stroke->value = stream->eof_met ? (stream->timed_out ? knull_timed_out
@@ -976,24 +1079,58 @@ keystroke_set_null(keystroke_stream stream, keystroke stroke)
: knull_not_eof ;
stroke->flags = 0 ;
stroke->len = 0 ;
+} ;
- return false ;
+/*------------------------------------------------------------------------------
+ * Set given keystroke to simple character
+ */
+inline static void
+keystroke_set_char(keystroke stroke, uint8_t u)
+{
+ stroke->type = ks_char ;
+ stroke->value = u ;
+
+ stroke->flags = 0 ;
+ stroke->len = 1 ;
+ stroke->buf[0] = u ;
} ;
/*------------------------------------------------------------------------------
- * Fetch 2nd or subsequent byte of keystroke.
- *
- * NB: it is impossible for partial keystrokes to be written, so this treats
- * buffer empty as a FATAL error.
+ * Set given keystroke to given escape
*/
-inline static uint8_t
-keystroke_get_byte(keystroke_stream stream)
+inline static void
+keystroke_set_esc(keystroke stroke, keystroke_type_t type,
+ keystroke_stream stream)
{
- int b = vio_fifo_get_byte(stream->fifo) ;
+ int len ;
- passert(b >= 0) ;
+ len = stream->in.len - 1 ; /* exclude the escape terminator */
+ assert((len >= 0) && (len < keystroke_max_len)) ;
+
+ if (len > 0)
+ memcpy(stroke->buf, stream->in.raw, len) ;
+ stroke->buf[len] = '\0' ;
- return b ;
+ stroke->type = type ;
+ stroke->value = stream->in.raw[len] ; /* last character */
+ stroke->flags = 0 ;
+ stroke->len = len ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set given keystroke to given escape
+ */
+inline static void
+keystroke_set_iac(keystroke stroke, keystroke_stream stream)
+{
+ assert((stream->in.len >= 1) && (stream->in.len <= keystroke_max_len)) ;
+
+ memcpy(stroke->buf, stream->in.raw, stream->in.len) ;
+
+ stroke->type = ks_iac ;
+ stroke->value = stream->in.raw[0] ;
+ stroke->flags = 0 ;
+ stroke->len = stream->in.len ;
} ;
/*==============================================================================
@@ -1002,6 +1139,8 @@ keystroke_get_byte(keystroke_stream stream)
/*------------------------------------------------------------------------------
* If possible, add character to the stream->in.raw[] buffer
+ *
+ * Note that the stream->in.len continues counting beyond the end of the buffer.
*/
static inline void
keystroke_add_raw(keystroke_stream stream, uint8_t u)
@@ -1013,122 +1152,66 @@ keystroke_add_raw(keystroke_stream stream, uint8_t u)
} ;
/*------------------------------------------------------------------------------
- * Store simple character value
+ * Store simple 8 bit character value, or steal it
*/
static void
-keystroke_put_char(keystroke_stream stream, uint32_t u)
+keystroke_put_char(keystroke_stream stream, uint8_t u)
{
- if (u < kf_compound)
- vio_fifo_put_byte(stream->fifo, (uint8_t)u) ;
+ if (stream->steal == steal_this)
+ {
+ keystroke_set_char(stream->stolen, u) ;
+ stream->steal = steal_done ;
+ }
else
{
- uint8_t buf[4] ;
- uint8_t* p ;
-
- p = buf + 4 ;
-
- do
- {
- *(--p) = u & 0xFF ;
- u >>= 8 ;
- }
- while (u != 0) ;
-
- keystroke_put(stream, ks_char, false, p, (buf + 4) - p) ;
+ if (u < kf_compound)
+ vio_fifo_put_byte(stream->fifo, u) ;
+ else
+ keystroke_put(stream, ks_char, kf_null, &u, 1) ;
} ;
} ;
/*------------------------------------------------------------------------------
- * Store simple ESC. Is broken if length (after ESC) == 0 !
- */
-static void
-keystroke_put_esc(keystroke_stream stream, uint8_t u, int len)
-{
- keystroke_put(stream, ks_esc, (len == 0), &u, len) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Store CSI.
- *
- * Plants the last character of the CSI in the buffer, even if has to overwrite
- * the existing last character -- the sequence is truncated, but this way at
- * least the end of the sequence is preserved.
- *
- * Is broken if u == '\0'. May also be truncated !
+ * Store simple ESC or CSI, or steal it (but won't steal if broken/truncated)
*/
static void
-keystroke_put_csi(keystroke_stream stream, uint8_t u)
+keystroke_put_esc(keystroke_stream stream, uint8_t u, keystroke_flags_t flags)
{
- int l ;
+ keystroke_type_t type ;
- l = stream->in.len++ ;
+ keystroke_add_raw(stream, u) ;
- if (l >= keystroke_max_len)
- l = keystroke_max_len - 1 ;
+ type = (stream->in.state == kst_esc) ? ks_esc : ks_csi ;
- stream->in.raw[l] = u ; /* plant terminator */
-
- keystroke_put(stream, ks_csi, (u == '\0'), stream->in.raw, stream->in.len) ;
+ if ((stream->steal == steal_this)
+ && ((flags & (kf_broken | kf_truncated)) == 0))
+ {
+ keystroke_set_esc(stream->stolen, type, stream) ;
+ stream->steal = steal_done ;
+ }
+ else
+ keystroke_put(stream, type, flags, stream->in.raw, stream->in.len) ;
} ;
/*------------------------------------------------------------------------------
* Store IAC -- if not broken, send it via any call-back.
*/
static void
-keystroke_put_iac(keystroke_stream stream, bool broken, uint8_t* bytes, int len)
+keystroke_put_iac(keystroke_stream stream, keystroke_flags_t flags)
{
bool dealt_with = false ;
- if (!broken && (stream->iac_callback != NULL))
+ if ((stream->callback != NULL) && ((flags & (kf_broken | kf_truncated)) == 0))
{
- struct keystroke stroke ;
-
- assert((len >= 1) && (bytes != NULL) && (len <= keystroke_max_len)) ;
-
- stroke.type = ks_iac ;
- stroke.value = bytes[0] ;
- stroke.flags = 0 ;
- stroke.len = len ;
+ keystroke_t stroke ;
- memcpy(&stroke.buf, bytes, len) ;
+ keystroke_set_iac(stroke, stream) ;
- dealt_with = (*stream->iac_callback)(stream->iac_callback_context,
- &stroke) ;
+ dealt_with = (*stream->callback)(stream->callback_context, stroke) ;
} ;
if (!dealt_with)
- keystroke_put(stream, ks_iac, broken, bytes, len) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Store one byte IAC.
- */
-static void
-keystroke_put_iac_one(keystroke_stream stream, uint8_t u)
-{
- keystroke_put_iac(stream, false, &u, 1) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Store long IAC. Is broken if says it is.
- */
-static void
-keystroke_put_iac_long(keystroke_stream stream, bool broken)
-{
- keystroke_put_iac(stream, broken, stream->in.raw, stream->in.len) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * If in IAC state, issue broken IAC and clear state.
- */
-static void
-keystroke_clear_iac(keystroke_stream stream)
-{
- if (stream->iac)
- {
- keystroke_put_iac(stream, true, NULL, 0) ;
- stream->iac = 0 ;
- } ;
+ keystroke_put(stream, ks_iac, flags, stream->in.raw, stream->in.len) ;
} ;
/*------------------------------------------------------------------------------
@@ -1137,74 +1220,22 @@ keystroke_clear_iac(keystroke_stream stream)
* If len == 0, bytes may be NULL
*/
static void
-keystroke_put(keystroke_stream stream, keystroke_compound_t type, bool broken,
- uint8_t* bytes, int len)
+keystroke_put(keystroke_stream stream, keystroke_type_t type,
+ keystroke_flags_t flags,
+ uint8_t* bytes, uint len)
{
if (len > keystroke_max_len)
{
len = keystroke_max_len ;
- type |= kf_truncated ;
+ flags |= kf_truncated ;
} ;
- vio_fifo_put_byte(stream->fifo,
- kf_compound | (broken ? kf_broken : 0) | type) ;
+ vio_fifo_put_byte(stream->fifo, kf_compound | flags | type) ;
vio_fifo_put_byte(stream->fifo, len) ;
if (len > 0)
vio_fifo_put_bytes(stream->fifo, (void*)bytes, len) ;
} ;
-/*------------------------------------------------------------------------------
- * Steal character value -- cannot be broken
- */
-static void
-keystroke_steal_char(keystroke steal, keystroke_stream stream, uint8_t u)
-{
- steal->type = ks_char ;
- steal->value = u ;
- steal->flags = 0 ;
- steal->len = 1 ;
- steal->buf[0] = u ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Steal simple escape -- cannot be broken
- */
-static void
-keystroke_steal_esc(keystroke steal, keystroke_stream stream, uint8_t u)
-{
- steal->type = ks_esc ;
- steal->value = u ;
- steal->flags = 0 ;
- steal->len = 1 ;
- steal->buf[0] = u ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Steal CSI escape -- cannot be broken or truncated.
- *
- * In the stream-in.raw buffer the last character is the escape terminator,
- * after the escape parameters.
- *
- * In keystroke buffer the escape parameters are '\0' terminated, and the
- * escape terminator is the keystroke value.
- */
-static void
-keystroke_steal_csi(keystroke steal, keystroke_stream stream, uint8_t u)
-{
- int len ;
-
- len = stream->in.len ; /* excludes the escape terminator */
- assert((len < keystroke_max_len) && (u >= 0x40) && (u <= 0x7E)) ;
-
- steal->type = ks_esc ;
- steal->value = u ;
- steal->flags = 0 ;
- steal->len = len ;
-
- memcpy(steal->buf, stream->in.raw, len) ;
- steal->buf[len] = '\0' ;
-} ;
-
/*==============================================================================
*/