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.c1187
1 files changed, 775 insertions, 412 deletions
diff --git a/lib/vty_cli.c b/lib/vty_cli.c
index de9bb53c..53d64716 100644
--- a/lib/vty_cli.c
+++ b/lib/vty_cli.c
@@ -26,10 +26,12 @@
#include "keystroke.h"
#include "vty.h"
#include "uty.h"
-#include "vty_io.h"
#include "vty_cli.h"
+#include "vty_io.h"
+#include "vio_lines.h"
#include "command.h"
+#include "command_queue.h"
#include "memory.h"
@@ -130,60 +132,41 @@ uty_new_host_name(const char* name)
*
* State of the CLI:
*
- * cli_blocked -- a command has been dispatched, and CLI is now waiting
- * for it and/or its output to complete.
- *
- * cmd_in_progress -- a command has been dispatched (and may have been
- * queued).
- *
- * If not blocked: can continue in the CLI until another
- * command is ready to be executed.
- *
- * There are two output FIFOs:
- *
- * 1. for the CLI itself -- uty_cli_out and friends
- *
- * 2. for output generated by commands -- vty_out and friends.
- *
- * The CLI FIFO is emptied whenever possible, in preference to the command
- * FIFO. The command FIFO is only emptied when !cmd_in_progress -- this means
- * that all the output from a given command is collected together before being
- * sent to the file.
- *
- * The CLI starts with cli_blocked and !cmd_in_progress, with the socket set
- * write on. The CLI progresses as follows:
- *
- * * on read_ready, if cli_blocked: do nothing.
+ * cli_blocked -- the CLI is unable to process any further keystrokes.
*
- * * on write_ready:
+ * cmd_in_progress -- a command has been dispatched and has not yet
+ * completed (may have been queued).
*
- * - empty out the CLI buffer
+ * cmd_out_enabled -- the command FIFO is may be emptied.
*
- * - if ! cmd_in_progress:
+ * This is set when a command completes, and cleared when
+ * everything is written away.
*
- * * empty out the command buffer
+ * cli_more_wait -- is in "--more--" wait state
*
- * * if the command buffer is empty, clear cli_blocked (if was set)
+ * The following are the valid combinations:
*
- * - generate a read_ready event unless write() would block.
+ * blkd : cip : o_en : m_wt :
+ * -----:------:------:------:--------------------------------------------
+ * 0 : 0 : 0 : 0 : collecting a new command
+ * 0 : 1 : 0 : 0 : command dispatched
+ * 1 : 1 : 0 : 0 : waiting for (queued) command to complete
+ * 1 : 0 : 1 : 0 : waiting for command output to complete
+ * 1 : 0 : 0 : 1 : waiting for "--more--" to be written away
+ * 0 : 0 : 0 : 1 : waiting for "--more--" response
+ * 1 : 1 : 1 : 0 : waiting for command to complete, after the
+ * CLI has been closed
*
- * So the CLI is kicked once all available output completes.
- *
- * * on read_ready, if !cli_blocked:
- *
- * - process keystrokes into command line under construction.
- *
- * - when required, dispatch the command:
- *
- * * set cmd_in_progress
- *
- * * execute the command -- which may generate output
+ * There are two output FIFOs:
*
- * * clear cmd_in_progress
+ * 1. for the CLI itself -- uty_cli_out and friends
*
- * * set cli_blocked
+ * 2. for output generated by commands -- vty_out and friends.
*
- * - set write on (or read on) as required.
+ * The CLI FIFO is emptied whenever possible, in preference to the command
+ * FIFO. The command FIFO is emptied when cmd_out_enabled. While
+ * cmd_in_progress is also !cmd_out_enabled -- so that all the output from a
+ * given command is collected together before being sent to the file.
*
* 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
@@ -203,13 +186,13 @@ uty_new_host_name(const char* name)
*
* This is largely buried in the output handling.
*
- * While cmd_in_progress is true, all output to the command output FIFO is held
- * there -- no actual write() is performed.
- *
- * When the command completes:
+ * While cmd_in_progress is true cmd_out_enabled will be false. When the
+ * command completes:
*
* * cmd_in_progress is cleared
*
+ * * cmd_out_enabled is set
+ *
* * cli_blocked will be set
*
* * the line_control structure is reset
@@ -218,10 +201,10 @@ uty_new_host_name(const char* name)
*
* 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 cmd_wait_more flag and its handling.)
+ * main CLI. (See the cli_more_wait flag and its handling.)
*
- * When all the output has completed, cli_blocked is cleared and the CLI will
- * be kicked.
+ * When all the output has completed the CLI will be kicked, which will see
+ * that the output buffer is now empty, and it can proceed.
*
* 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.
@@ -275,70 +258,191 @@ uty_new_host_name(const char* name)
*/
/*==============================================================================
- * Command Line Interface
+ * The CLI
+ */
+
+#define CONTROL(X) ((X) - '@')
+
+static void uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret) ;
+static enum vty_readiness uty_cli_standard(vty_io vio) ;
+static enum vty_readiness uty_cli_more_wait(vty_io vio) ;
+static void uty_cli_draw(vty_io vio) ;
+static void uty_cli_draw_this(vty_io vio, enum node_type node) ;
+static void uty_cli_wipe(vty_io vio, int len) ;
+
+static void uty_will_echo (vty_io vio) ;
+static void uty_will_suppress_go_ahead (vty_io vio) ;
+static void uty_dont_linemode (vty_io vio) ;
+static void uty_do_window_size (vty_io vio) ;
+static void uty_dont_lflow_ahead (vty_io vio) ;
+
+/*------------------------------------------------------------------------------
+ * Initialise CLI.
*
- * State of the CLI:
+ * It is assumed that the following have been initialised, empty or zero:
+ *
+ * cli_prompt_for_node
+ * cl
+ * clx
+ * cli_vbuf
+ * cli_obuf
*
- * cli_blocked -- a command has been dispatched, and now waiting
- * for it and/or its output to complete.
+ * cli_drawn
+ * cli_dirty
*
- * cmd_in_progress -- a command has been dispatched (and may have been
- * queued).
+ * cli_prompt_set
*
- * Can continue in the CLI until another command is
- * ready to be executed.
+ * cli_blocked
+ * cmd_in_progress
+ * cmd_out_enabled
+ * cli_wait_more
+ *
+ * cli_more_enabled
+ *
+ * Sets the CLI such that there is apparently a command in progress, so that
+ * further initialisation (in particular hello messages and the like) is
+ * treated as a "start up command".
+ *
+ * Sends a suitable set of Telnet commands to start the process.
+ */
+extern void
+uty_cli_init(vty_io vio)
+{
+ assert(vio->type == VTY_TERM) ;
+
+ vio->cmd_in_progress = 1 ;
+ vio->cli_blocked = 1 ;
+
+ vio->cli_do = cli_do_nothing ;
+
+ /* Setting up terminal. */
+ uty_will_echo (vio);
+ uty_will_suppress_go_ahead (vio);
+ uty_dont_linemode (vio);
+ uty_do_window_size (vio);
+ if (0)
+ uty_dont_lflow_ahead (vio) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Start the CLI.
+ *
+ * All start-up operations are complete -- so the "command" is now complete.
+ *
+ * Returns: write_ready -- so the first event is a write event, to flush
+ * any output to date.
+ */
+extern enum vty_readiness
+uty_cli_start(vty_io vio)
+{
+ uty_cli_cmd_complete(vio, CMD_SUCCESS) ;
+ return write_ready ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Close the CLI
+ *
+ * Note that if any command is revoked, then will clear cmd_in_progress and
+ * set cmd_out_enabled -- so any output can now clear.
+ */
+extern void
+uty_cli_close(vty_io vio)
+{
+ cq_revoke(vio->vty) ;
+
+ vio->cli_blocked = 1 ; /* don't attempt any more */
+ vio->cmd_out_enabled = 1 ; /* allow output to clear */
+} ;
+
+/*------------------------------------------------------------------------------
+ * CLI for VTY_TERM
+ *
+ * Do nothing at all if half closed.
+ *
+ * Otherwise do: standard CLI
+ * or: "--more--" CLI
+ *
+ * NB: on return, requires that an attempt is made to write away anything that
+ * may be ready for that.
+ */
+extern enum vty_readiness
+uty_cli(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
+
+ if (vio->half_closed)
+ return not_ready ; /* Nothing more if half closed */
+
+ /* Standard or "--more--" CLI ? */
+ if (vio->cli_more_wait)
+ return uty_cli_more_wait(vio) ;
+ else
+ return uty_cli_standard(vio) ;
+} ;
+
+/*==============================================================================
+ * The Standard CLI
*/
-static void uty_cli_draw(vty_io vio, enum node_type node) ;
static enum cli_do uty_cli_process(vty_io vio, enum node_type node) ;
static void uty_cli_response(vty_io vio, enum cli_do cli_do) ;
-static int uty_cli_dispatch(vty_io vio) ;
+static bool uty_cli_dispatch(vty_io vio) ;
/*------------------------------------------------------------------------------
- * Run the CLI until:
+ * Standard CLI for VTY_TERM -- if not blocked, runs until:
*
- * * becomes blocked
* * runs out of keystrokes
* * executes a command
*
- * When exits will (in general):
- *
- * * set write on if there is anything to be written or have something
- * in the keystroke stream to be processed.
- *
- * * set read on if does not set write on, if not yet hit EOF on the
- * keystroke stream.
- *
* Note that this executes at most one command each time it is called. This
* is to allow for a modicum of sharing of the system. For real keyboard input
* this will make no difference at all !
+ *
+ * Returns: not_ready blocked and was blocked when entered
+ * write_ready if there is anything in the keystroke stream
+ * read_ready otherwise
*/
-extern void
-uty_cli(vty_io vio)
+static enum vty_readiness
+uty_cli_standard(vty_io vio)
{
- bool won ;
-
VTY_ASSERT_LOCKED() ;
+ assert(vio->type == VTY_TERM) ;
/* cli_blocked is set when is waiting for a command, or its output to
- * complete.
+ * complete -- unless either of those has happened, is still blocked.
*
- * There is no good reason to arrive here in that state, and nothing to be
- * done if that happens.
+ * NB: in both these cases, assumes that other forces are at work to
+ * keep things moving.
*/
if (vio->cli_blocked)
- return ;
+ {
+ assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
- /* If there is something to do, that is because a previous command has
- * now completed, which may have wiped the pending command.
- *
- * If there is nothing pending, then can run the CLI until there is
+ if (vio->cmd_in_progress)
+ {
+ assert(!vio->cmd_out_enabled) ;
+ return not_ready ;
+ } ;
+
+ if (!vio_fifo_empty(&vio->cmd_obuf))
+ return not_ready ;
+
+ vio->cli_blocked = 0 ;
+ vio->cmd_out_enabled = 0 ;
+ } ;
+
+ /* If there is nothing pending, then can run the CLI until there is
* something to do, or runs out of input.
+ *
+ * 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 (vio->cli_do != cli_do_nothing)
- uty_cli_draw(vio, vio->vty->node) ;
- else
+ if (vio->cli_do == cli_do_nothing)
vio->cli_do = uty_cli_process(vio, vio->vty->node) ;
+ else
+ uty_cli_draw_this(vio, vio->vty->node) ;
/* If have something to do, do it. */
if (vio->cli_do != cli_do_nothing)
@@ -357,73 +461,21 @@ uty_cli(vty_io vio)
vio->cli_blocked = 1 ;
} ;
- /* If there is anything in the CLI output FIFO, must set write on to clear
- * it.
+ /* Use write_ready as a proxy for read_ready on the keystroke stream.
*
- * If there is anything in the command output FIFO *and* is !cmd_in_progress,
- * must set write on to clear it.
+ * Also, if the command line is not drawn, then return write_ready, so
+ * that
*
- * Otherwise: if cmd_in_progress, then the command buffer is not to be
- * output yet, and the command completion will kick things into action -- so
- * nothing further is required here.
- *
- * Otherwise: if there is anything in the command output buffer, must set
- * write on to clear it.
- *
- * Otherwise: if there is something in the keystroke stream, then must set
- * write on... the write_ready acts as a proxy for keystroke stream is ready.
- *
- * Otherwise: set read on -- so if further input arrives, will get on it.
+ * 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.
*/
- do
- {
- won = 1 ; /* generally the case */
-
- /* If have some CLI output -- do that */
- if (!vio_fifo_empty(&vio->cli_obuf))
- break ;
-
- /* If is blocked with command in progress -- do nothing.
- *
- * This state <=> that there is a queued command, and the CLI is now
- * blocked until the command completes. When it does, things will
- * progress.
- */
- if (vio->cli_blocked && vio->cmd_in_progress)
- {
- won = 0 ;
- break ;
- } ;
-
- /* If have some command output which is not held because there is
- * a command in progress -- do that.
- */
- if (!vio_fifo_empty(&vio->cmd_obuf) && !vio->cmd_in_progress)
- break ;
-
- /* If the keystroke buffer is not empty then write_ready is used as a
- * proxy for keystroke ready.
- */
- if (!keystroke_stream_empty(vio->key_stream))
- break ;
-
- /* Finally... if not at keystroke EOF, set read on */
- won = 0 ;
-
- if (!keystroke_stream_eof(vio->key_stream))
- uty_file_set_read(&vio->file, on) ;
-
- } while (0) ;
-
- if (won)
- uty_file_set_write(&vio->file, on) ;
+ if (keystroke_stream_empty(vio->key_stream))
+ return read_ready ;
+ else
+ return write_ready ;
} ;
-/* TODO: dealing with EOF and timeout and closing and such
- *
- * uty_cout (vty, "%sVty connection is timed out.%s", VTY_NEWLINE, VTY_NEWLINE);
- *
- */
/*------------------------------------------------------------------------------
* Dispatch the current vio->cli_do -- queueing it if necessary.
*
@@ -432,19 +484,19 @@ uty_cli(vty_io vio)
* Expects to be on new blank line, and when returns will be on new, blank
* line.
*
- * Returns: true <=> output is pending and command completed
- * false => no output pending or command was queued
+ * Returns: true <=> command completed and output is pending
+ * false => command has been queued and is now in progress
*
* Generally sets vio->cl_do = cli_do_nothing and clears vio->cl to empty.
*
* Can set vio->cl_do = and vio->cl to be a follow-on command.
*/
-static int
+static bool
uty_cli_dispatch(vty_io vio)
{
qstring_t tmp ;
enum cli_do cli_do ;
- int queued ;
+ enum cmd_return_code ret ;
struct vty* vty = vio->vty ;
@@ -455,7 +507,8 @@ uty_cli_dispatch(vty_io vio)
*
* Clear vio->cl and vio->cl_do.
*/
- vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
+ vio->cmd_in_progress = 1 ; /* => vty->buf is valid */
+ vio->cmd_out_enabled = 0 ; /* => collect all output */
tmp = vio->clx ; /* swap clx and cl */
vio->clx = vio->cl ;
@@ -466,18 +519,17 @@ uty_cli_dispatch(vty_io vio)
cli_do = vio->cli_do ; /* current operation */
vio->cli_do = cli_do_nothing ; /* clear */
- qs_set_empty(&vio->cl) ; /* set cl empty (with '\0') */
+ qs_clear(&vio->cl) ; /* set cl empty (with '\0') */
/* Reset the command output FIFO and line_control */
-
assert(vio_fifo_empty(&vio->cmd_obuf)) ;
- /* TODO: reset line_control */
+ uty_out_clear(vio) ; /* clears FIFO and line control */
/* Dispatch command */
if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
{
/* AUTH_NODE and AUTH_ENABLE_NODE are unique */
- queued = uty_auth(vty, vty->buf, cli_do) ;
+ ret = uty_auth(vty, vty->buf, cli_do) ;
}
else
{
@@ -488,23 +540,27 @@ uty_cli_dispatch(vty_io vio)
break ;
case cli_do_command:
- queued = uty_command(vty, vty->buf) ;
+ ret = uty_command(vty) ;
break ;
case cli_do_ctrl_c:
- queued = uty_stop_input(vty) ;
+ ret = uty_stop_input(vty) ;
break ;
case cli_do_ctrl_d:
- queued = uty_down_level(vty) ;
+ ret = uty_down_level(vty) ;
break ;
case cli_do_ctrl_z:
- queued = uty_command(vty, vty->buf) ;
- if (queued)
- vio->cli_do = cli_do_ctrl_z ;
+ ret = uty_command(vty) ;
+ if (ret == CMD_QUEUED)
+ vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
else
- queued = uty_end_config(vty) ;
+ ret = uty_end_config(vty) ; /* do the ^Z now */
+ break ;
+
+ case cli_do_eof:
+ ret = uty_cmd_close(vio->vty, "End") ;
break ;
default:
@@ -512,13 +568,16 @@ uty_cli_dispatch(vty_io vio)
} ;
} ;
- if (!queued)
+ if (ret == CMD_QUEUED)
+ {
+ uty_cli_draw(vio) ; /* draw the prompt */
+ return 0 ; /* command not complete */
+ }
+ else
{
- vio->cmd_in_progress = 0 ; /* command complete */
- vty->buf = NULL ; /* finished with command line */
+ uty_cli_cmd_complete(vio, ret) ;
+ return 1 ; /* command complete */
} ;
-
- return ! (vio->cmd_in_progress || vio_fifo_empty(&vio->cmd_obuf)) ;
} ;
/*------------------------------------------------------------------------------
@@ -528,7 +587,7 @@ uty_cli_dispatch(vty_io vio)
* or not... write_ready will kick read_ready.
*/
extern void
-vty_queued_result(struct vty *vty, int result, int action)
+vty_queued_result(struct vty *vty, enum cmd_return_code ret)
{
vty_io vio ;
@@ -536,20 +595,210 @@ vty_queued_result(struct vty *vty, int result, int action)
vio = vty->vio ;
- vio->cmd_in_progress = 0 ; /* command complete */
- vty->buf = NULL ; /* finished with command line */
+ if (!vio->closed)
+ {
+ uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
- vio->cli_blocked = !vio_fifo_empty(&vio->cmd_obuf) ;
- /* blocked if output is now pending */
+ /* Do the command completion actions that were deferred because the
+ * command was queued.
+ *
+ * Return of CMD_QUEUED => command was revoked before being executed.
+ * However interesting that might be... frankly don't care.
+ */
+ uty_cli_cmd_complete(vio, ret) ;
- uty_cli_wipe(vio) ; /* wipe any partly constructed line */
+ /* Kick the socket -- to write away any outstanding output, and
+ * re-enter the CLI when that's done.
+ */
+ uty_sock_set_readiness(&vio->sock, write_ready) ;
+ }
+ else
+ {
+ /* If the VTY is closed, the only reason it still exists is because
+ * there was cmd_in_progress.
+ */
+ vio->cmd_in_progress = 0 ;
+
+ uty_close(vio) ; /* Final close */
+ } ;
- uty_file_set_write(&vty->vio->file, on) ;
- /* flush any output -- which will do a
- * read_ready when all finished */
VTY_UNLOCK() ;
}
+/*------------------------------------------------------------------------------
+ * Command has completed, so:
+ *
+ * * clear cmd_in_progress
+ * * set cmd_out_enabled -- 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.
+ *
+ * It also means that the decision about whether there is anything to output
+ * is left to the output code.
+ */
+static void
+uty_cli_cmd_complete(vty_io vio, enum cmd_return_code ret)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cmd_in_progress && !vio->cmd_out_enabled) ;
+
+ if (ret == CMD_CLOSE)
+ uty_half_close(vio, NULL) ;
+
+ vio->cmd_in_progress = 0 ; /* command complete */
+ vio->cmd_out_enabled = 1 ; /* enable the output */
+ vio->cli_blocked = 1 ; /* now blocked waiting for output */
+
+ vio->vty->buf = NULL ; /* finished with command line */
+
+ uty_cmd_output_start(vio) ; /* reset line control (belt & braces) */
+} ;
+
+/*==============================================================================
+ * The "--more--" CLI
+ *
+ * While command output is being cleared from its FIFO, the CLI is cli_blocked.
+ *
+ * When the output side signals that "--more--" is required, it sets the
+ * cli_more_wait flag and clears the cmd_out_enabled flag.
+ *
+ * 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.
+ */
+
+/*------------------------------------------------------------------------------
+ * Change the CLI to the "--more--" CLI.
+ *
+ * Outputs the new prompt line.
+ */
+extern void
+uty_cli_go_more_wait(vty_io vio)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ assert(vio->cli_blocked && vio->cmd_out_enabled && !vio->cli_more_wait) ;
+
+ uty_cli_wipe(vio, 0) ; /* make absolutely sure that command line is
+ wiped before change the CLI state */
+
+ vio->cmd_out_enabled = 0 ; /* stop output pro tem */
+ vio->cli_more_wait = 1 ; /* new state */
+
+ uty_cli_draw(vio) ; /* draw the "--more--" */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Handle the "--more--" state.
+ *
+ * Deals with the first stage if cli_blocked.
+ *
+ * Tries to steal a keystroke, and when succeeds wipes the "--more--"
+ * prompt and exits cli_more_wait -- and may cancel all outstanding output.
+ *
+ * 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
+ */
+static enum vty_readiness
+uty_cli_more_wait(vty_io vio)
+{
+ struct keystroke steal ;
+
+ VTY_ASSERT_LOCKED() ;
+
+ /* Deal with the first stage of "--more--" */
+ if (vio->cli_blocked)
+ {
+ int get ;
+
+ /* 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(&vio->cli_obuf))
+ return not_ready ;
+
+ vio->cli_blocked = 0 ;
+
+ /* empty the input buffer into the keystroke stream */
+ do
+ {
+ get = uty_read(vio, NULL) ;
+ } while (get > 0) ;
+ } ;
+
+ /* Go through the "--more--" process, unless no longer write_open (!) */
+ if (vio->sock.write_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 error etc), returns knull_eof.
+ */
+ uty_read(vio, &steal) ;
+
+ /* 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(vio)) /* "--more--" if req. */
+ return write_ready ;
+ else
+ return read_ready ;
+ } ;
+
+ /* Stolen a keystroke -- a (very) few terminate all output */
+ if (steal.type == ks_char)
+ {
+ switch (steal.value)
+ {
+ case CONTROL('C'):
+ case 'q':
+ case 'Q':
+ uty_out_clear(vio) ;
+ break;
+
+ default: /* everything else, thrown away */
+ break ;
+ } ;
+ } ;
+ } ;
+
+ /* End of "--more--" process
+ *
+ * Wipe out the prompt and update state.
+ *
+ * Return write_ready to tidy up the screen and, unless cleared, write
+ * some more.
+ */
+ uty_cli_wipe(vio, 0) ;
+
+ vio->cli_blocked = 1 ; /* back to blocked waiting for output */
+ vio->cli_more_wait = 0 ; /* exit more_wait */
+ vio->cmd_out_enabled = 1 ; /* re-enable output */
+
+ return now_ready ;
+} ;
+
/*==============================================================================
* CLI VTY output
*
@@ -600,21 +849,17 @@ static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
* CLI VTY output -- cf fprintf()
*/
static void
-uty_cli_out (vty_io vio, const char *format, ...)
+uty_cli_out(vty_io vio, const char *format, ...)
{
VTY_ASSERT_LOCKED() ;
- if (vio->file.write_open)
+ if (vio->sock.write_open)
{
- va_list args;
- int len ;
+ va_list args ;
va_start (args, format);
- len = qs_vprintf(&vio->cli_vbuf, format, args) ;
+ vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
va_end(args);
-
- if (len > 0)
- uty_cli_write(vio, qs_chars(&vio->cli_vbuf), len) ;
} ;
} ;
@@ -624,11 +869,11 @@ uty_cli_out (vty_io vio, const char *format, ...)
* Do nothing if echo suppressed (eg in AUTH_NODE) or not write_open
*/
static void
-uty_cli_echo (vty_io vio, const char *this, size_t len)
+uty_cli_echo(vty_io vio, const char *this, size_t len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->file.write_open)
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
return ;
uty_cli_write(vio, this, len) ;
@@ -644,7 +889,7 @@ uty_cli_echo_n(vty_io vio, cli_rep_char chars, int n)
{
VTY_ASSERT_LOCKED() ;
- if (vio->cli_echo_suppress || !vio->file.write_open)
+ if (vio->cli_echo_suppress || !vio->sock.write_open)
return ;
uty_cli_write_n(vio, chars, n) ;
@@ -658,7 +903,7 @@ uty_cli_write(vty_io vio, const char *this, int len)
{
VTY_ASSERT_LOCKED() ;
- if (vio->file.write_open)
+ if (vio->sock.write_open)
vio_fifo_put(&vio->cli_obuf, this, len) ;
} ;
@@ -704,13 +949,14 @@ uty_cli_write_s(vty_io vio, const char *str)
/*------------------------------------------------------------------------------
* Send newline to the console.
*
- * Clears the cli_drawn flag.
+ * Clears the cli_drawn and the cli_dirty flags.
*/
static void
uty_cli_out_newline(vty_io vio)
{
uty_cli_write(vio, telnet_newline, 2) ;
vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
} ;
/*------------------------------------------------------------------------------
@@ -747,16 +993,18 @@ uty_cli_out_wipe_n(vty_io vio, int n)
static const char* cli_response [2][cli_do_count] =
{
{ /* when not waiting for previous command to complete */
- [cli_do_command] = "",
- [cli_do_ctrl_c] = "^C",
- [cli_do_ctrl_d] = "^D",
- [cli_do_ctrl_z] = "^Z",
+ [cli_do_command] = "",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
},
{ /* when waiting for a previous command to complete */
- [cli_do_command] = "^",
- [cli_do_ctrl_c] = "^C",
- [cli_do_ctrl_d] = "^D",
- [cli_do_ctrl_z] = "^Z",
+ [cli_do_command] = "^",
+ [cli_do_ctrl_c] = "^C",
+ [cli_do_ctrl_d] = "^D",
+ [cli_do_ctrl_z] = "^Z",
+ [cli_do_eof] = "^*"
}
} ;
@@ -766,7 +1014,7 @@ uty_cli_response(vty_io vio, enum cli_do cli_do)
const char* str ;
int len ;
- if (cli_do == cli_do_nothing)
+ if ((cli_do == cli_do_nothing) || (vio->half_closed))
return ;
str = (cli_do < cli_do_count)
@@ -811,36 +1059,72 @@ uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
/*------------------------------------------------------------------------------
* Wipe the current console line -- if any.
*/
-extern void
-uty_cli_wipe(vty_io vio)
+static void
+uty_cli_wipe(vty_io vio, int len)
{
int a ;
int b ;
- if (!vio->cli_drawn == 0)
+ if (!vio->cli_drawn)
return ; /* quit if already wiped */
assert(vio->cl.cp <= vio->cl.len) ;
- /* deal with echo suppression first */
- if (vio->cli_echo_suppress)
- {
- b = 0 ;
- a = 0 ;
- }
- else
+ /* Establish how much ahead and how much behind the cursor */
+ a = vio->cli_extra_len ;
+ b = vio->cli_prompt_len ;
+
+ if (!vio->cli_echo_suppress && !vio->cli_more_wait)
{
- b = vio->cl.cp ; /* behind cursor */
- a = vio->cl.len - b ; /* ahead of cursor */
- }
+ a += vio->cl.len - vio->cl.cp ;
+ b += vio->cl.cp ;
+ } ;
- /* Stuff ahead of the current position */
- uty_cli_out_wipe_n(vio, a + vio->cli_extra_len) ;
+ /* Stuff ahead of the current position if any ahead of new len */
+ if ((a + b) > len)
+ uty_cli_out_wipe_n(vio, +a) ;
- /* Stuff behind the current position */
- uty_cli_out_wipe_n(vio, vio->cli_prompt_len + b) ;
+ /* Stuff behind current position, but ahead of new len */
+ if (b > len)
+ {
+ uty_cli_out_wipe_n(vio, -(b - len)) ;
+ b = len ; /* moved the cursor back */
+ } ;
+
+ /* Back to the beginning of the line */
+ uty_cli_write_n(vio, telnet_backspaces, b) ;
+ /* Nothing there any more */
vio->cli_drawn = 0 ;
+ vio->cli_dirty = 0 ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * If not currently drawn, draw prompt etc according to the current state
+ * and node.
+ *
+ * See uty_cli_draw().
+ */
+extern bool
+uty_cli_draw_if_required(vty_io vio)
+{
+ if (vio->cli_drawn)
+ return false ;
+
+ uty_cli_draw(vio) ;
+
+ return true ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Draw prompt etc for the current vty node.
+ *
+ * See uty_cli_draw_this()
+ */
+static void
+uty_cli_draw(vty_io vio)
+{
+ uty_cli_draw_this(vio, vio->vty->node) ;
} ;
/*------------------------------------------------------------------------------
@@ -851,34 +1135,52 @@ uty_cli_wipe(vty_io vio)
*
* Otherwise, assumes is positioned at start of an empty line.
*
- * If there is a command queued, uses a dummy prompt -- because by the time
- * the command is executed, the node may have changed, so the current prompt
- * may be invalid.
+ * Draws prompt according to the given 'node', except:
+ *
+ * * if is half_closed, draw nothing -- wipes the current line
+ *
+ * * if is cli_more_wait, draw the "--more--" prompt
+ *
+ * * if is cmd_in_progress, 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)
*/
static void
-uty_cli_draw(vty_io vio, enum node_type node)
+uty_cli_draw_this(vty_io vio, enum node_type node)
{
const char* prompt ;
+ size_t l_len ;
+ int p_len ;
- if (vio->cli_drawn)
- uty_cli_wipe(vio) ;
-
- vio->cli_drawn = 1 ;
- vio->cli_extra_len = 0 ;
- vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+ if (vio->cli_dirty)
+ uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
/* Sort out what the prompt is. */
-
- if (vio->cmd_in_progress)
+ if (vio->half_closed)
+ {
+ prompt = "" ;
+ p_len = 0 ;
+ l_len = 0 ;
+ }
+ else if (vio->cli_more_wait)
+ {
+ prompt = "--more--" ;
+ p_len = strlen(prompt) ;
+ l_len = 0 ;
+ }
+ else if (vio->cmd_in_progress)
{
/* If there is a queued command, the prompt is a minimal affair. */
prompt = "~ " ;
- vio->cli_prompt_len = strlen(prompt) ;
+ p_len = strlen(prompt) ;
+ l_len = vio->cl.len ;
}
else
{
@@ -909,27 +1211,99 @@ uty_cli_draw(vty_io vio, enum node_type node)
vio->cli_prompt_set = 1 ;
} ;
- prompt = qs_chars(&vio->cli_prompt_for_node) ;
- vio->cli_prompt_len = vio->cli_prompt_for_node.len ;
+ prompt = vio->cli_prompt_for_node.body ;
+ p_len = vio->cli_prompt_for_node.len ;
+ l_len = vio->cl.len ;
} ;
- uty_cli_write(vio, prompt, vio->cli_prompt_len) ;
+ /* Now, if line is currently drawn, time to wipe it */
+ if (vio->cli_drawn)
+ uty_cli_wipe(vio, p_len + l_len) ;
+
+ /* Set about writing the prompt and the line */
+ vio->cli_drawn = 1 ;
+ vio->cli_extra_len = 0 ;
+ vio->cli_echo_suppress = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
+
+ vio->cli_prompt_len = p_len ;
+
+ uty_cli_write(vio, prompt, p_len) ;
- if ((vio->cl.len != 0) && !vio->cli_echo_suppress)
+ if (l_len != 0)
{
- uty_cli_write(vio, qs_chars(&vio->cl), vio->cl.len) ;
- if (vio->cl.cp < vio->cl.len)
- uty_cli_write_n(vio, telnet_backspaces, vio->cl.len - vio->cl.cp) ;
+ uty_cli_write(vio, qs_chars(&vio->cl), l_len) ;
+ if (vio->cl.cp < l_len)
+ uty_cli_write_n(vio, telnet_backspaces, l_len - vio->cl.cp) ;
} ;
} ;
/*==============================================================================
- * Command line processing loop
+ * 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 !
*/
-#define CONTROL(X) ((X) - '@')
+ /*-----------------------------------------------------------------------------
+ * Prepare for new monitor output line.
+ *
+ * Wipe any existing command line.
+ */
+extern void
+uty_cli_pre_monitor(vty_io vio, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ uty_cli_wipe(vio, len) ;
+} ;
-static void uty_telnet_command(vty_io vio, keystroke stroke) ;
+/*------------------------------------------------------------------------------
+ * 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_io vio, const char* buf, size_t len)
+{
+ VTY_ASSERT_LOCKED() ;
+
+ if (len != 0)
+ uty_cli_write(vio, buf, len) ;
+
+ if (vio->cli_more_wait || (vio->cl.len != 0))
+ {
+ uty_cli_draw(vio) ;
+ ++len ;
+ } ;
+
+ return len ;
+} ;
+
+/*==============================================================================
+ * Command line processing loop
+ */
+
+static bool uty_telnet_command(vty_io vio, keystroke stroke, bool callback) ;
static int uty_cli_insert (vty_io vio, const char* chars, int n) ;
static int uty_cli_overwrite (vty_io vio, char* chars, int n) ;
static int uty_cli_word_overwrite (vty_io vio, char *str) ;
@@ -999,10 +1373,13 @@ uty_cli_process(vty_io vio, enum node_type node)
while (1)
{
if (!vio->cli_drawn)
- uty_cli_draw(vio, node) ;
+ uty_cli_draw_this(vio, node) ;
if (!uty_cli_get_keystroke(vio, &stroke))
- break ;
+ {
+ ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
+ break ;
+ } ;
if (stroke.flags != 0)
{
@@ -1160,10 +1537,10 @@ uty_cli_process(vty_io vio, enum node_type node)
/* Telnet Command ----------------------------------------------------*/
case ks_iac:
- uty_telnet_command(vio, &stroke) ;
+ uty_telnet_command(vio, &stroke, false) ;
break ;
- /* Single byte escape ------------------------------------------------*/
+ /* Unknown -----------------------------------------------------------*/
default:
zabort("unknown keystroke type") ;
} ;
@@ -1185,95 +1562,6 @@ uty_cli_process(vty_io vio, enum node_type node)
} ;
/*==============================================================================
- * Support for the "--More--" handling
- */
-
-#define MSG_MORE_PROMPT "--more--"
-
-/*------------------------------------------------------------------------------
- * Output the "--more--" prompt and enter waiting for more state.
- *
- * As enters the waiting state, empties all the available input into the
- * keystroke FIFO... so doesn't treat anything that arrived before the
- * prompt was issued as a response to the prompt !
- *
- * Enables read -- now waiting for response to --more--
- *
- * NB: it is the caller's responsibility to clear the "--more--" prompt from
- * the CLI buffer.
- */
-extern void
-uty_cli_want_more(vty_io vio)
-{
- int get ;
-
- /* Empty the input buffers into the keystroke FIFO */
- do
- {
- get = uty_read(vio, NULL) ;
- } while (get > 0) ;
-
- /* Output the prompt and update the state */
- uty_cli_write_s(vio, MSG_MORE_PROMPT) ;
-
- vio->cmd_wait_more = 1 ;
-
- uty_file_set_read(&vio->file, on) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Waiting for response to "--more--" prompt.
- *
- * Steal the next available keystroke and process it. Error and EOF appear as
- * a null keystroke.
- *
- * Wipes the prompt and exits cli_wait_more state when has stolen keystroke.
- *
- * Enables write -- need to clear the prompt from the cli buffers and may
- * now continue command output, or signal it's done.
- */
-extern void
-uty_cli_wait_more(vty_io vio)
-{
- struct keystroke steal ;
-
- /* 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_read(vio, &steal) ;
-
- if ((steal.type == ks_null) || (steal.value != knull_eof))
- return ;
-
- /* Stolen a keystroke -- a (very) few terminate all output */
- if (steal.type == ks_char)
- {
- switch (steal.value)
- {
- case CONTROL('C'):
- case 'q':
- case 'Q':
- uty_out_discard(vio) ;
- break;
-
- default: /* evrything else, thrown away */
- break ;
- } ;
- } ;
-
- /* Wipe out the prompt and update state. */
- uty_cli_out_wipe_n(vio, - (int)strlen(MSG_MORE_PROMPT)) ;
-
- vio->cmd_wait_more = 0 ;
-
- uty_file_set_write(&vio->file, on) ;
-} ;
-
-/*==============================================================================
* Command line operations
*/
@@ -1289,7 +1577,7 @@ uty_cli_insert (vty_io vio, const char* chars, int n)
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.cp <= vio->cl.len)&& (n >= 0)) ;
+ assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
if (n <= 0)
return n ; /* avoid trouble */
@@ -1359,12 +1647,12 @@ uty_cli_word_overwrite (vty_io vio, char *str)
static int
uty_cli_forwards(vty_io vio, int n)
{
- int c ;
+ int have ;
VTY_ASSERT_LOCKED() ;
- c = vio->cl.len - vio->cl.cp ;
- if (n > c)
- n = c ;
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ;
assert(n >= 0) ;
@@ -1385,17 +1673,18 @@ uty_cli_forwards(vty_io vio, int n)
static int
uty_cli_backwards(vty_io vio, int n)
{
- int c ;
VTY_ASSERT_LOCKED() ;
- c = vio->cl.len - vio->cl.cp ;
- if (n > c)
- n = c ;
+ if ((int)vio->cl.cp < n)
+ n = vio->cl.cp ;
assert(n >= 0) ;
- uty_cli_echo_n(vio, telnet_backspaces, n) ;
- vio->cl.cp -= n ;
+ if (n > 0)
+ {
+ uty_cli_echo_n(vio, telnet_backspaces, n) ;
+ vio->cl.cp -= n ;
+ } ;
return n ;
}
@@ -1409,10 +1698,15 @@ static int
uty_cli_del_forwards(vty_io vio, int n)
{
int after ;
+ int have ;
VTY_ASSERT_LOCKED() ;
- assert((vio->cl.len - vio->cl.cp) && (n >= 0)) ;
+ have = vio->cl.len - vio->cl.cp ;
+ if (have < n)
+ n = have ; /* cannot delete more than have */
+
+ assert(n >= 0) ;
if (n <= 0)
return 0 ;
@@ -1425,8 +1719,6 @@ uty_cli_del_forwards(vty_io vio, int n)
uty_cli_echo_n(vio, telnet_spaces, n) ;
uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
- vio->cl.len -= n ;
-
return n ;
}
@@ -1660,31 +1952,18 @@ uty_cli_transpose_chars(vty_io vio)
extern void
uty_cli_hist_add (vty_io vio, const char* cmd_line)
{
- char* prev_line ;
- char* line ;
- char* e ;
+ qstring prev_line ;
+ qstring_t line ;
int prev_index ;
VTY_ASSERT_LOCKED() ;
+ /* Construct a dummy qstring for the given command line */
+ qs_dummy(&line, cmd_line, 1) ; /* set cursor to the end */
+
/* make sure have a suitable history vector */
vector_set_min_length(&vio->hist, VTY_MAXHIST) ;
- /* trim leading and trailing spaces before considering for history */
-
- while (*cmd_line == ' ')
- ++cmd_line ; /* hack off leading spaces */
-
- if (*cmd_line == '\0')
- return ; /* ignore empty lines */
-
- line = XSTRDUP(MTYPE_VTY_HIST, cmd_line) ;
-
- e = line + strlen(line) ;
- while (*(e - 1) == ' ')
- --e ;
- *e = '\0' ; /* hack off any trailing spaces */
-
/* find the previous command line in the history */
prev_index = vio->hindex - 1 ;
if (prev_index < 0)
@@ -1692,24 +1971,27 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
prev_line = vector_get_item(&vio->hist, prev_index) ;
- /* Insert unless same as previous line */
- if ((prev_line == NULL) || (strcmp(line, prev_line) != 0))
- {
- /* Insert history entry. */
- if (prev_line != NULL)
- XFREE (MTYPE_VTY_HIST, prev_line) ;
+ /* If the previous line is NULL, that means the history is empty.
+ *
+ * If the previous line is essentially the same as the current line,
+ * replace it with the current line -- so that the latest whitespace
+ * version is saved.
+ *
+ * Either way, replace the the previous line entry by moving hindex
+ * back !
+ */
+ if ((prev_line == NULL) || (qs_cmp_sig(prev_line, &line) == 0))
+ vio->hindex = prev_index ;
+ else
+ prev_line = vector_get_item(&vio->hist, vio->hindex) ;
- vector_set_item(&vio->hist, vio->hindex, line) ;
+ /* Now replace the hindex entry */
+ vector_set_item(&vio->hist, vio->hindex, qs_copy(prev_line, &line)) ;
- /* History index rotation. */
- vio->hindex++;
- if (vio->hindex == VTY_MAXHIST)
- vio->hindex = 0;
- }
- else
- {
- XFREE(MTYPE_VTY_HIST, line) ;
- } ;
+ /* Advance to the near future and reset the history pointer */
+ vio->hindex++;
+ if (vio->hindex == VTY_MAXHIST)
+ vio->hindex = 0;
vio->hp = vio->hindex;
} ;
@@ -1718,36 +2000,58 @@ uty_cli_hist_add (vty_io vio, const char* cmd_line)
* Replace command line by current history.
*
* This function is called from vty_next_line and vty_previous_line.
+ *
+ * Step +1 is towards the present
+ * -1 is into the past
*/
static void
uty_cli_history_use(vty_io vio, int step)
{
int index ;
- unsigned new_len ;
unsigned old_len ;
- char* hist ;
+ unsigned after ;
+ unsigned back ;
+ qstring hist ;
VTY_ASSERT_LOCKED() ;
- /* See if have anything usable */
+ assert((step == +1) || (step == -1)) ;
+
index = vio->hp ;
- if ((step > 0) && (index == vio->hindex))
- return ; /* cannot step forward past the insertion point */
+ /* Special case of being at the insertion point */
+ if (index == vio->hindex)
+ {
+ 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.
+ */
+ hist = vector_get_item(&vio->hist, vio->hindex) ;
+ vector_set_item(&vio->hist, vio->hindex, qs_copy(hist, &vio->cl)) ;
+ } ;
+
+ /* Advance or retreat */
index += step ;
if (index < 0)
index = VTY_MAXHIST - 1 ;
- else if (index >= VTY_MAXHIST) ;
+ else if (index >= VTY_MAXHIST)
index = 0 ;
- if ((step < 0) && (index == vio->hindex))
- return ; /* cannot step back to the insertion point */
-
hist = vector_get_item(&vio->hist, index) ;
- if (hist == NULL)
- return ; /* cannot step to unused entry */
+ /* If moving backwards in time, may not move back to the insertion
+ * 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 (step < 0)
+ if ((hist == NULL) || (index == vio->hindex))
+ return ;
+
+ /* Now, if arrived at the insertion point, this is returning to the
+ * present, which is fine.
+ */
vio->hp = index;
/* Move back to the start of the current line */
@@ -1755,13 +2059,26 @@ uty_cli_history_use(vty_io vio, int step)
/* Get previous line from history buffer and echo that */
old_len = vio->cl.len ;
- new_len = qs_set(&vio->cl, hist) ;
- vio->cl.cp = new_len ;
+ qs_copy(&vio->cl, hist) ;
+
+ /* Sort out wiping out any excess and setting the cursor position */
+ if (old_len > vio->cl.len)
+ after = old_len - vio->cl.len ;
+ else
+ after = 0 ;
- uty_cli_echo(vio, hist, new_len) ;
+ back = after ;
+ if (vio->cl.len > vio->cl.cp)
+ back += (vio->cl.len - vio->cl.cp) ;
+
+ if (vio->cl.len > 0)
+ uty_cli_echo(vio, vio->cl.body, vio->cl.len) ;
+
+ if (after > 0)
+ uty_cli_echo_n(vio, telnet_spaces, after) ;
- if (old_len < new_len)
- uty_cli_del_to_eol(vio) ;
+ if (back > 0)
+ uty_cli_echo_n(vio, telnet_backspaces, back) ;
return ;
} ;
@@ -1804,6 +2121,8 @@ uty_cli_complete_command (vty_io vio, enum node_type node)
{
unsigned i ;
int ret ;
+ int len ;
+ int n ;
vector matched ;
vector vline ;
@@ -1841,16 +2160,32 @@ uty_cli_complete_command (vty_io vio, enum node_type node)
break ;
case CMD_COMPLETE_LIST_MATCH:
+ len = 6 ;
for (i = 0; i < vector_end(matched); i++)
{
- if ((i % 6) == 0)
+ int sl = strlen((char*)vector_get_item(matched, i)) ;
+ if (len < sl)
+ len = sl ;
+ } ;
+
+ n = vio->width ;
+ if (n == 0)
+ n = 60 ;
+ n = n / (len + 2) ;
+ if (n == 0)
+ n = 1 ;
+
+ for (i = 0; i < vector_end(matched); i++)
+ {
+ if ((i % n) == 0)
uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out (vio, "%-10s ", (char*)vector_get_item(matched, i));
+ uty_cli_out(vio, "%-*s ", len, (char*)vector_get_item(matched, i));
}
+ uty_cli_out_newline(vio) ;
break;
- case CMD_ERR_NOTHING_TODO:
+ case CMD_COMPLETE_ALREADY:
default:
break;
} ;
@@ -2052,7 +2387,7 @@ uty_cli_cmd_prepare(vty_io vio, int help)
* VTY telnet stuff
*/
-#define TELNET_OPTION_DEBUG 1 /* 0 to turn off */
+#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
static const char* telnet_commands[256] =
{
@@ -2150,7 +2485,7 @@ uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
/*------------------------------------------------------------------------------
* Send telnet: "WILL TELOPT_ECHO"
*/
-extern void
+static void
uty_will_echo (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
@@ -2161,7 +2496,7 @@ uty_will_echo (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "suppress Go-Ahead"
*/
-extern void
+static void
uty_will_suppress_go_ahead (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
@@ -2172,7 +2507,7 @@ uty_will_suppress_go_ahead (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "don't use linemode"
*/
-extern void
+static void
uty_dont_linemode (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
@@ -2183,7 +2518,7 @@ uty_dont_linemode (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "Use window size"
*/
-extern void
+static void
uty_do_window_size (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
@@ -2194,7 +2529,7 @@ uty_do_window_size (vty_io vio)
/*------------------------------------------------------------------------------
* Send telnet: "don't use lflow" -- not currently used
*/
-extern void
+static void
uty_dont_lflow_ahead (vty_io vio)
{
unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
@@ -2203,20 +2538,42 @@ uty_dont_lflow_ahead (vty_io vio)
}
/*------------------------------------------------------------------------------
+ * The keystroke iac callback function.
+ *
+ * This deals with IAC sequences that should be dealt with as soon as they
+ * are read -- not stored in the keystroke stream for later processing.
+ */
+extern bool
+uty_cli_iac_callback(keystroke_iac_callback_args)
+{
+ return uty_telnet_command((vty_io)context, stroke, true) ;
+} ;
+
+/*------------------------------------------------------------------------------
* Process incoming Telnet Option(s)
*
+ * May be called during keystroke iac callback, or when processing CLI
+ * keystrokes.
+ *
* In particular: get telnet window size.
+ *
+ * Returns: true <=> dealt with, for:
+ *
+ * * telnet window size.
*/
-static void
-uty_telnet_command(vty_io vio, keystroke stroke)
+static bool
+uty_telnet_command(vty_io vio, keystroke stroke, bool callback)
{
uint8_t* p ;
uint8_t o ;
int left ;
+ bool dealt_with ;
/* Echo to the other end if required */
if (TELNET_OPTION_DEBUG)
{
+ uty_cli_wipe(vio, 0) ;
+
p = stroke->buf ;
left = stroke->len ;
@@ -2245,16 +2602,17 @@ uty_telnet_command(vty_io vio, keystroke stroke)
}
} ;
- if (!(stroke->flags & kf_broken))
+ if (stroke->flags & kf_broken)
uty_cli_out (vio, "BROKEN") ;
uty_cli_out (vio, "\r\n") ;
-
} ;
/* Process the telnet command */
+ dealt_with = false ;
+
if (stroke->flags != 0)
- return ; /* go no further if broken */
+ return dealt_with ; /* go no further if broken */
p = stroke->buf ;
left = stroke->len ;
@@ -2294,6 +2652,9 @@ uty_telnet_command(vty_io vio, keystroke stroke)
uty_cli_out(vio, "TELNET NAWS window size received: "
"width %d, height %d%s",
vio->width, vio->height, telnet_newline) ;
+ uty_set_height(vio) ;
+
+ dealt_with = true ;
} ;
break ;
@@ -2305,4 +2666,6 @@ uty_telnet_command(vty_io vio, keystroke stroke)
default: /* no other IAC X */
break ;
} ;
+
+ return dealt_with ;
} ;