summaryrefslogtreecommitdiffstats
path: root/lib/vty_cli.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vty_cli.c')
-rw-r--r--lib/vty_cli.c2192
1 files changed, 1121 insertions, 1071 deletions
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index 42d78349..3b82a112 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -40,6 +40,13 @@
#include "memory.h"
+/*------------------------------------------------------------------------------
+ * Essential stuff.
+ */
+#define TELNET_NEWLINE "\r\n"
+static const char* telnet_newline = TELNET_NEWLINE ;
+ const char* uty_cli_newline = TELNET_NEWLINE ;
+
/*==============================================================================
* Construct and destroy CLI object
*
@@ -49,13 +56,15 @@
static bool uty_cli_iac_callback(keystroke_iac_callback_args) ;
static void uty_cli_update_more(vty_cli cli) ;
+static void uty_cli_cancel(vty_cli cli) ;
+
/*------------------------------------------------------------------------------
* Construct and initialise a new CLI object -- never embedded.
*
* The CLI is started up in the state it is in waiting for a command to
* complete. This means that all start-up messages etc. are handled as the
- * output from an implied start command. uty_cli_start() is called when the
- * start-up messages etc. are complete.
+ * output from an implied start command. When the command loop is entered,
+ * will find its way to uty_cli_want_command().
*
* Note that the vty_io_term stuff sends out a bunch of telnet commands, which
* will be buffered in the CLI buffer. That will be output, before the
@@ -72,9 +81,10 @@ uty_cli_new(vio_vf vf)
*
* vf = NULL -- set below
*
- * hist = empty vector (embedded) -- set up when first used.
+ * hist = NULL -- set up when first used.
* hp = 0 -- hp == h_now => in the present ...
* h_now = 0 -- ... see uty_cli_hist_add()
+ * h_repeat = false -- ... see uty_cli_hist_add()
*
* width = 0 -- unknown width ) Telnet window size
* height = 0 -- unknown height )
@@ -92,46 +102,48 @@ uty_cli_new(vio_vf vf)
* drawn = false
* dirty = false
*
+ * tilde_prompt = false -- tilde prompt is not drawn
+ * tilde_enabled = false -- set below if ! multi-threaded
+ *
* prompt_len = 0 -- not drawn, in any case
* extra_len = 0 -- not drawn, in any case
*
- * echo_suppress = false
- *
* prompt_node = NULL_NODE -- so not set !
* prompt_gen = 0 -- not a generation number
- * prompt_for_node = empty qstring (embedded)
+ * prompt_for_node = NULL -- not set, yet
*
- * password_fail = 0 -- so far, so good.
- *
- * blocked = false -- see below
+ * dispatched = false -- see below
* in_progress = false -- see below
+ * blocked = false -- see below
+ * paused = false
+ *
* out_active = false
+ * flush = false
*
* more_wait = false
- * more_active = false
- *
- * out_done = false -- not that it matters
+ * more_enter = false
*
* more_enabled = false -- not in "--More--" state
*
- * node = NULL_NODE -- set in vty_cli_start()
- * to_do = cmd_do_nothing
+ * pause_timer = NULL -- set below if multi-threaded
*
+ * context = NULL -- see below
+ * context_auth = false -- set by uty_cli_want_command()
+ *
+ * parsed = NULL -- see below
+ * to_do = cmd_do_nothing
* cl = NULL qstring -- set below
+ * clo = NULL qstring -- set below
* clx = NULL qstring -- set below
+ * dispatch = all zeros -- nothing to dispatch
*
- * parsed = all zeros -- empty parsed object
- * cbuf = empty fifo (embedded)
+ * cbuf = NULL -- see below
*
- * olc = empty line control (embedded)
+ * olc = NULL -- see below
*/
- confirm(VECTOR_INIT_ALL_ZEROS) ; /* hist */
- confirm(QSTRING_INIT_ALL_ZEROS) ; /* prompt_for_node */
confirm(NULL_NODE == 0) ; /* prompt_node & node */
confirm(cmd_do_nothing == 0) ; /* to_do */
- confirm(VIO_FIFO_INIT_ALL_ZEROS) ; /* cbuf */
- confirm(VIO_LINE_CONTROL_INIT_ALL_ZEROS) ; /* olc */
- confirm(CMD_PARSED_INIT_ALL_ZEROS) ; /* parsed */
+ confirm(CMD_ACTION_ALL_ZEROS) ; /* dispatch */
cli->vf = vf ;
@@ -139,40 +151,43 @@ uty_cli_new(vio_vf vf)
cli->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, cli) ;
/* Set up cl and clx qstrings and the command line output fifo */
- cli->cl = qs_init_new(NULL, 120) ; /* reasonable line length */
- cli->clx = qs_init_new(NULL, 120) ;
+ cli->cl = qs_new(120) ; /* reasonable line length */
+ cli->cls = qs_new(120) ;
+ cli->clx = qs_new(120) ;
+
+ cli->cbuf = vio_fifo_init_new(NULL, 1000) ;
- vio_fifo_init_new(cli->cbuf, 500) ; /* just for the CLI stuff */
+ cli->olc = vio_lc_new(0 , 0, telnet_newline) ;
+
+ /* Make an empty context object and an empty parsed object */
+ cli->context = cmd_context_new() ;
+ cli->parsed = cmd_parsed_new() ;
/* Use global 'lines' setting, as default -- but 'lines_set' false. */
uty_cli_set_lines(cli, host.lines, false) ;
uty_cli_update_more(cli) ; /* and update the olc etc to suit. */
- /* Ready to be started -- out_active & more_wait are false. */
- cli->blocked = true ;
+ /* Enable "~ " prompt and pause timer if multi-threaded */
+ if (vty_nexus)
+ {
+ cli->pause_timer = qtimer_init_new(NULL, vty_cli_nexus->pile,
+ vty_term_pause_timeout, cli) ;
+ cli->tilde_enabled = vty_multi_nexus ;
+ } ;
+
+ /* Ready to be started -- paused, out_active, flush & more_wait are false.
+ *
+ * Is started by the first call of uty_cli_want_command(), which (inter alia)
+ * set the cli->context so CLI knows how to prompt etc.
+ */
+ cli->dispatched = true ;
cli->in_progress = true ;
+ cli->blocked = true ;
return cli ;
} ;
/*------------------------------------------------------------------------------
- * Start the CLI.
- *
- * The implied start "command" is complete and everything is ready to start
- * the CLI.
- *
- * Note that before releasing any pending output, calls
- *
- * Returns: write_ready -- so the first event is a write event, to flush
- * any output to date.
- */
-extern void
-uty_cli_start(vty_cli cli, node_type_t node)
-{
- uty_cli_done_command(cli, node) ; /* implied push output */
-} ;
-
-/*------------------------------------------------------------------------------
* Close CLI object, if any -- may be called more than once.
*
* Shuts down and discards anything to do with the input.
@@ -180,7 +195,7 @@ uty_cli_start(vty_cli cli, node_type_t node)
* Revokes anything that can be revoked, which may allow output to proceed
* and the vty to clear itself down.
*
- * If in_progress and not final, keeps the cbuf, the olc and clx if in_progress.
+ * If dispatched and not final, keeps the cbuf, the olc and clx if dispatched.
* If not final, keeps the cbuf and the olc.
*
* Destroys self if final.
@@ -199,11 +214,7 @@ uty_cli_close(vty_cli cli, bool final)
if (cli == NULL)
return NULL ;
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- /* Revoke any command that is in flight. */
- cq_revoke(cli->vf->vio->vty) ;
+ VTY_ASSERT_CLI_THREAD_LOCKED() ;
/* Bring as much of the command handling to a stop as possible, and
* turn off "---more--" etc. so that output can proceed.
@@ -211,38 +222,48 @@ uty_cli_close(vty_cli cli, bool final)
cli->more_enabled = false ;
cli->lines = 0 ; /* so will not be re-enabled */
- uty_cli_wipe(cli, 0) ; /* wipe anything the CLI has on screen */
+ uty_cli_cancel(cli) ; /* " ^C\r\n" unless believed blank */
- cli->more_wait = false ; /* exit more_wait (if was in it) */
- cli->more_active = false ; /* and so cannot be this */
+ cli->more_wait = false ; /* exit more_wait (if was in it) */
+ cli->more_enter = false ; /* and so cannot be this */
- cli->out_active = true ; /* if there is any output, it can go */
+ cli->blocked = true ; /* do not renter the CLI */
+ cli->out_active = true ; /* if there is any output, it can go */
+ cli->flush = true ; /* all of it can go */
/* Ream out the history. */
{
qstring line ;
- while ((line = vector_ream(cli->hist, keep_it)) != NULL)
+ while ((line = vector_ream(cli->hist, free_it)) != NULL)
qs_reset(line, free_it) ;
+ cli->hist = NULL ;
} ;
/* Empty the keystroke handling. */
cli->key_stream = keystroke_stream_free(cli->key_stream) ;
- /* Can discard active command line if not in_progress. */
- if (!cli->in_progress || final)
+ /* Can discard active command line if not dispatched TODO ?? */
+ if (!cli->dispatched || final)
cli->clx = qs_reset(cli->clx, free_it) ;
- /* Can discard parsed object. */
- cmd_parsed_reset(cli->parsed, keep_it) ;
+ /* Can discard context and parsed objects */
+ cli->context = cmd_context_free(cli->context, true) ; /* its a copy */
+ cli->parsed = cmd_parsed_free(cli->parsed) ;
+
+ /* Discard any pause_timer, and suppress */
+ cli->pause_timer = qtimer_free(cli->pause_timer) ;
+ cli->paused = false ;
+ cli->tilde_enabled = false ;
/* If final, free the CLI object. */
if (final)
{
- qs_reset(cli->prompt_for_node, keep_it) ;
- cli->cl = qs_reset(cli->cl, free_it) ;
+ cli->prompt_for_node = qs_reset(cli->prompt_for_node, free_it) ;
+ cli->cl = qs_reset(cli->cl, free_it) ;
+ cli->cls = qs_reset(cli->cls, free_it) ;
- vio_fifo_reset(cli->cbuf, keep_it) ; /* embedded fifo */
- vio_lc_reset(cli->olc, keep_it) ; /* embedded line control */
+ cli->cbuf = vio_fifo_reset(cli->cbuf, free_it) ;
+ cli->olc = vio_lc_reset(cli->olc, free_it) ;
XFREE(MTYPE_VTY_CLI, cli) ; /* sets cli = NULL */
} ;
@@ -373,12 +394,18 @@ uty_cli_update_more(vty_cli cli)
* consider it for read_ready or write_ready, respectively.
*
* All command actions are dispatched via the command queue -- commands are
- * not executed in the cli. [For legacy threads the event queue is used. If
- * that is too slow, a priority event queue will have to be invented.]
+ * not executed inside the CLI code. [For legacy threads the event queue is
+ * used. If that is too slow, a priority event queue will have to be
+ * invented.]
*
* State of the CLI:
*
- * in_progress -- a command has been dispatched and has not yet completed.
+ * dispatched -- a command line has been dispatched, and is waiting for
+ * (dsp) command loop to fetch it.
+ *
+ * in_progress -- a command has been taken by the command loop, and is still
+ * (inp) running -- or at least no further command has been
+ * fetched by the command loop.
*
* The command may be a in_pipe, so there may be many commands
* to be completed before the CLI level command is.
@@ -386,36 +413,48 @@ uty_cli_update_more(vty_cli cli)
* or: the CLI has been closed.
*
* blocked -- is in_progress and a further command is now ready to be
- * dispatched.
+ * (bkd) dispatched.
*
* or: the CLI has been closed.
*
* out_active -- the command output FIFO is being emptied.
- *
- * This is set when a command completes, and cleared when
- * everything is written away.
- *
- * Note that in this context a command is an individual
- * command -- where in_progress may cover any number of
- * command if the CLI level command is an in_pipe.
- *
- * more_wait -- is in "--more--" wait state
- *
- * more_active -- is in "--more--" wait state, and the "--more--" prompt
- * has not been written away, yet.
+ * (oa)
+ * This is set when output is pushed, and cleared when
+ * everything is written away and flush is set. When it
+ * is set, any current command line is wiped.
+ *
+ * Note that what this flag does is prevent the CLI from
+ * running until the output completes, and in particular
+ * prevents it from writing anything to the CLI buffer.
+ *
+ * flush -- is set when the CLI is ready for the next command (so
+ * (fsh) when in_progress is cleared) to cause any incomplete
+ * command output to be flushed, and to signal that
+ * out_active should be cleared when all output is complete.
+ *
+ * more_wait -- is in "--more--" wait state. => out_active !
+ * (mwt)
+ * The "--more--" CLI uses the CLI output buffer to draw
+ * and undraw the "--more--" prompt. The buffer will
+ * otherwise be empty because is out_active.
+ *
+ * more_enter -- is in the process of entering the "--more--" wait state,
+ * (men) waiting to write the "--more--" prompt and prepare the
+ * keystroke input.
*
* The following are the valid combinations:
*
- * in_p: blkd: o_ac: m_wt: m_ac:
- * ----:-----:-----:-----:-----:-----------------------------------------
- * 0 : 0 : 0 : 0 : 0 : collecting a new command
- * 0 : 0 : 1 : 0 : 0 : waiting for command output to finish
- * 1 : 0 : X : 0 : 0 : command dispatched
- * 1 : 1 : X : 0 : 0 : waiting for command to complete
- * X : X : 0 : 1 : 1 : waiting for "--more--" to be written away
- * X : X : 0 : 1 : 0 : waiting for "--more--" response
- * 1 : 1 : 1 : 0 : 0 : waiting for command to complete, after the
- * CLI has been closed
+ * dsp:inp:bkd: oa:fsh:mwt:men:
+ * ---:---:---:---:---:---:---:-----------------------------------------
+ * 0 : 0 : 0 : 0 : 0 : 0 : 0 : collecting a new command
+ * 1 : 0 : 0 : 0 : 0 : 0 : 0 : waiting for command to be fetched
+ * 1 : 1 : 0 : X : 0 : 0 : 0 : command fetched and running
+ * 1 : 1 : 1 : X : 0 : 0 : 0 : waiting for command to complete
+ * 0 : 0 : 0 : 1 : 1 : 0 : 0 : waiting for command output to finish
+ * 1 : 1 : X : 1 : X : 1 : 1 : waiting for "--more--" to start
+ * 1 : 1 : X : 1 : X : 1 : 0 : waiting for "--more--" response
+ * 1 : 1 : 1 : 1 : 1 : 0 : 0 : waiting for command to complete,
+ * after the CLI has been closed
*
* There are two output FIFOs:
*
@@ -426,7 +465,17 @@ uty_cli_update_more(vty_cli cli)
* The CLI FIFO is emptied whenever possible, in preference to the command
* FIFO. The command FIFO is emptied when out_active. While a command is
* in_progress all its output is collected in the command output buffer,
- * to be written away when the command completes.
+ * to be written away when the command completes, or if it pushes the output
+ * explicitly. Note that where the CLI level command is a pipe-in, the first
+ * output will set out_active, and that will persist until returns to the CLI
+ * level. Between commands in the pipe, the output will be pushed, so will
+ * output stuff more or less as it is generated.
+ *
+ * It is expected that each command's output will end with a newline. However,
+ * the line control imposes some discipline, and holds on to incomplete lines
+ * until a newline arrives, or the output if flushed. -- so that when the
+ * CLI is kicked, the cursor will be at the start of an empty line.
+ *
*
* Note that only sets read on when the keystroke stream is empty and has not
* yet hit eof. The CLI process is driven mostly by write_ready -- which
@@ -436,32 +485,20 @@ uty_cli_update_more(vty_cli cli)
* re-entered again on write_ready/read_ready -- so does one command line at
* a time, yielding the processor after each one.
*
- * Note that select/pselect treat a socket which is at "EOF", or has seen an
- * error, or has been half closed, etc. as readable and writable. This means
- * that the CLI will continue to move forward even after the socket is no
- * longer delivering any data.
- *
*------------------------------------------------------------------------------
* The "--more--" handling.
*
* This is largely buried in the output handling.
*
- * When a command completes and its output is pushed to written away,
- * out_active will be set (and any command line will be wiped). The output
- * process is then kicked.
+ * When command is pushed to written away, out_active will be set (and any
+ * command line will be wiped). The output process is then kicked.
*
* The output process used the line_control structure to manage the output, and
* occasionally enter the trivial "--more--" CLI. This is invisible to the
- * main CLI. (See the more_wait and more_active flags and their handling.)
- *
- * When all the output has completed the out_active flag is cleared and the CLI
- * will be kicked.
- *
- * It is expected that the output will end with a newline -- so that when the
- * CLI is kicked, the cursor will be at the start of an empty line.
+ * main CLI. (See the more_wait and more_enter flags and their handling.)
*
* If the user decides to abandon output at the "--more--" prompt, then the
- * contents of the command output FIFO are discarded.
+ * then contents of the command output FIFO are discarded.
*
*------------------------------------------------------------------------------
* The qpthreads/qpnexus extension.
@@ -512,9 +549,9 @@ uty_cli_update_more(vty_cli cli)
#define CONTROL(X) ((X) & 0x1F)
static enum vty_readiness uty_cli_standard(vty_cli cli) ;
+static cmd_do_t uty_cli_auth(vty_cli cli) ;
static enum vty_readiness uty_cli_more_wait(vty_cli cli) ;
static void uty_cli_draw(vty_cli cli) ;
-static void uty_cli_draw_this(vty_cli cli, node_type_t node) ;
/*------------------------------------------------------------------------------
* CLI for VTY_TERMINAL
@@ -534,8 +571,8 @@ uty_cli(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- if (cli->vf->vin_state != vf_open)
- return not_ready ; /* Nothing more from CLI if closing */
+ if (cli->vf->vin_state == vf_closed)
+ return not_ready ; /* Nothing more from CLI if closed */
/* Standard or "--more--" CLI ? */
if (cli->more_wait)
@@ -548,14 +585,14 @@ uty_cli(vty_cli cli)
* The Standard CLI
*/
-static cmd_do_t uty_cli_process(vty_cli cli, node_type_t node) ;
+static cmd_do_t uty_cli_process(vty_cli cli) ;
static void uty_cli_response(vty_cli cli, cmd_do_t cmd_do) ;
-
-static void uty_cli_dispatch(vty_cli cli) ;
+static bool uty_cli_dispatch(vty_cli cli) ;
static cmd_do_t uty_cli_command(vty_cli cli) ;
static void uty_cli_hist_add (vty_cli cli, qstring clx) ;
+static void uty_cli_pause_start(vty_cli cli) ;
/*------------------------------------------------------------------------------
* Standard CLI for VTY_TERM -- if not blocked, runs until:
@@ -574,6 +611,8 @@ static void uty_cli_hist_add (vty_cli cli, qstring clx) ;
static vty_readiness_t
uty_cli_standard(vty_cli cli)
{
+ bool need_prompt ;
+
VTY_ASSERT_LOCKED() ;
assert(!cli->more_wait) ; /* cannot be here in more_wait state ! */
@@ -584,22 +623,24 @@ uty_cli_standard(vty_cli cli)
* NB: in both these cases, assumes that other forces are at work to
* keep things moving.
*/
- if (cli->blocked || cli->out_active)
+ if (cli->blocked || cli->out_active || cli->paused || cli->mon_active)
return not_ready ;
- /* If there is nothing pending, then can run the CLI until there is
- * something to do, or runs out of input.
+ /* Make sure that the command line is drawn.
*
- * If there is something to do, that is because a previous command has
- * now completed, which may have wiped the pending command or changed
- * the required prompt.
+ * If there is nothing pending, then can run the CLI until there is
+ * something to do, or runs out of input.
*/
+ if (!cli->drawn)
+ uty_cli_draw(cli) ;
+
if (cli->to_do == cmd_do_nothing)
- cli->to_do = uty_cli_process(cli, cli->node) ;
- else
- uty_cli_draw_this(cli, cli->node) ;
+ cli->to_do = cli->auth_context ? uty_cli_auth(cli)
+ : uty_cli_process(cli) ;
/* If have something to do, do it if we can. */
+ need_prompt = false ;
+
if (cli->to_do != cmd_do_nothing)
{
/* Reflect immediate response */
@@ -610,25 +651,46 @@ uty_cli_standard(vty_cli cli)
*
* Otherwise is now blocked until queued command completes.
*/
- if (cli->in_progress)
- cli->blocked = true ; /* blocked waiting for previous */
+ if (cli->dispatched)
+ cli->blocked = true ; /* waiting for previous */
else
- uty_cli_dispatch(cli) ; /* can dispatch the latest */
+ need_prompt = uty_cli_dispatch(cli) ; /* dispatch latest */
} ;
- /* Use write_ready as a proxy for read_ready on the keystroke stream.
+ /* If blocked, must wait for some other event to continue in CLI.
+ *
+ * Note that will be blocked if have just dispatched a command, and is
+ * "tilde_enabled" -- which will be true if single threaded, and may be
+ * set for other reasons.
*
- * Also, if the command line is not drawn, then return write_ready, so
- * that
+ * If the keystroke stream is not empty, use write_ready as a proxy for
+ * CLI ready -- no point doing anything until any buffered.
*
- * Note that if has just gone cli_blocked, still returns ready. This is
- * defensive: at worst will generate one unnecessary read_ready/write_ready
- * event.
+ * If command prompt has been redrawn, need to kick writer to deal with
+ * that -- will reenter to then process any keystrokes.
*/
- if (keystroke_stream_empty(cli->key_stream))
- return read_ready ;
- else
+ assert(!cli->paused) ;
+
+ if (cli->blocked)
+ return not_ready ;
+
+ if (!keystroke_stream_empty(cli->key_stream) || need_prompt)
return write_ready ;
+
+ /* The keystroke stream is empty, but CLI is not blocked.
+ *
+ * If a command is dispatched, and output is not active, and the command
+ * line is not drawn, then go to paused state, so that will delay output
+ * of the prompt slightly (or until a keystroke arrives, or the current
+ * command completes, or something else happens).
+ *
+ * Note that if a command has been dispatched, and is !tilde_enabled, then
+ * will now be blocked.
+ */
+ if (cli->dispatched && !cli->out_active && !cli->drawn)
+ uty_cli_pause_start(cli) ;
+
+ return read_ready ;
} ;
/*------------------------------------------------------------------------------
@@ -642,8 +704,10 @@ uty_cli_standard(vty_cli cli)
* Generally sets cli->to_do = cmd_do_nothing and clears cli->cl to empty.
*
* Can set cli->cl_do = and cli->cl to be a follow-on command.
+ *
+ * Returns: true <=> nothing, in fact, to do: prompt has been redrawn.
*/
-static void
+static bool
uty_cli_dispatch(vty_cli cli)
{
qstring tmp ;
@@ -654,16 +718,17 @@ uty_cli_dispatch(vty_cli cli)
VTY_ASSERT_LOCKED() ;
/* About to dispatch a command, so must be in the following state. */
- assert(!cli->in_progress && !cli->out_active && !cli->blocked) ;
- assert(cli->node == vio->vty->node) ;
+ assert(!cli->dispatched && !cli->in_progress
+ && !cli->blocked && !cli->out_active) ;
+ qassert(cli->context->node == vio->vty->exec->context->node) ;
/* Set cli->clx to the command about to execute & pick up cli->to_do.
*
* Clear cli->cl and cli->to_do.
*/
- tmp = cli->clx ; /* swap clx and cl */
- cli->clx = cli->cl ;
- cli->cl = tmp ;
+ tmp = cli->clx ; /* swap clx and cl */
+ cli->clx = cli->cl ;
+ cli->cl = tmp ;
to_do_now = cli->to_do ; /* current operation */
@@ -674,10 +739,8 @@ uty_cli_dispatch(vty_cli cli)
//uty_out_clear(cli->vio) ; /* clears FIFO and line control */
/* Dispatch command */
- if ((cli->node == AUTH_NODE) && (to_do_now != cmd_do_nothing))
+ if (cli->auth_context)
to_do_now |= cmd_do_auth ;
- else if ((cli->node == AUTH_ENABLE_NODE) && (to_do_now != cmd_do_nothing))
- to_do_now |= cmd_do_auth_enable ;
else
{
/* All other nodes... */
@@ -686,6 +749,7 @@ uty_cli_dispatch(vty_cli cli)
case cmd_do_nothing:
case cmd_do_ctrl_c:
case cmd_do_eof:
+ case cmd_do_timed_out:
break ;
case cmd_do_command:
@@ -715,28 +779,34 @@ uty_cli_dispatch(vty_cli cli)
if (to_do_now != cmd_do_nothing)
{
- cli->in_progress = true ;
- uty_cmd_dispatch(vio, to_do_now, cli->clx) ;
+ cmd_action_set(cli->dispatch, to_do_now, cli->clx) ;
+ cli->dispatched = true ;
+
+ uty_cmd_signal(vio, CMD_SUCCESS) ;
+
+ cli->blocked = (to_do_now != cmd_do_command) || !cli->tilde_enabled ;
}
else
- uty_cli_draw(cli) ;
+ {
+ cmd_action_clear(cli->dispatch) ;
+ cli->dispatched = false ;
+
+ uty_cli_draw(cli) ;
+ } ;
+
+ return !cli->dispatched ;
} ;
/*------------------------------------------------------------------------------
- * Enqueue command -- adding to history -- if is not empty or just comment
+ * Check if command is empty, if not add to history
*
- * This is for VTY_TERM type VTY.
- *
- * Returns: CMD_SUCCESS => empty command line (or just comment)
- * CMD_WAITING => enqueued for parse and execute
+ * Returns: cmd_do_nothing -- empty command line
+ * cmd_do_command -- command ready to be executed (added to history)
*/
static cmd_do_t
uty_cli_command(vty_cli cli)
{
- VTY_ASSERT_LOCKED() ;
- VTY_ASSERT_CLI_THREAD() ;
-
- if (cmd_is_empty(qs_els_nn(cli->clx)))
+ if (cmd_is_empty(cli->clx))
return cmd_do_nothing ; /* easy when nothing to do ! */
/* Add not empty command to history */
@@ -746,196 +816,79 @@ uty_cli_command(vty_cli cli)
} ;
/*------------------------------------------------------------------------------
- * A command has completed, and there is (or may be) some output to now
- * write away.
- */
-extern void
-uty_cli_out_push(vty_cli cli)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (cli->more_wait)
- return ; /* can do nothing */
-
- if (vio_fifo_empty(cli->vf->obuf))
- {
- assert(!cli->out_active ) ;
- return ; /* need do nothing if is empty */
- } ;
-
- uty_cli_wipe(cli, 0) ; /* wipe any partly constructed line */
-
- cli->out_active = true ; /* enable the output */
-
- uty_term_set_readiness(cli->vf, write_ready) ;
- /* kick the write side into action */
-} ;
-
-/*------------------------------------------------------------------------------
- * Queued command has completed.
- *
- * Note that sets write on whether there is anything in the output buffer
- * or not... write_ready will kick read_ready.
-
-
- * Command has completed, so:
- *
- * * clear cmd_in_progress
- * * set cmd_out_active -- so any output can now proceed
- * * set cli_blocked -- waiting for output to complete
- * * and prepare the line control for output
- *
- * If the return is CMD_CLOSE, then also now does the required half close.
- *
- * Note that apart from CMD_CLOSE, don't really care what the return was. Any
- * diagnostics or other action must be dealt with elsewhere (as part of the
- * command execution.
- *
- * Note that everything proceeds as if there is some output. So after every
- * command goes through at least one write_ready event.
- *
- * This ensures some multiplexing at the command level.
+ * Want another command line from the CLI.
*
- * It also means that the decision about whether there is anything to output
- * is left to the output code.
-
-
-
- */
-extern void
-uty_cli_done_command(vty_cli cli, node_type_t node)
-{
- VTY_ASSERT_LOCKED() ;
-
- uty_cli_out_push(cli) ; /* just in case */
-
- cli->in_progress = false ; /* command complete */
- cli->blocked = false ; /* releases possibly blocked command */
- cli->node = node ; /* and now in this node */
-
- /* Reach all the way back, and get the now current node. */
- cli->node = cli->vf->vio->vty->node ;
-
- uty_term_set_readiness(cli->vf, write_ready) ;
- /* kick the write side into action */
-} ;
-
-/*==============================================================================
- * All commands are dispatched to command_queue.
+ * If in_progress this <=> any previous command has completed, and output may
+ * be active writing the results away. Will:
*
- * Some commands are VLI specific... and end up back here.
- */
-
-/*------------------------------------------------------------------------------
- * Authentication of vty
+ * * clear cli->dispatched
+ * * clear cli->in_progress
+ * * clear cli->blocked
*
- * Note that if the AUTH_NODE password fails too many times, the terminal is
- * closed.
+ * May be in more_wait state -- so avoids touching that.
*
- * Returns: CMD_SUCCESS -- OK, one way or another
- * CMD_WARNING -- with error message sent to output
- * CMD_CLOSE -- too many password failures
+ * If not in_progress, then if dispatched, that is a new command ready to pass
+ * to the command loop -- which we do here, and set cli->in_progress.
*/
extern cmd_return_code_t
-uty_cli_auth(vty_cli cli)
+uty_cli_want_command(vty_cli cli, cmd_action action, cmd_context context)
{
- char *crypt (const char *, const char *);
-
- char* passwd = NULL ;
- bool encrypted = false ;
- node_type_t next_node = 0 ;
- cmd_return_code_t ret ;
- vty_io vio ;
- cmd_exec exec ;
-
VTY_ASSERT_LOCKED() ;
- vio = cli->vf->vio ;
- exec = vio->vty->exec ;
-
- /* Deal with the exotic terminators. */
- switch (exec->to_do & cmd_do_mask)
- {
- case cmd_do_nothing:
- case cmd_do_ctrl_c:
- case cmd_do_ctrl_z:
- return CMD_SUCCESS ;
-
- case cmd_do_command:
- break ;
-
- case cmd_do_ctrl_d:
- case cmd_do_eof:
- return uty_cmd_close(vio, "End") ;
-
- default:
- zabort("unknown or invalid cmd_do") ;
- } ;
-
- /* Ordinary command dispatch -- see if password is OK.
- *
- * Select the password we need to check against.
- */
- if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth)
+ if (cli->in_progress)
{
- passwd = host.password ;
- encrypted = host.password_encrypted ;
+ /* Previous command has completed
+ *
+ * Make sure state reflects the fact that we are now waiting for a
+ * command.
+ */
+ cli->dispatched = false ; /* no longer have a dispatched command */
+ cli->in_progress = false ; /* command complete */
+ cli->blocked = false ; /* releases possibly blocked command */
+ cli->paused = false ; /* override paused state */
+
+ *cli->context = *context ; /* make sure is up to date */
+ cli->auth_context = ( (cli->context->node == AUTH_NODE)
+ || (cli->context->node == AUTH_ENABLE_NODE) ) ;
- if (host.advanced)
- next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
+ /* If the output is owned by command output, then set flush flag, so
+ * that when buffers empty, the output will be released.
+ *
+ * If the output side is not owned by command output, wipe any temporary
+ * prompt.
+ *
+ * In any case, kick write_ready to ensure output clears and prompt is
+ * written and so on.
+ */
+ if (cli->out_active)
+ cli->flush = true ;
else
- next_node = VIEW_NODE;
- }
- else if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth_enable)
- {
- passwd = host.enable ;
- encrypted = host.enable_encrypted ;
+ uty_cli_draw(cli) ;
- next_node = ENABLE_NODE;
+ uty_term_set_readiness(cli->vf, write_ready) ;
}
- else
- zabort("unknown to_do_value") ;
-
- /* Check against selected password (if any) */
- if (passwd != NULL)
+ else if (cli->dispatched)
{
- char* candidate = qs_make_string(exec->line) ;
-
- if (encrypted)
- candidate = crypt(candidate, passwd) ;
-
- if (strcmp(candidate, passwd) == 0)
- {
- cli->password_fail = 0 ; /* forgive any recent failures */
- vio->vty->node = next_node;
+ /* New command has been dispatched -- can now pass that to the
+ * command loop -- setting it in_progress.
+ */
+ assert(cli->dispatch->to_do != cmd_do_nothing) ;
+ cmd_action_take(action, cli->dispatch) ;
- return CMD_SUCCESS ; /* <<< SUCCESS <<<<<<<< */
- } ;
+ cli->in_progress = true ;
} ;
- /* Password failed -- or none set ! */
- cli->password_fail++ ;
-
- ret = CMD_SUCCESS ; /* OK so far */
-
- if (cli->password_fail >= 3)
- {
- if ((exec->to_do & ~cmd_do_mask) == cmd_do_auth)
- {
- ret = uty_cmd_close(vio, "Bad passwords, too many failures!") ;
- }
- else
- {
- /* AUTH_ENABLE_NODE */
- cli->password_fail = 0 ; /* allow further attempts */
- uty_out(vio, "%% Bad enable passwords, too many failures!\n") ;
- vio->vty->node = host.restricted_mode ? RESTRICTED_NODE
- : VIEW_NODE ;
- ret = CMD_WARNING ;
- } ;
- } ;
+ return cli->in_progress ? CMD_SUCCESS : CMD_WAITING ;
+} ;
- return ret ;
+/*------------------------------------------------------------------------------
+ * Start pause timer and set paused.
+ */
+static void
+uty_cli_pause_start(vty_cli cli)
+{
+ qtimer_set(cli->pause_timer, qt_add_monotonic(QTIME(0.2)), NULL) ;
+ cli->paused = true ;
} ;
/*==============================================================================
@@ -944,13 +897,13 @@ uty_cli_auth(vty_cli cli)
* While command output is being cleared from its FIFO, the CLI is blocked.
*
* When the output side signals that "--more--" is required, it sets the
- * more_wait flag and clears the out_active flag.
+ * more_wait and more_enter flags.
*
* The first stage of handling "--more--" is to suck the input dry, so that
* (as far as is reasonably possible) does not steal a keystroke as the
* "--more--" response which was typed before the prompt was issued.
*
- * The cli_blocked flag indicates that the CLI is in this first stage.
+ * The more_enter flag indicates that the CLI is in this first stage.
*/
/*------------------------------------------------------------------------------
@@ -963,49 +916,10 @@ uty_cli_enter_more_wait(vty_cli cli)
{
VTY_ASSERT_LOCKED() ;
- assert(cli->out_active && !cli->more_wait) ;
-
- uty_cli_wipe(cli, 0) ; /* make absolutely sure that command line is
- wiped before change the CLI state */
+ assert(cli->out_active && !cli->more_wait && !cli->drawn) ;
- cli->out_active = false ; /* stop output pro tem */
- cli->more_wait = true ; /* new state */
- cli->more_active = true ; /* drawing the "--more--" */
-
- uty_cli_draw(cli) ; /* draw the "--more--" */
-} ;
-
-/*------------------------------------------------------------------------------
- * Exit the "--more--" CLI.
- *
- * Wipes the "--more--" prompt.
- *
- * This is used when the user responds to the prompt.
- *
- * It is also used when the vty is "half-closed". In this case, it is (just)
- * possible that the '--more--' prompt is yet to be completely written away,
- * so:
- *
- * * assert that is either: !cli->blocked (most of the time it will)
- * or: !vio_fifo_empty(cli->cbuf)
- *
- * * note that can wipe the prompt even though it hasn't been fully
- * written away yet. (The effect is to append the wipe action to the
- * cli_obuf !)
- */
-extern void
-uty_cli_exit_more_wait(vty_cli cli)
-{
- VTY_ASSERT_LOCKED() ;
-
- assert(cli->more_wait) ;
-
- uty_cli_wipe(cli, 0) ; /* wipe the prompt ('--more--')
- before changing the CLI state */
-
- cli->more_wait = false ; /* exit more_wait */
- cli->more_active = false ; /* tidy */
- cli->out_active = true ; /* re-enable output */
+ cli->more_wait = true ; /* new state */
+ cli->more_enter = true ; /* drawing the "--more--" etc. */
} ;
/*------------------------------------------------------------------------------
@@ -1018,92 +932,184 @@ uty_cli_exit_more_wait(vty_cli cli)
*
* EOF on input causes immediate exit from cli_more_state.
*
- * Returns: read_ready -- waiting to steal a keystroke
- * now_ready -- just left cli_more_wait
- * not_ready -- otherwise
+ * Returns: read_ready -- waiting to steal a keystroke
+ * write_ready -- waiting to draw or undraw the "--more--" prompt
+ * not_ready -- otherwise
*/
static vty_readiness_t
uty_cli_more_wait(vty_cli cli)
{
- struct keystroke steal ;
+ keystroke_t steal ;
+ bool cancel ;
VTY_ASSERT_LOCKED() ;
assert(cli->more_wait) ; /* must be in more_wait state ! */
+ if (cli->paused || cli->mon_active)
+ return not_ready ;
+
/* Deal with the first stage of "--more--" */
- if (cli->more_active)
+ if (cli->more_enter)
{
int get ;
+ uty_cli_draw_if_required(cli) ; /* draw the "--more--" */
+
/* If the CLI buffer is not yet empty, then is waiting for the
* initial prompt to clear, so nothing to be done here.
*/
if (!vio_fifo_empty(cli->cbuf))
return write_ready ;
- cli->more_active = false ;
+ cli->more_enter = false ;
/* empty the input buffer into the keystroke stream */
do
{
get = uty_term_read(cli->vf, NULL) ;
} while (get > 0) ;
+
+ return read_ready ;
} ;
/* Go through the "--more--" process, unless closing */
- if (cli->vf->vout_state == vf_open)
+ /* The read fetches a reasonable lump from the I/O -- so if there
+ * is a complete keystroke available, expect to get it.
+ *
+ * If no complete keystroke available to steal, returns ks_null.
+ *
+ * If has hit EOF or timeout (or error etc), returns knull_eof.
+ */
+ uty_term_read(cli->vf, steal) ;
+
+ /* If nothing stolen, make sure prompt is drawn and wait for more
+ * input.
+ *
+ * If anything at all has been stolen, then continue or cancel.
+ */
+ cancel = false ;
+ switch(steal->type)
{
- /* The read fetches a reasonable lump from the I/O -- so if there
- * is a complete keystroke available, expect to get it.
- *
- * If no complete keystroke available to steal, returns ks_null.
- *
- * If has hit EOF (or error etc), returns knull_eof.
- */
- uty_term_read(cli->vf, &steal) ;
+ case ks_null:
+ switch(steal->value)
+ {
+ case knull_not_eof:
+ // TODO need to refresh "--more--" in case of monitor ??
+ return read_ready; /* <<< exit: no keystroke */
+ break ;
- /* If nothing stolen, make sure prompt is drawn and wait for more
- * input.
- */
- if ((steal.type == ks_null) && (steal.value != knull_eof))
- {
- if (uty_cli_draw_if_required(cli)) /* "--more--" if req. */
- {
- cli->more_active = true ; /* written "--more--" again */
- return write_ready ;
- }
- else
- return read_ready ;
- } ;
+ case knull_eof:
+ case knull_timed_out:
+ cancel = true ;
+ break ;
- /* Stolen a keystroke -- a (very) few terminate all output */
- if (steal.type == ks_char)
- {
- switch (steal.value)
+ default:
+ break ;
+ } ;
+ break ;
+
+ case ks_char:
+ switch (steal->value)
{
case CONTROL('C'):
case 'q':
case 'Q':
- uty_out_clear(cli->vf->vio) ;
+ cancel = true ;
break;
- default: /* everything else, thrown away */
+ default:
break ;
} ;
- } ;
+ break ;
+
+ default:
+ break ;
} ;
/* End of "--more--" process
*
- * Wipe out the prompt and update state.
+ * Wipe out the prompt (unless "cancel") and update state.
*
* Return write_ready to tidy up the screen and, unless cleared, write
* some more.
*/
- uty_cli_exit_more_wait(cli) ;
+ if (cancel)
+ {
+ uty_out_clear(cli->vf->vio) ;
+ vio_lc_clear(cli->olc) ; /* clear & reset counter */
+ uty_cli_cancel(cli) ;
+ }
+ else
+ {
+ vio_lc_counter_reset(cli->olc) ;
+ uty_cli_wipe(cli, 0) ;
+ } ;
+
+ cli->more_wait = false ; /* exit more_wait */
+ cli->more_enter = false ; /* tidy */
+
+ return write_ready ;
+} ;
+
+/*==============================================================================
+ * Monitor output.
+ *
+ * To prepare for monitor output, wipe as much as is necessary for the
+ * monitor line to appear correctly.
+ *
+ * After monitor output, may need to do two things:
+ *
+ * * if the output was incomplete, place the rump in the CLI buffer,
+ * so that:
+ *
+ * a. don't mess up the console with partial lines
+ *
+ * b. don't lose part of a message
+ *
+ * c. act as a brake on further monitor output -- cannot do any more
+ * until the last, part, line is dealt with.
+ *
+ * * restore the command line, unless it is empty !
+ */
+
+ /*-----------------------------------------------------------------------------
+ * Prepare for new monitor output line.
+ *
+ * Wipe any existing command line.
+ */
+extern void
+uty_cli_pre_monitor(vty_cli cli)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_wipe(cli, 0) ;
+
+ cli->mon_active = true ; /* block cli & enable empty of fifo */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Recover from monitor line output.
+ *
+ * If monitor line failed to complete, append the rump to the CLI buffer.
+ *
+ * If have a non-empty command line, or is cli_more_wait, redraw the command
+ * line.
+ *
+ * Returns: 0 => rump was empty and no command line stuff written
+ * > 0 => rump not empty or some command line stuff written
+ */
+extern void
+uty_cli_post_monitor(vty_cli cli)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_pause_start(cli) ; /* do not draw prompt immediately */
- return now_ready ;
+ cli->more_enter = cli->more_wait ;
+ /* revert to more_enter state */
+
+ cli->mon_active = false ; /* unblock cli & turn off output */
} ;
/*==============================================================================
@@ -1116,14 +1122,9 @@ uty_cli_more_wait(vty_cli cli)
*
* It is expected that can flush straight to the file, since this is running at
* CLI speed. However, if the CLI is being driven by something other than a
- * keyboard, or "monitor" output has filled the buffers, then may need to
- * have intermediate buffering.
+ * keyboard then may need to have intermediate buffering.
*
* No actual I/O takes place here-- all "output" is to cli->cbuf
- *
- * The "cli_echo" functions discard the output if cli->echo_suppress.
- * This is used while passwords are entered and to allow command line changes
- * to be made while the line is not visible.
*/
enum { cli_rep_count = 32 } ;
@@ -1142,12 +1143,11 @@ CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
/* 12345678901234567890123456789012 */
static cli_rep telnet_spaces = " " ;
static cli_rep telnet_dots = "................................" ;
+static cli_rep telnet_stars = "********************************" ;
CONFIRM(sizeof(telnet_spaces) == (sizeof(cli_rep_char) + 1)) ;
CONFIRM(sizeof(telnet_dots) == (sizeof(cli_rep_char) + 1)) ;
-static const char* telnet_newline = "\r\n" ;
-
static void uty_cli_write_n(vty_cli cli, cli_rep_char chars, int n) ;
/*------------------------------------------------------------------------------
@@ -1175,41 +1175,11 @@ uty_cli_out_clear(vty_cli cli)
} ;
/*------------------------------------------------------------------------------
- * CLI VTY output -- echo user input
- *
- * Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
- */
-static void
-uty_cli_echo(vty_cli cli, const char *this, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (!cli->echo_suppress)
- uty_cli_write(cli, this, len) ;
-}
-
-/*------------------------------------------------------------------------------
- * CLI VTY output -- echo 'n' characters using a cli_rep_char string
- *
- * Do nothing if echo suppressed (eg in AUTH_NODE)
- */
-static void
-uty_cli_echo_n(vty_cli cli, cli_rep_char chars, int n)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (!cli->echo_suppress)
- uty_cli_write_n(cli, chars, n) ;
-}
-
-/*------------------------------------------------------------------------------
* CLI VTY output -- cf write()
*/
extern void
uty_cli_write(vty_cli cli, const char *this, int len)
{
- VTY_ASSERT_LOCKED() ;
-
vio_fifo_put_bytes(cli->cbuf, this, len) ;
} ;
@@ -1249,8 +1219,9 @@ uty_cli_write_s(vty_cli cli, const char *str)
} ;
/*==============================================================================
- * Standard Messages
+ * Prompts and responses
*/
+static void uty_cli_goto_end_if_drawn(vty_cli cli) ;
/*------------------------------------------------------------------------------
* Send newline to the console.
@@ -1260,6 +1231,8 @@ uty_cli_write_s(vty_cli cli, const char *str)
extern void
uty_cli_out_newline(vty_cli cli)
{
+ uty_cli_goto_end_if_drawn(cli) ;
+
uty_cli_write(cli, telnet_newline, 2) ;
cli->drawn = false ;
cli->dirty = false ;
@@ -1288,6 +1261,42 @@ uty_cli_out_wipe_n(vty_cli cli, int n)
} ;
/*------------------------------------------------------------------------------
+ * If the command line is drawn, then show that it has been cancelled.
+ *
+ * If the command line is dirty, then cancel it and start new line.
+ *
+ * Sets: cli_drawn = false
+ * cli_dirty = false
+ */
+static void
+uty_cli_cancel(vty_cli cli)
+{
+ if (cli->drawn || cli->dirty)
+ {
+ uty_cli_goto_end_if_drawn(cli) ;
+ uty_cli_write_s(cli, " ^C" TELNET_NEWLINE) ;
+ } ;
+
+ cli->drawn = false ;
+ cli->dirty = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the command line is drawn and the cursor is not at the end of the line,
+ * move the physical cursor to the end of the line.
+ *
+ * Assumes about to issue newline.
+ */
+static void
+uty_cli_goto_end_if_drawn(vty_cli cli)
+{
+ ulen after ;
+
+ if (cli->drawn && ( (after = qs_after_cp_nn(cli->cl)) != 0 ))
+ uty_cli_write(cli, qs_cp_char_nn(cli->cl), after) ;
+} ;
+
+/*------------------------------------------------------------------------------
* Send response to the given cmd_do
*
* If no command is in progress, then will send newline to signal that the
@@ -1299,18 +1308,20 @@ uty_cli_out_wipe_n(vty_cli cli, int n)
static const char* cli_response [2][cmd_do_count] =
{
{ /* when not waiting for previous command to complete */
- [cmd_do_command] = "",
- [cmd_do_ctrl_c] = "^C",
- [cmd_do_ctrl_d] = "^D",
- [cmd_do_ctrl_z] = "^Z",
- [cmd_do_eof] = "^*"
+ [cmd_do_command] = "",
+ [cmd_do_ctrl_c] = "^C",
+ [cmd_do_ctrl_d] = "^D",
+ [cmd_do_ctrl_z] = "^Z",
+ [cmd_do_eof] = "^*",
+ [cmd_do_timed_out] = "^!"
},
{ /* when waiting for a previous command to complete */
- [cmd_do_command] = "^",
- [cmd_do_ctrl_c] = "^C",
- [cmd_do_ctrl_d] = "^D",
- [cmd_do_ctrl_z] = "^Z",
- [cmd_do_eof] = "^*"
+ [cmd_do_command] = "^",
+ [cmd_do_ctrl_c] = "^C",
+ [cmd_do_ctrl_d] = "^D",
+ [cmd_do_ctrl_z] = "^Z",
+ [cmd_do_eof] = "^*",
+ [cmd_do_timed_out] = "^!"
}
} ;
@@ -1320,16 +1331,16 @@ uty_cli_response(vty_cli cli, cmd_do_t to_do)
const char* str ;
int len ;
- if ((to_do == cmd_do_nothing) || (cli->vf->vin_state != vf_open))
- return ;
+ assert((to_do != cmd_do_nothing) && cli->drawn
+ && (qs_cp_nn(cli->cl) == qs_len_nn(cli->cl))) ;
str = (to_do < cmd_do_count)
- ? cli_response[cli->in_progress ? 1 : 0][to_do] : NULL ;
+ ? cli_response[cli->dispatched ? 1 : 0][to_do] : NULL ;
assert(str != NULL) ;
len = uty_cli_write_s(cli, str) ;
- if (cli->in_progress)
+ if (cli->dispatched)
{
cli->extra_len = len ;
uty_cli_write_n(cli, telnet_backspaces, len) ;
@@ -1341,28 +1352,6 @@ uty_cli_response(vty_cli cli, cmd_do_t to_do)
} ;
/*------------------------------------------------------------------------------
- * Send various messages with trailing newline.
- */
-#if 0
-static void
-uty_cli_out_CMD_ERR_AMBIGUOUS(vty_cli cli)
-{
- uty_cli_write_s(cli, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
- uty_cli_out_newline(cli) ;
-} ;
-
-static void
-uty_cli_out_CMD_ERR_NO_MATCH(vty_cli cli)
-{
- uty_cli_write_s(cli, "% " MSG_CMD_ERR_NO_MATCH ".") ;
- uty_cli_out_newline(cli) ;
-} ;
-#endif
-/*==============================================================================
- * Command line draw and wipe
- */
-
-/*------------------------------------------------------------------------------
* Current prompt length
*/
extern ulen
@@ -1393,7 +1382,7 @@ uty_cli_wipe(vty_cli cli, int len)
a = cli->extra_len ;
b = cli->prompt_len ;
- if (!cli->echo_suppress && !cli->more_wait)
+ if (!cli->more_wait)
{
a += qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ;
b += qs_cp_nn(cli->cl) ;
@@ -1436,17 +1425,6 @@ uty_cli_draw_if_required(vty_cli cli)
} ;
/*------------------------------------------------------------------------------
- * Draw prompt etc for the current vty node.
- *
- * See uty_cli_draw_this()
- */
-static void
-uty_cli_draw(vty_cli cli)
-{
- uty_cli_draw_this(cli, cli->node) ;
-} ;
-
-/*------------------------------------------------------------------------------
* Draw prompt and entire command line, leaving current position where it
* should be.
*
@@ -1458,21 +1436,20 @@ uty_cli_draw(vty_cli cli)
*
* * if is closing, draw nothing -- wipes the current line
*
- * * if is cli_more_wait, draw the "--more--" prompt
+ * * if is more_wait, draw the "--more--" prompt
*
- * * if is cmd_in_progress, draw the vestigial prompt.
+ * * if is dispatched, draw the vestigial prompt.
*
* By the time the current command completes, the node may have changed, so
* the current prompt may be invalid.
*
- * Sets: cli_drawn = true
- * cli_dirty = false
- * cli_prompt_len = length of prompt used
- * cli_extra_len = 0
- * cli_echo_suppress = (AUTH_NODE or AUTH_ENABLE_NODE)
+ * Sets: cli->drawn = true
+ * cli->dirty = false
+ * cli->prompt_len = length of prompt used
+ * cli->extra_len = 0
*/
static void
-uty_cli_draw_this(vty_cli cli, enum node_type node)
+uty_cli_draw(vty_cli cli)
{
const char* prompt ;
size_t l_len ;
@@ -1494,7 +1471,7 @@ uty_cli_draw_this(vty_cli cli, enum node_type node)
p_len = strlen(prompt) ;
l_len = 0 ;
}
- else if (cli->in_progress)
+ else if (cli->dispatched)
{
/* If there is a queued command, the prompt is a minimal affair. */
prompt = "~ " ;
@@ -1510,6 +1487,8 @@ uty_cli_draw_this(vty_cli cli, enum node_type node)
*
* If the host name changes, the cli_prompt_set flag is cleared.
*/
+ node_type_t node = cli->context->node ;
+
if ((node != cli->prompt_node) || (host.name_gen != cli->prompt_gen))
{
const char* prompt ;
@@ -1521,7 +1500,8 @@ uty_cli_draw_this(vty_cli cli, enum node_type node)
prompt = "%s ???: " ;
} ;
- qs_printf(cli->prompt_for_node, prompt, host.name);
+ cli->prompt_for_node =
+ qs_printf(cli->prompt_for_node, prompt, host.name) ;
cli->prompt_node = node ;
cli->prompt_gen = host.name_gen ;
@@ -1539,134 +1519,60 @@ uty_cli_draw_this(vty_cli cli, enum node_type node)
/* Set about writing the prompt and the line */
cli->drawn = true ;
cli->extra_len = false ;
- cli->echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
- cli->prompt_len = p_len ;
+ cli->prompt_len = p_len ;
uty_cli_write(cli, prompt, p_len) ;
if (l_len != 0)
{
- uty_cli_write(cli, qs_char(cli->cl), l_len) ;
+ if (cli->auth_context)
+ uty_cli_write_n(cli, telnet_stars, l_len) ;
+ else
+ uty_cli_write(cli, qs_char(cli->cl), l_len) ;
+
if (qs_cp_nn(cli->cl) < l_len)
uty_cli_write_n(cli, telnet_backspaces, l_len - qs_cp_nn(cli->cl)) ;
} ;
} ;
/*==============================================================================
- * Monitor output.
- *
- * To prepare for monitor output, wipe as much as is necessary for the
- * monitor line to appear correctly.
- *
- * After monitor output, may need to do two things:
- *
- * * if the output was incomplete, place the rump in the CLI buffer,
- * so that:
- *
- * a. don't mess up the console with partial lines
- *
- * b. don't lose part of a message
- *
- * c. act as a brake on further monitor output -- cannot do any more
- * until the last, part, line is dealt with.
- *
- * * restore the command line, unless it is empty !
- */
-
- /*-----------------------------------------------------------------------------
- * Prepare for new monitor output line.
- *
- * Wipe any existing command line.
- */
-extern void
-uty_cli_pre_monitor(vty_cli cli, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- uty_cli_wipe(cli, len) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Recover from monitor line output.
- *
- * If monitor line failed to complete, append the rump to the CLI buffer.
- *
- * If have a non-empty command line, or is cli_more_wait, redraw the command
- * line.
- *
- * Returns: 0 => rump was empty and no command line stuff written
- * > 0 => rump not empty or some command line stuff written
- */
-extern int
-uty_cli_post_monitor(vty_cli cli, const char* buf, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (len != 0)
- uty_cli_write(cli, buf, len) ;
-
- if (cli->more_wait || (qs_len_nn(cli->cl) != 0))
- {
- uty_cli_draw(cli) ;
- ++len ;
- } ;
-
- return len ;
-} ;
-
-/*==============================================================================
* Command line processing loop
*/
-
-static int uty_cli_insert (vty_cli cli, const char* chars, int n) ;
-static int uty_cli_overwrite (vty_cli cli, char* chars, int n) ;
-//static int uty_cli_word_overwrite (vty_cli cli, char *str) ;
-static int uty_cli_forwards(vty_cli cli, int n) ;
-static int uty_cli_backwards(vty_cli cli, int n) ;
-static int uty_cli_del_forwards(vty_cli cli, int n) ;
-static int uty_cli_del_backwards(vty_cli cli, int n) ;
-static int uty_cli_bol (vty_cli cli) ;
-static int uty_cli_eol (vty_cli cli) ;
-static int uty_cli_word_forwards_delta(vty_cli cli) ;
-static int uty_cli_word_forwards(vty_cli cli) ;
-static int uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces) ;
-//static int uty_cli_word_backwards_pure (vty_cli cli) ;
-static int uty_cli_word_backwards (vty_cli cli) ;
-static int uty_cli_del_word_forwards(vty_cli cli) ;
-static int uty_cli_del_word_backwards(vty_cli cli) ;
-static int uty_cli_del_to_eol (vty_cli cli) ;
-static int uty_cli_clear_line(vty_cli cli) ;
-static int uty_cli_transpose_chars(vty_cli cli) ;
-static void uty_cli_hist_use(vty_cli cli, int step) ;
-static void uty_cli_hist_next(vty_cli cli) ;
-static void uty_cli_hist_previous(vty_cli cli) ;
-static void uty_cli_complete_command (vty_cli cli, node_type_t node) ;
-static void uty_cli_describe_command (vty_cli cli, node_type_t node) ;
-
-/*------------------------------------------------------------------------------
- * Fetch next keystroke, reading from the file if required.
- */
-static bool
-uty_cli_get_keystroke(vty_cli cli, keystroke stroke)
-{
- int got ;
-
- do
- {
- if (keystroke_get(cli->key_stream, stroke))
- return true ;
-
- got = uty_term_read(cli->vf, NULL) ; /* not stealing */
- } while (got > 0) ;
-
- return false ;
-} ;
+static cmd_do_t uty_cli_get_keystroke(vty_cli cli, keystroke stroke) ;
+static void uty_cli_update_line(vty_cli cli, uint rc) ;
+
+static void uty_cli_insert (qstring cl, const char* chars, int n) ;
+static int uty_cli_forwards(qstring cl, int n) ;
+static int uty_cli_backwards(qstring cl, int n) ;
+static void uty_cli_del_forwards(qstring cl, int n) ;
+static void uty_cli_del_backwards(qstring cl, int n) ;
+static void uty_cli_bol(qstring cl) ;
+static void uty_cli_eol(qstring cl) ;
+static int uty_cli_word_forwards_delta(qstring cl) ;
+static void uty_cli_word_forwards(qstring cl) ;
+static int uty_cli_word_backwards_delta(qstring cl) ;
+static void uty_cli_word_backwards(qstring cl) ;
+static void uty_cli_del_word_forwards(qstring cl) ;
+static void uty_cli_del_word_backwards(qstring cl) ;
+static void uty_cli_del_to_eol(qstring cl) ;
+static void uty_cli_clear_line(qstring cl) ;
+static void uty_cli_transpose_chars(qstring cl) ;
+static void uty_cli_complete_command (vty_cli cli) ;
+static void uty_cli_describe_command (vty_cli cli) ;
+
+enum hist_step
+{
+ hist_previous = -1,
+ hist_next = +1,
+};
+
+static void uty_cli_hist_use(vty_cli cli, enum hist_step) ;
/*------------------------------------------------------------------------------
* Process keystrokes until run out of input, or get something to cmd_do.
*
- * If required, draw the prompt and command line.
+ * Expects the command line to have been drawn.
*
* Process keystrokes until run out of stuff to do, or have a "command line"
* that must now be executed.
@@ -1675,53 +1581,41 @@ uty_cli_get_keystroke(vty_cli cli, keystroke stroke)
* ready to read and return. (To give some "sharing".)
*
* Returns: cmd_do_xxxx
- *
- * When returns the cl is '\0' terminated.
*/
static cmd_do_t
-uty_cli_process(vty_cli cli, node_type_t node)
+uty_cli_process(vty_cli cli)
{
- struct keystroke stroke ;
+ keystroke_t stroke ;
uint8_t u ;
- bool auth ;
cmd_do_t to_do ;
- auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+ qs_copy(cli->cls, cli->cl) ;
+
+ assert(cli->drawn) ;
/* Now process as much as possible of what there is */
- to_do = cmd_do_nothing ;
- while (to_do == cmd_do_nothing)
+ do
{
- if (!cli->drawn)
- uty_cli_draw_this(cli, node) ;
-
- if (!uty_cli_get_keystroke(cli, &stroke))
- {
- to_do = (stroke.value == knull_eof) ? cmd_do_eof : cmd_do_nothing ;
- break ;
- } ;
+ to_do = uty_cli_get_keystroke(cli, stroke) ;
- if (stroke.flags != 0)
- {
- /* TODO: deal with broken keystrokes */
- continue ;
- } ;
+ if (to_do != cmd_do_keystroke)
+ break ;
- switch (stroke.type)
+ switch (stroke->type)
{
/* Straightforward character -----------------------------------*/
/* Note: only interested in 8-bit characters ! */
case ks_char:
- u = (uint8_t)stroke.value ;
+ u = (uint8_t)stroke->value ;
- switch (stroke.value)
+ switch (stroke->value)
{
case CONTROL('A'):
- uty_cli_bol (cli);
+ uty_cli_bol(cli->cl) ;
break;
case CONTROL('B'):
- uty_cli_backwards(cli, 1);
+ uty_cli_backwards(cli->cl, 1);
break;
case CONTROL('C'):
@@ -1729,47 +1623,44 @@ uty_cli_process(vty_cli cli, node_type_t node)
break ;
case CONTROL('D'):
- if (auth)
- to_do = cmd_do_ctrl_d ; /* Exit on ^D ..................*/
- else
- uty_cli_del_forwards(cli, 1);
+ uty_cli_del_forwards(cli->cl, 1);
break;
case CONTROL('E'):
- uty_cli_eol (cli);
+ uty_cli_eol(cli->cl);
break;
case CONTROL('F'):
- uty_cli_forwards(cli, 1);
+ uty_cli_forwards(cli->cl, 1);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_backwards(cli, 1);
+ uty_cli_del_backwards(cli->cl, 1);
break;
case CONTROL('K'):
- uty_cli_del_to_eol (cli);
+ uty_cli_del_to_eol(cli->cl);
break;
case CONTROL('N'):
- uty_cli_hist_next (cli);
+ uty_cli_hist_use(cli, hist_next) ;
break;
case CONTROL('P'):
- uty_cli_hist_previous(cli);
+ uty_cli_hist_use(cli, hist_previous) ;
break;
case CONTROL('T'):
- uty_cli_transpose_chars (cli);
+ uty_cli_transpose_chars(cli->cl) ;
break;
case CONTROL('U'):
- uty_cli_clear_line(cli);
+ uty_cli_clear_line(cli->cl) ;
break;
case CONTROL('W'):
- uty_cli_del_word_backwards (cli);
+ uty_cli_del_word_backwards(cli->cl) ;
break;
case CONTROL('Z'):
@@ -1782,49 +1673,45 @@ uty_cli_process(vty_cli cli, node_type_t node)
break ;
case '\t':
- if (auth)
- uty_cli_insert (cli, " ", 1) ;
- else if (cmd_token_position(cli->parsed, cli->cl))
- uty_cli_insert (cli, " ", 1) ;
+ if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert (cli->cl, " ", 1) ;
else
- uty_cli_complete_command (cli, node);
+ uty_cli_complete_command(cli);
break;
case '?':
- if (auth)
- uty_cli_insert (cli, (char*)&u, 1) ;
- else if (cmd_token_position(cli->parsed, cli->cl))
- uty_cli_insert (cli, (char*)&u, 1) ;
+ if (cmd_token_position(cli->parsed, cli->cl))
+ uty_cli_insert(cli->cl, (char*)&u, 1) ;
else
- uty_cli_describe_command (cli, node);
+ uty_cli_describe_command(cli);
break;
default:
- if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
- uty_cli_insert (cli, (char*)&u, 1) ;
+ if ((stroke->value >= 0x20) && (stroke->value < 0x7F))
+ uty_cli_insert(cli->cl, (char*)&u, 1) ;
break;
}
break ;
/* ESC X -------------------------------------------------------------*/
case ks_esc:
- switch (stroke.value)
+ switch (stroke->value)
{
case 'b':
- uty_cli_word_backwards (cli);
+ uty_cli_word_backwards(cli->cl);
break;
case 'f':
- uty_cli_word_forwards (cli);
+ uty_cli_word_forwards(cli->cl);
break;
case 'd':
- uty_cli_del_word_forwards (cli);
+ uty_cli_del_word_forwards(cli->cl);
break;
case CONTROL('H'):
case 0x7f:
- uty_cli_del_word_backwards (cli);
+ uty_cli_del_word_backwards(cli->cl);
break;
default:
@@ -1832,37 +1719,39 @@ uty_cli_process(vty_cli cli, node_type_t node)
} ;
break ;
- /* ESC [ X -----------------------------------------------------------*/
+ /* ESC [ ... ---------------------------------------------------------*/
case ks_csi:
- if (stroke.len != 0)
- break ; /* only recognise 3 byte sequences */
-
- switch (stroke.value)
- {
- case ('A'): /* up arrow */
- uty_cli_hist_previous(cli);
- break;
-
- case ('B'): /* down arrow */
- uty_cli_hist_next (cli);
- break;
-
- case ('C'): /* right arrow */
- uty_cli_forwards(cli, 1);
- break;
-
- case ('D'): /* left arrow */
- uty_cli_backwards(cli, 1);
- break;
-
- default:
- break ;
- } ;
- break ;
-
- /* Telnet Command ----------------------------------------------------*/
- case ks_iac:
- uty_telnet_command(cli->vf, &stroke, false) ;
+ if (stroke->len == 0)
+ {
+ /* ESC [ X */
+ switch (stroke->value)
+ {
+ case ('A'): /* up arrow */
+ uty_cli_hist_use(cli, hist_previous) ;
+ break;
+
+ case ('B'): /* down arrow */
+ uty_cli_hist_use(cli, hist_next) ;
+ break;
+
+ case ('C'): /* right arrow */
+ uty_cli_forwards(cli->cl, 1) ;
+ break;
+
+ case ('D'): /* left arrow */
+ uty_cli_backwards(cli->cl, 1) ;
+ break;
+
+ default:
+ break ;
+ } ;
+ }
+ else if (stroke->len == 1)
+ {
+ /* ESC [ 3 ~ only ! */
+ if ((stroke->value == '~') && (stroke->buf[0] == '3'))
+ uty_cli_del_forwards(cli->cl, 1) ;
+ } ;
break ;
/* Unknown -----------------------------------------------------------*/
@@ -1870,151 +1759,309 @@ uty_cli_process(vty_cli cli, node_type_t node)
zabort("unknown keystroke type") ;
} ;
- } ;
+ }
+ while (to_do == cmd_do_keystroke) ;
/* Tidy up and return where got to. */
if (to_do != cmd_do_nothing)
- uty_cli_eol (cli) ; /* go to the end of the line */
+ uty_cli_eol(cli->cl) ; /* go to the end of the line */
+
+ uty_cli_update_line(cli, qs_cp_nn(cli->cl)) ;
return to_do ;
} ;
-/*==============================================================================
- * Command line operations
- */
-
/*------------------------------------------------------------------------------
- * Insert 'n' characters at current position in the command line
+ * Update the command line to reflect the difference between old line and the
+ * new line.
*
- * Returns number of characters inserted -- ie 'n'
+ * Leave the screen cursor at the given required cursor position.
*/
-static int
-uty_cli_insert (vty_cli cli, const char* chars, int n)
+static void
+uty_cli_update_line(vty_cli cli, uint rc)
{
- int after ;
+ const char* np ; /* new line */
+ const char* sp ; /* screen line */
- VTY_ASSERT_LOCKED() ;
+ ulen nl ; /* new length */
+ ulen sl ; /* screen length */
- assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0)) ;
+ ulen sc ; /* screen cursor */
- if (n <= 0)
- return n ; /* avoid trouble */
+ ulen s, l ;
- after = qs_insert(cli->cl, chars, n) ;
+ assert(cli->drawn) ;
- uty_cli_echo(cli, qs_cp_char(cli->cl), after) ;
+ np = qs_char_nn(cli->cl) ;
+ nl = qs_len_nn(cli->cl) ;
- if ((after - n) != 0)
- uty_cli_echo_n(cli, telnet_backspaces, after - n) ;
+ sp = qs_char_nn(cli->cls) ;
+ sl = qs_len_nn(cli->cls) ;
+ sc = qs_cp_nn(cli->cls) ;
- qs_move_cp_nn(cli->cl, n) ;
+ /* Find how many characters are the same. */
+ l = (nl <= sl) ? nl : sl ;
+ s = 0 ;
+ while ((s < l) && (*(np + s) == *(sp + s)))
+ ++s ;
- return n ;
+ /* If the screen and new are different lengths, or the strings are not
+ * the same, then need to draw stuff to correct what is on the screen.
+ * That will leave the screen cursor at the end of the new line, after
+ * any spaces required to wipe out excess characters.
+ *
+ * Note that sc is the current cursor position on the screen, and we keep
+ * that up to date as we draw stuff.
+ */
+ if ((nl != sl) || (s != nl))
+ {
+ /* Move back if the screen cursor is beyond the same section */
+ if (sc > s)
+ {
+ uty_cli_write_n(cli, telnet_backspaces, sc - s) ;
+ sc = s ;
+ } ;
+
+ /* Write from cursor to the end of the new line. */
+ uty_cli_write(cli, np + sc, nl - sc) ;
+ sc = nl ;
+
+ /* If the old line was longer, need to wipe out old stuff */
+ if (sl > nl)
+ {
+ uty_cli_write_n(cli, telnet_spaces, sl - nl) ;
+ sc = sl ;
+ } ;
+ } ;
+
+ /* Now move cursor to the required cursor position */
+ if (sc > rc)
+ uty_cli_write_n(cli, telnet_backspaces, sc - rc) ;
+
+ if (sc < rc) /* => lines unchanged, but cursor moved */
+ uty_cli_write(cli, np + sc, rc - sc) ;
} ;
/*------------------------------------------------------------------------------
- * Overstrike 'n' characters at current position in the command line
+ * For password: process keystrokes until run out of input, or get something
+ * to cmd_do.
*
- * Move current position forwards.
+ * Similar to uty_cli_auth, except accepts a limited number of keystrokes.
*
- * Returns number of characters inserted -- ie 'n'
+ * Returns: cmd_do_xxxx
*/
-static int
-uty_cli_overwrite (vty_cli cli, char* chars, int n)
+static cmd_do_t
+uty_cli_auth(vty_cli cli)
{
- VTY_ASSERT_LOCKED() ;
+ keystroke_t stroke ;
+ uint8_t u ;
+ cmd_do_t to_do ;
+ int olen, nlen ;
- assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0)) ;
+ /* For auth command lines, cursor is always at the end */
+ assert(qs_cp_nn(cli->cl) == qs_len_nn(cli->cl)) ;
- if (n > 0)
+ olen = qs_len_nn(cli->cl) ;
+
+ /* Now process as much as possible of what there is */
+ do
{
- qs_replace(cli->cl, n, chars, n) ;
- uty_cli_echo(cli, chars, n) ;
+ to_do = uty_cli_get_keystroke(cli, stroke) ;
- qs_move_cp_nn(cli->cl, n) ;
- } ;
+ if (to_do != cmd_do_keystroke)
+ break ;
- return n ;
-}
+ switch (stroke->type)
+ {
+ /* Straightforward character ---------------------------------*/
+ /* Note: only interested in 8-bit characters ! */
+ case ks_char:
+ u = (uint8_t)stroke->value ;
+
+ switch (stroke->value)
+ {
+ case CONTROL('C'):
+ to_do = cmd_do_ctrl_c ; /* Exit on ^C */
+ break ;
+
+ case CONTROL('D'):
+ to_do = cmd_do_ctrl_d ; /* Exit on ^D */
+ break;
+
+ case CONTROL('H'):
+ case 0x7F:
+ uty_cli_del_backwards(cli->cl, 1);
+ break;
+
+ case CONTROL('U'):
+ case CONTROL('W'):
+ uty_cli_clear_line(cli->cl);
+ break;
+
+ case CONTROL('Z'):
+ to_do = cmd_do_ctrl_z ; /* Exit on ^Z */
+ break;
+
+ case '\n':
+ case '\r':
+ to_do = cmd_do_command ; /* Exit on CR or LF */
+ break ;
+
+ default:
+ if ((stroke->value >= 0x20) && (stroke->value < 0x7F))
+ uty_cli_insert(cli->cl, (char*)&u, 1) ;
+ break;
+ }
+ break ;
+
+ /* ESC X -----------------------------------------------------*/
+ case ks_esc:
+ switch (stroke->value)
+ {
+ case CONTROL('H'):
+ case 0x7f:
+ uty_cli_clear_line(cli->cl);
+ break;
+
+ default:
+ break;
+ } ;
+ break ;
+
+ /* ESC [ ... ---------------------------------------------------*/
+ case ks_csi:
+ break ;
+
+ /* Unknown -----------------------------------------------------*/
+ default:
+ zabort("unknown keystroke type") ;
+ } ;
+ }
+ while (to_do == cmd_do_keystroke) ;
+
+ /* Tidy up and return where got to. */
+
+ nlen = qs_len_nn(cli->cl) ;
+
+ if (nlen < olen)
+ uty_cli_out_wipe_n(cli, nlen - olen) ;
+ if (nlen > olen)
+ uty_cli_write_n(cli, telnet_stars, nlen - olen) ;
+
+ return to_do ;
+} ;
/*------------------------------------------------------------------------------
- * Replace 'm' characters at the current position, by 'n' characters and leave
- * cursor at the end of the inserted characters.
+ * Fetch next keystroke, reading from the file if required.
*
- * Returns number of characters inserted -- ie 'n'
+ * Returns: cmd_do_t
*/
-static int
-uty_cli_replace(vty_cli cli, int m, const char* chars, int n)
+static cmd_do_t
+uty_cli_get_keystroke(vty_cli cli, keystroke stroke)
{
- int a, b ;
+ while (1)
+ {
+ if (keystroke_get(cli->key_stream, stroke))
+ {
+ if (stroke->flags != 0)
+ {
+ /* TODO: deal with broken keystrokes */
+ }
- VTY_ASSERT_LOCKED() ;
+ if (stroke->type != ks_iac)
+ return cmd_do_keystroke ; /* have a keystroke */
- assert((qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) && (n >= 0) && (m >= 0)) ;
+ /* Deal with telnet command, so invisible to upper level */
+ uty_telnet_command(cli->vf, stroke, false) ;
+ }
+ else
+ {
+ int get ;
- b = qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ;
- a = qs_replace(cli->cl, m, chars, n) ;
+ assert(stroke->type == ks_null) ;
- uty_cli_echo(cli, qs_cp_char(cli->cl), a) ;
+ switch (stroke->value)
+ {
+ case knull_not_eof:
+ break ;
- if (a < b)
- uty_cli_echo_n(cli, telnet_spaces, b - a) ;
- else
- b = a ;
+ case knull_eof:
+ return cmd_do_eof ;
- if (b > n)
- uty_cli_echo_n(cli, telnet_backspaces, b - n) ;
+ case knull_timed_out:
+ return cmd_do_timed_out ;
- qs_move_cp_nn(cli->cl, n) ;
+ default:
+ zabort("unknown knull_xxx") ;
+ break ;
+ } ;
- return n ;
+ get = uty_term_read(cli->vf, NULL) ; /* sets eof in key_stream
+ if hit eof or error */
+ if (get <= 0)
+ return cmd_do_nothing ;
+ } ;
+ } ;
} ;
-#if 0
+/*==============================================================================
+ * Command line operations
+ */
+
/*------------------------------------------------------------------------------
- * Insert a word into vty interface with overwrite mode.
+ * Insert 'n' characters at current position in the command line, leaving
+ * cursor after the inserted characters.
*
- * NB: Assumes result will then be the end of the line.
- *
- * Returns number of characters inserted -- ie length of string
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_word_overwrite (vty_cli cli, char *str)
+static void
+uty_cli_insert(qstring cl, const char* chars, int n)
{
- int n ;
- VTY_ASSERT_LOCKED() ;
+ assert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0)) ;
- n = uty_cli_overwrite(cli, str, strlen(str)) ;
+ if (n > 0)
+ {
+ qs_insert(cl, chars, n) ;
+ qs_move_cp_nn(cl, n) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Replace 'm' characters at the current position, by 'n' characters and leave
+ * cursor at the end of the inserted characters.
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
+ */
+static void
+uty_cli_replace(qstring cl, int m, const char* chars, int n)
+{
+ assert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0) && (m >= 0)) ;
- qs_set_len_nn(cli->cl, qs_cp_nn(cli->cl)) ;
+ qs_replace(cl, m, chars, n) ;
- return n ;
-}
-#endif
+ qs_move_cp_nn(cl, n) ;
+} ;
/*------------------------------------------------------------------------------
* Forward 'n' characters -- stop at end of line.
*
* Returns number of characters actually moved
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static int
-uty_cli_forwards(vty_cli cli, int n)
+uty_cli_forwards(qstring cl, int n)
{
int have ;
- VTY_ASSERT_LOCKED() ;
- have = qs_after_cp_nn(cli->cl) ;
+ have = qs_after_cp_nn(cl) ;
if (have < n)
n = have ;
assert(n >= 0) ;
- if (n > 0)
- {
- uty_cli_echo(cli, qs_cp_char(cli->cl), n) ;
- qs_move_cp_nn(cli->cl, n) ;
- } ;
+ qs_move_cp_nn(cl, n) ;
return n ;
} ;
@@ -2023,22 +2070,18 @@ uty_cli_forwards(vty_cli cli, int n)
* Backwards 'n' characters -- stop at start of line.
*
* Returns number of characters actually moved
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static int
-uty_cli_backwards(vty_cli cli, int n)
+uty_cli_backwards(qstring cl, int n)
{
- VTY_ASSERT_LOCKED() ;
-
- if ((int)qs_cp_nn(cli->cl) < n)
- n = qs_cp_nn(cli->cl) ;
+ if ((int)qs_cp_nn(cl) < n)
+ n = qs_cp_nn(cl) ;
assert(n >= 0) ;
- if (n > 0)
- {
- uty_cli_echo_n(cli, telnet_backspaces, n) ;
- qs_move_cp_nn(cli->cl, -n) ;
- } ;
+ qs_move_cp_nn(cl, -n) ;
return n ;
} ;
@@ -2047,86 +2090,69 @@ uty_cli_backwards(vty_cli cli, int n)
* Move forwards (if n > 0) or backwards (if n < 0) -- stop at start or end of
* line.
*
- * Returns number of characters actually moved -- signed
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_move(vty_cli cli, int n)
+static void
+uty_cli_move(qstring cl, int n)
{
- VTY_ASSERT_LOCKED() ;
-
if (n < 0)
- return - uty_cli_backwards(cli, -n) ;
+ uty_cli_backwards(cl, -n) ;
if (n > 0)
- return + uty_cli_forwards(cli, +n) ;
-
- return 0 ;
+ uty_cli_forwards(cl, +n) ;
} ;
/*------------------------------------------------------------------------------
* Delete 'n' characters -- forwards -- stop at end of line.
*
- * Returns number of characters actually deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_forwards(vty_cli cli, int n)
+static void
+uty_cli_del_forwards(qstring cl, int n)
{
- int after ;
int have ;
- VTY_ASSERT_LOCKED() ;
-
- have = qs_after_cp_nn(cli->cl) ;
+ have = qs_after_cp_nn(cl) ;
if (have < n)
n = have ; /* cannot delete more than have */
assert(n >= 0) ;
- if (n <= 0)
- return 0 ;
-
- after = qs_delete(cli->cl, n) ;
-
- if (after > 0)
- uty_cli_echo(cli, qs_cp_char(cli->cl), after) ;
-
- uty_cli_echo_n(cli, telnet_spaces, n) ;
- uty_cli_echo_n(cli, telnet_backspaces, after + n) ;
-
- return n ;
+ if (n > 0)
+ qs_delete(cl, n) ;
}
/*------------------------------------------------------------------------------
* Delete 'n' characters before the point.
*
- * Returns number of characters actually deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_backwards(vty_cli cli, int n)
+static void
+uty_cli_del_backwards(qstring cl, int n)
{
- return uty_cli_del_forwards(cli, uty_cli_backwards(cli, n)) ;
+ uty_cli_del_forwards(cl, uty_cli_backwards(cl, n)) ;
}
/*------------------------------------------------------------------------------
* Move to the beginning of the line.
*
- * Returns number of characters moved over.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_bol (vty_cli cli)
+static void
+uty_cli_bol(qstring cl)
{
- return uty_cli_backwards(cli, qs_cp_nn(cli->cl)) ;
+ qs_set_cp_nn(cl, 0) ;
} ;
/*------------------------------------------------------------------------------
* Move to the end of the line.
*
- * Returns number of characters moved over
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_eol (vty_cli cli)
+static void
+uty_cli_eol(qstring cl)
{
- return uty_cli_forwards(cli, qs_after_cp_nn(cli->cl)) ;
+ qs_set_cp_nn(cl, qs_len_nn(cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -2137,25 +2163,23 @@ uty_cli_eol (vty_cli cli)
* Steps over non-space characters and then any spaces.
*/
static int
-uty_cli_word_forwards_delta(vty_cli cli)
+uty_cli_word_forwards_delta(qstring cl)
{
char* cp ;
char* tp ;
char* ep ;
- VTY_ASSERT_LOCKED() ; ;
+ cp = qs_cp_char(cl) ;
+ ep = qs_ep_char(cl) ;
- assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
-
- cp = qs_cp_char(cli->cl) ;
- ep = qs_ep_char(cli->cl) ;
+ assert(cp <= ep) ;
tp = cp ;
- while ((tp < ep) && (*tp != ' '))
+ while ((tp < ep) && (*tp != ' ')) /* step over spaces */
++tp ;
- while ((tp < ep) && (*tp == ' '))
+ while ((tp < ep) && (*tp == ' ')) /* step to space */
++tp ;
return tp - cp ;
@@ -2165,11 +2189,13 @@ uty_cli_word_forwards_delta(vty_cli cli)
* Forward word -- move to start of next word.
*
* Moves past any non-spaces, then past any spaces.
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_word_forwards(vty_cli cli)
+static void
+uty_cli_word_forwards(qstring cl)
{
- return uty_cli_forwards(cli, uty_cli_word_forwards_delta(cli)) ;
+ uty_cli_forwards(cl, uty_cli_word_forwards_delta(cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -2181,24 +2207,21 @@ uty_cli_word_forwards(vty_cli cli)
* Steps back until next (backwards) character is space, or hits start of line.
*/
static int
-uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces)
+uty_cli_word_backwards_delta(qstring cl)
{
char* cp ;
char* tp ;
char* sp ;
- VTY_ASSERT_LOCKED() ; ;
-
- assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ;
+ assert(qs_cp_nn(cl) <= qs_len_nn(cl)) ;
- cp = qs_cp_char(cli->cl) ;
- sp = qs_char(cli->cl) ;
+ cp = qs_cp_char(cl) ;
+ sp = qs_char(cl) ;
tp = cp ;
- if (eat_spaces)
- while ((tp > sp) && (*(tp - 1) == ' '))
- --tp ;
+ while ((tp > sp) && (*(tp - 1) == ' '))
+ --tp ;
while ((tp > sp) && (*(tp - 1) != ' '))
--tp ;
@@ -2206,33 +2229,18 @@ uty_cli_word_backwards_delta(vty_cli cli, int eat_spaces)
return cp - tp ;
} ;
-#if 0
-/*------------------------------------------------------------------------------
- * Backward word, but not trailing spaces.
- *
- * Move back until next (backwards) character is space or start of line.
- *
- * Returns number of characters stepped over.
- */
-static int
-uty_cli_word_backwards_pure (vty_cli cli)
-{
- return uty_cli_backwards(cli, uty_cli_word_backwards_delta(cli, 0)) ;
-} ;
-#endif
-
/*------------------------------------------------------------------------------
* Backward word -- move to start of previous word.
*
* Moves past any spaces, then move back until next (backwards) character is
* space or start of line.
*
- * Returns number of characters stepped over.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_word_backwards (vty_cli cli)
+static void
+uty_cli_word_backwards(qstring cl)
{
- return uty_cli_backwards(cli, uty_cli_word_backwards_delta(cli, 1)) ;
+ uty_cli_backwards(cl, uty_cli_word_backwards_delta(cl)) ;
} ;
/*------------------------------------------------------------------------------
@@ -2240,12 +2248,12 @@ uty_cli_word_backwards (vty_cli cli)
*
* Deletes any leading spaces, then deletes upto next space or end of line.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_word_forwards(vty_cli cli)
+static void
+uty_cli_del_word_forwards(qstring cl)
{
- return uty_cli_del_forwards(cli, uty_cli_word_forwards_delta(cli)) ;
+ uty_cli_del_forwards(cl, uty_cli_word_forwards_delta(cl)) ;
}
/*------------------------------------------------------------------------------
@@ -2253,73 +2261,69 @@ uty_cli_del_word_forwards(vty_cli cli)
*
* Deletes any trailing spaces, then deletes upto next space or start of line.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_word_backwards(vty_cli cli)
+static void
+uty_cli_del_word_backwards(qstring cl)
{
- return uty_cli_del_backwards(cli, uty_cli_word_backwards_delta(cli, 1)) ;
+ uty_cli_del_backwards(cl, uty_cli_word_backwards_delta(cl)) ;
} ;
/*------------------------------------------------------------------------------
* Kill rest of line from current point.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_del_to_eol (vty_cli cli)
+static void
+uty_cli_del_to_eol(qstring cl)
{
- return uty_cli_del_forwards(cli, qs_after_cp_nn(cli->cl)) ;
+ qs_set_len_nn(cl, qs_cp_nn(cl)) ;
} ;
/*------------------------------------------------------------------------------
* Kill line from the beginning.
*
- * Returns number of characters deleted.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_clear_line(vty_cli cli)
+static void
+uty_cli_clear_line(qstring cl)
{
- uty_cli_bol(cli) ;
- return uty_cli_del_to_eol(cli) ;
+ qs_set_cp_nn(cl, 0) ;
+ qs_set_len_nn(cl, 0) ;
} ;
/*------------------------------------------------------------------------------
* Transpose chars before or at the point.
*
- * Return number of characters affected.
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
-static int
-uty_cli_transpose_chars(vty_cli cli)
+static void
+uty_cli_transpose_chars(qstring cl)
{
- char chars[2] ;
+ char ch ;
char* cp ;
- VTY_ASSERT_LOCKED() ;
-
/* Give up if < 2 characters or at start of line. */
- if ((qs_len_nn(cli->cl) < 2) || (qs_cp_nn(cli->cl) < 1))
- return 0 ;
+ if ((qs_len_nn(cl) < 2) || (qs_cp_nn(cl) < 1))
+ return ;
- /* Move back to first of characters to exchange */
- if (qs_cp_nn(cli->cl) == qs_len_nn(cli->cl))
- uty_cli_backwards(cli, 2) ;
- else
- uty_cli_backwards(cli, 1) ;
+ /* If we are not at the end, step past the second character */
+ if (qs_after_cp_nn(cl) == 0)
+ qs_move_cp_nn(cl, 1) ;
- /* Pick up in the new order */
- cp = qs_cp_char(cli->cl) ;
- chars[1] = *cp++ ;
- chars[0] = *cp ;
+ /* Get address of first character */
+ cp = qs_cp_char(cl) - 2 ;
- /* And overwrite */
- return uty_cli_overwrite(cli, chars, 2) ;
+ /* swap characters */
+ ch = *(cp + 1) ;
+ *(cp + 1) = *cp ;
+ *cp = ch ;
} ;
/*==============================================================================
* Command line history handling
*
- * cli->hist is vector of qstrings
+ * cli->hist is vector of qstrings (created on demand)
* cli->h_now is index of the present time
* cli->hp is index of most recent line read back
*
@@ -2335,7 +2339,8 @@ uty_cli_transpose_chars(vty_cli cli)
*
* hp == h_now means we are in the present.
*
- * Cannot step forwards from hp == h_now (into the future !).
+ * Cannot step forwards from hp == h_now (into the future, which is the
+ * same as the oldest thing we can remember !).
*
* Before stepping backwards from hp == hp_now, sets hp_now to be a copy of
* the current line (complete with cp), so can return to the present.
@@ -2347,6 +2352,40 @@ uty_cli_transpose_chars(vty_cli cli)
* is the same as the new line, apart from whitespace.
*/
+static inline uint
+hp_next(uint hp)
+{
+ return (hp != (VTY_HIST_COUNT - 1)) ? hp + 1 : 0 ;
+} ;
+
+static inline uint
+hp_prev(uint hp)
+{
+ return (hp != 0) ? hp - 1 : VTY_HIST_COUNT - 1 ;
+} ;
+
+static inline uint
+hp_step(uint hp, enum hist_step step)
+{
+ if (step == hist_previous)
+ return hp_prev(hp) ;
+
+ if (step == hist_next)
+ return hp_next(hp) ;
+
+ zabort("invalid hist_step") ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Create cli->hist.
+ */
+static void
+uty_cli_hist_make(vty_cli cli)
+{
+ cli->hist = vector_init_new(cli->hist, VTY_HIST_COUNT) ;
+ vector_set_min_length(cli->hist, VTY_HIST_COUNT) ;
+} ;
+
/*------------------------------------------------------------------------------
* Add given command line to the history buffer.
*
@@ -2358,19 +2397,12 @@ static void
uty_cli_hist_add (vty_cli cli, qstring clx)
{
qstring hist_line ;
- int prev ;
+ int h_prev ;
VTY_ASSERT_LOCKED() ;
- /* make sure have a suitable history vector */
- vector_set_min_length(cli->hist, VTY_MAXHIST) ;
-
- /* get the previous command line */
- prev = cli->h_now - 1 ;
- if (prev < 0)
- prev = VTY_MAXHIST - 1 ;
-
- hist_line = vector_get_item(cli->hist, prev) ;
+ if (cli->hist == NULL)
+ uty_cli_hist_make(cli) ; /* create if required */
/* If the previous line is NULL, that means the history is empty.
*
@@ -2384,10 +2416,20 @@ uty_cli_hist_add (vty_cli cli, qstring clx)
* Otherwise, leave cli->h_now and point hist_line at the most ancient
* line in history.
*/
+ h_prev = hp_prev(cli->h_now) ;
+
+ hist_line = vector_get_item(cli->hist, h_prev) ;
+
if ((hist_line == NULL) || (qs_cmp_sig(hist_line, clx) == 0))
- cli->h_now = prev ;
+ {
+ cli->h_now = h_prev ;
+ cli->h_repeat = true ; /* latest history is a repeat */
+ }
else
- hist_line = vector_get_item(cli->hist, cli->h_now) ;
+ {
+ hist_line = vector_get_item(cli->hist, cli->h_now) ;
+ cli->h_repeat = false ; /* latest history is novel */
+ } ;
/* Now replace the h_now entry
*
@@ -2398,12 +2440,8 @@ uty_cli_hist_add (vty_cli cli, qstring clx)
qs_set_cp_nn(hist_line, qs_len_nn(hist_line)) ;
vector_set_item(cli->hist, cli->h_now, hist_line) ;
- /* Advance to the near future and reset the history pointer */
- cli->h_now++;
- if (cli->h_now == VTY_MAXHIST)
- cli->h_now = 0;
-
- cli->hp = cli->h_now;
+ /* Advance history */
+ cli->hp = cli->h_now = hp_next(cli->h_now) ;
} ;
/*------------------------------------------------------------------------------
@@ -2413,148 +2451,110 @@ uty_cli_hist_add (vty_cli cli, qstring clx)
*
* Step -1 is into the past (up)
* +1 is towards the present (down)
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static void
-uty_cli_hist_use(vty_cli cli, int step)
+uty_cli_hist_use(vty_cli cli, enum hist_step step)
{
- int hp ;
- ulen old_len ;
- ulen new_len ;
- ulen after ;
- ulen back ;
+ uint hp ;
qstring hist_line ;
- VTY_ASSERT_LOCKED() ;
-
assert((step == +1) || (step == -1)) ;
+ if (cli->hist == NULL)
+ uty_cli_hist_make(cli) ; /* create if required */
+
hp = cli->hp ;
- /* Special case of being at the insertion point */
+ /* Special case of being at the insertion point (the present)
+ *
+ * Cannot step forwards from the present.
+ *
+ * before stepping back from the present, take a copy of the current
+ * command line -- so can get back to it.
+ *
+ * Note that the 'cp' is stored with the line. So if return to the present,
+ * the cursor returns to its current position. (When lines are added to
+ * the history, the cursor is at the end of the line.)
+ */
if (hp == cli->h_now)
{
if (step > 0)
return ; /* already in the present */
- /* before stepping back from the present, take a copy of the
- * current command line -- so can get back to it.
- *
- * Note that the 'cp' is stored with the line. If return to here
- * and later enter the line it will replace this.
- */
hist_line = vector_get_item(cli->hist, cli->h_now) ;
vector_set_item(cli->hist, cli->h_now, qs_copy(hist_line, cli->cl)) ;
} ;
- /* Advance or retreat */
- hp += step ;
- if (hp < 0)
- hp = VTY_MAXHIST - 1 ;
- else if (hp >= VTY_MAXHIST)
- hp = 0 ;
+ /* Advance or retreat and get history line. */
+ hp = hp_step(hp, step) ;
hist_line = vector_get_item(cli->hist, hp) ;
/* If moving backwards in time, may not move back to the h_now
* point (that would be wrapping round to the present) and may not
* move back to a NULL entry (that would be going back before '.').
- *
- * If moving forwards in time, may return to the present, with
- * hp == cli->h_now.
*/
if (step < 0)
if ((hist_line == NULL) || (hp == cli->h_now))
return ;
+ /* Update the history pointer and copy history line to current line. */
cli->hp = hp ;
-
- /* Move back to the start of the current line */
- uty_cli_bol(cli) ;
-
- /* Get previous line from history buffer and echo that */
- old_len = qs_len_nn(cli->cl) ;
qs_copy(cli->cl, hist_line) ;
- new_len = qs_len_nn(cli->cl) ;
-
- /* Sort out wiping out any excess and setting the cursor position */
- if (old_len > new_len)
- after = old_len - new_len ;
- else
- after = 0 ;
-
- /* Return cursor to stored 'cp' -- which will be end of line unless
- * this is the copy of the original current line stored above.
- */
- back = after + qs_after_cp_nn(cli->cl) ;
-
- if (new_len > 0)
- uty_cli_echo(cli, qs_char_nn(cli->cl), new_len) ;
-
- if (after > 0)
- uty_cli_echo_n(cli, telnet_spaces, after) ;
-
- if (back > 0)
- uty_cli_echo_n(cli, telnet_backspaces, back) ;
-
- return ;
} ;
/*------------------------------------------------------------------------------
- * Use previous history line, if any (up arrow).
- */
-static void
-uty_cli_hist_previous(vty_cli cli)
-{
- uty_cli_hist_use(cli, -1) ;
-}
-
-/*------------------------------------------------------------------------------
- * Use next history line, if any (down arrow).
- */
-static void
-uty_cli_hist_next(vty_cli cli)
-{
- uty_cli_hist_use(cli, +1) ;
-}
-
-/*------------------------------------------------------------------------------
* Show the contents of the history
*/
extern void
uty_cli_hist_show(vty_cli cli)
{
- int hp ;
+ uint hp ;
+ uint h_end ;
VTY_ASSERT_LOCKED() ;
+ if (cli->hist == NULL)
+ return ; /* if no history */
+
+ /* We start with the oldest thing we can remember, which means that
+ * we start by stepping "forwards" from "now".
+ *
+ * Until the history buffer fills, there will be a number of NULL entries
+ * between "now" and the oldest thing in the history.
+ *
+ * We do not show the "now" entry, which is not part of history.
+ *
+ * We do not show the entry before "now", because that is the current
+ * executing command, unless that was a repeat of the command before !
+ */
hp = cli->h_now ;
- while(1)
+ h_end = cli->h_repeat ? hp : hp_prev(hp) ;
+
+ while (1)
{
qstring line ;
- ++hp ;
- if (hp == VTY_MAXHIST)
- hp = 0 ;
+ hp = hp_next(hp) ;
- if (hp == cli->h_now)
- break ; /* wrapped round to "now" */
+ if (hp == h_end)
+ break ; /* reached end of history */
line = vector_get_item(cli->hist, hp) ;
- if (line == NULL)
- break ; /* reached limit of history so far */
-
- uty_out(cli->vf->vio, " %s\n", qs_string(line));
- }
+ if (line != NULL)
+ uty_out(cli->vf->vio, " %s\n", qs_make_string(line));
+ } ;
} ;
/*==============================================================================
* Command Completion and Command Description
*
*/
-static uint uty_cli_help_parse(vty_cli cli, node_type_t node) ;
-static void uty_cli_out_message(vty_cli cli, const char* msg) ;
+static uint uty_cli_help_parse(vty_cli cli) ;
static void uty_cli_complete_keyword(vty_cli cli, const char* keyword) ;
static void uty_cli_complete_list(vty_cli cli, vector item_v) ;
@@ -2562,9 +2562,12 @@ static void uty_cli_complete_list(vty_cli cli, vector item_v) ;
static void uty_cli_describe_list(vty_cli cli, vector item_v) ;
static void uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
const char* doc, uint len) ;
-
static uint uty_cli_width_to_use(vty_cli cli) ;
+static void uty_cli_help_message(vty_cli cli, const char* msg) ;
+static void uty_cli_help_newline(vty_cli cli) ;
+static void uty_cli_help_finish(vty_cli cli) ;
+
/*------------------------------------------------------------------------------
* Command completion
*
@@ -2572,9 +2575,12 @@ static uint uty_cli_width_to_use(vty_cli cli) ;
* establish which token the cursor is in. Must NOT call this if the cursor
* is in a "special" place.
*
+ * This is called from inside "uty_cli_process()".
+ *
+ * NB: assumes line will be updated by uty_cli_update_line()
*/
static void
-uty_cli_complete_command (vty_cli cli, node_type_t node)
+uty_cli_complete_command (vty_cli cli)
{
uint n_items ;
cmd_parsed parsed ;
@@ -2585,61 +2591,68 @@ uty_cli_complete_command (vty_cli cli, node_type_t node)
parsed = cli->parsed ;
/* Establish what items may be present at the current token position. */
- n_items = uty_cli_help_parse(cli, node) ;
-
- if (n_items == 0) /* quit if nothing to consider */
- return ;
+ n_items = uty_cli_help_parse(cli) ;
- if (n_items > 1) /* render list of alternatives */
- return uty_cli_complete_list(cli, parsed->item_v) ;
+ if (n_items > 1) /* render list of alternatives */
+ uty_cli_complete_list(cli, parsed->item_v) ;
+ else if (n_items == 1)
+ {
+ /* One possible item -- one or more possible commands */
+ item = vector_get_item(parsed->item_v, 0) ;
- /* One possible item -- one or more possible commands */
- item = vector_get_item(parsed->item_v, 0) ;
+ switch (item->type)
+ {
+ case item_null:
+ zabort("invalid item_null") ;
- switch (item->type)
- {
- case item_null:
- zabort("invalid item_null") ;
+ case item_eol:
- case item_eol:
+ case item_option_word:
- case item_option_word:
+ case item_vararg:
- case item_vararg:
+ case item_word:
- case item_word:
+ case item_ipv6_prefix:
+ case item_ipv6_address:
+ case item_ipv4_prefix:
+ case item_ipv4_address:
- case item_ipv6_prefix:
- case item_ipv6_address:
- case item_ipv4_prefix:
- case item_ipv4_address:
+ case item_range:
+ uty_cli_describe_list(cli, parsed->item_v) ;
+ break ;
- case item_range:
- return uty_cli_describe_list(cli, parsed->item_v) ;
+ case item_keyword:
+ uty_cli_complete_keyword(cli, item->str) ;
+ break ;
- case item_keyword:
- return uty_cli_complete_keyword(cli, item->str) ;
+ default:
+ zabort("unknown item type") ;
+ } ;
+ } ;
- default:
- zabort("unknown item type") ;
- } ;
+ /* If necessary, redraw the command line */
+ uty_cli_help_finish(cli) ;
} ;
/*------------------------------------------------------------------------------
* Command Description
*/
static void
-uty_cli_describe_command (vty_cli cli, node_type_t node)
+uty_cli_describe_command (vty_cli cli)
{
uint n_items ;
VTY_ASSERT_LOCKED() ;
/* Establish what items may be present at the current token position. */
- n_items = uty_cli_help_parse(cli, node) ;
+ n_items = uty_cli_help_parse(cli) ;
if (n_items > 0) /* render list of possibilities */
uty_cli_describe_list(cli, cli->parsed->item_v) ;
+
+ /* If necessary, redraw the command line */
+ uty_cli_help_finish(cli) ;
} ;
/*------------------------------------------------------------------------------
@@ -2654,7 +2667,7 @@ uty_cli_describe_command (vty_cli cli, node_type_t node)
* Returns: number of items to consider.
*/
static uint
-uty_cli_help_parse(vty_cli cli, node_type_t node)
+uty_cli_help_parse(vty_cli cli)
{
const char* msg ;
cmd_return_code_t ret ;
@@ -2666,22 +2679,25 @@ uty_cli_help_parse(vty_cli cli, node_type_t node)
msg = cmd_help_preflight(cli->parsed) ;
if (msg != NULL)
{
- uty_cli_out_message(cli, msg) ;
+ uty_cli_help_message(cli, msg) ;
return 0 ;
} ;
/* Now see what the cmd_completion can come up with. */
- ret = cmd_completion(cli->parsed, node) ;
+ ret = cmd_completion(cli->parsed, cli->context) ;
if (ret == CMD_ERR_PARSING)
{
- uint eloc = cli->prompt_len + cli->parsed->eloc ;
+ if (cli->parsed->eloc >= 0)
+ {
+ uint eloc = cli->prompt_len + cli->parsed->eloc ;
- uty_cli_out_newline(cli) ; /* clears cli_drawn */
- uty_cli_write_n(cli, telnet_dots, eloc) ;
- uty_cli_write_s(cli, "^") ;
+ uty_cli_help_newline(cli) ; /* clears cli_drawn etc. */
+ uty_cli_write_n(cli, telnet_dots, eloc) ;
+ uty_cli_write_s(cli, "^") ;
+ } ;
- uty_cli_out_message(cli, cli->parsed->emess) ;
+ uty_cli_help_message(cli, qs_make_string(cli->parsed->emess)) ;
return 0 ;
} ;
@@ -2692,29 +2708,14 @@ uty_cli_help_parse(vty_cli cli, node_type_t node)
n_items = vector_length(cli->parsed->item_v) ;
if (n_items == 0)
- uty_cli_out_message(cli, "command not recognised") ;
+ uty_cli_help_message(cli, "command not recognised") ;
return n_items ;
} ;
-
-
-
-
-
-
-
-static void
-uty_cli_out_message(vty_cli cli, const char* msg)
-{
- uty_cli_out_newline(cli) ; /* clears cli_drawn */
- uty_cli_write_s(cli, "% ") ;
- uty_cli_write_s(cli, msg) ;
- uty_cli_out_newline(cli) ;
-} ;
-
-
-
+/*------------------------------------------------------------------------------
+ * Can complete a keyword.
+ */
static void
uty_cli_complete_keyword(vty_cli cli, const char* keyword)
{
@@ -2722,13 +2723,14 @@ uty_cli_complete_keyword(vty_cli cli, const char* keyword)
cmd_complete_keyword(cli->parsed, &pre, &rep, &ins, &mov) ;
- uty_cli_move(cli, pre) ; /* move to start of token */
- uty_cli_replace(cli, rep, keyword, strlen(keyword)) ;
+ uty_cli_move(cli->cl, pre) ; /* move to start of token */
+ uty_cli_replace(cli->cl, rep, keyword, strlen(keyword)) ;
+ assert(ins <= 2) ;
if (ins > 0)
- uty_cli_insert(cli, " ", ins) ;
+ uty_cli_insert(cli->cl, " ", ins) ;
- uty_cli_move(cli, mov) ;
+ uty_cli_move(cli->cl, mov) ;
return ;
} ;
@@ -2775,7 +2777,7 @@ uty_cli_complete_list(vty_cli cli, vector item_v)
uty_cli_out(cli, "%-*s ", len, item->str) ;
}
- uty_cli_out_newline(cli) ;
+ uty_cli_help_newline(cli) ;
} ;
/*------------------------------------------------------------------------------
@@ -2877,7 +2879,7 @@ uty_cli_describe_list(vty_cli cli, vector item_v)
uty_cli_describe_line(cli, str_width, str, dp, ep - dp) ;
} ;
- uty_cli_out_newline(cli) ;
+ uty_cli_help_newline(cli) ;
} ;
/*------------------------------------------------------------------------------
@@ -2890,7 +2892,7 @@ uty_cli_describe_line(vty_cli cli, uint str_width, const char* str,
if ((*str == '\0') && (len == 0))
return ; /* quit if nothing to say */
- uty_cli_out_newline(cli) ;
+ uty_cli_help_newline(cli) ;
if (len == 0)
uty_cli_out(cli, " %s", str) ; /* left justify */
@@ -2911,3 +2913,51 @@ uty_cli_width_to_use(vty_cli cli)
{
return (cli->width == 0) ? 60 : cli->width ;
} ;
+
+/*------------------------------------------------------------------------------
+ * Move to new line, issue message and leave on new line.
+ *
+ * Deals with udating the command line if we are currently on it.
+ */
+static void
+uty_cli_help_message(vty_cli cli, const char* msg)
+{
+ uty_cli_help_newline(cli) ; /* clears cli->drawn etc. */
+ uty_cli_write_s(cli, "% ") ;
+ uty_cli_write_s(cli, msg) ;
+ uty_cli_write(cli, telnet_newline, 2) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the command line is drawn, make sure it is up to date, leaving cursor
+ * at the end of the line, and then issue newline.
+ *
+ * Clears cli->drawn and cli->dirty.
+ */
+static void
+uty_cli_help_newline(vty_cli cli)
+{
+ if (cli->drawn)
+ uty_cli_update_line(cli, qs_len_nn(cli->cl)) ;
+
+ uty_cli_write(cli, telnet_newline, 2) ;
+
+ cli->drawn = false ;
+ cli->dirty = false ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If the command line help has "undrawn" the command line, then redraw it now
+ * and make a new copy to cli->cls.
+ *
+ * Sets cli->drawn
+ */
+static void
+uty_cli_help_finish(vty_cli cli)
+{
+ if (!cli->drawn)
+ {
+ uty_cli_draw(cli) ;
+ qs_copy(cli->cls, cli->cl) ;
+ } ;
+} ;