summaryrefslogtreecommitdiffstats
path: root/lib/vty_pipe.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vty_pipe.c')
-rw-r--r--lib/vty_pipe.c2742
1 files changed, 185 insertions, 2557 deletions
diff --git a/lib/vty_pipe.c b/lib/vty_pipe.c
index 3276085c..ad86e30c 100644
--- a/lib/vty_pipe.c
+++ b/lib/vty_pipe.c
@@ -188,2681 +188,309 @@
*
*
*/
-
-/*==============================================================================
- *
- */
-
#if 0
-
-
-
-
-
-
-
-
-
-
-
-static char* vty_host_name = NULL ;
-int vty_host_name_set = 0 ;
-
-static void uty_new_host_name(const char* name) ;
-
-/*------------------------------------------------------------------------------
- * Update vty_host_name as per "hostname" or "no hostname" command
- */
-extern void
-uty_set_host_name(const char* name)
-{
- VTY_ASSERT_LOCKED() ;
-
- vty_host_name_set = (name != NULL) ;
-
- if (vty_host_name_set)
- uty_new_host_name(name) ;
- else
- uty_check_host_name() ;
-} ;
-
-/*------------------------------------------------------------------------------
- * If vty_host_name is set, free it.
- */
-extern void
-uty_free_host_name(void)
-{
- if (vty_host_name != NULL)
- XFREE (MTYPE_HOST, vty_host_name) ; /* sets vty_host_name = NULL */
-} ;
-
-/*------------------------------------------------------------------------------
- * If the host name is not set by command, see if the actual host name has
- * changed, and if so change it.
- *
- * This is done periodically in case the actual host name changes !
- */
-extern void
-uty_check_host_name(void)
-{
- struct utsname names ;
-
- VTY_ASSERT_LOCKED() ;
-
- if (vty_host_name_set)
- return ; /* nothing to do if set by command */
-
- uname (&names) ;
-
- if ((vty_host_name == NULL) || (strcmp(vty_host_name, names.nodename) != 0))
- uty_new_host_name(names.nodename) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Set new vty_host_name and run along list of VTYs to mark the change.
- */
-static void
-uty_new_host_name(const char* name)
-{
- vty_io vio ;
-
- VTY_ASSERT_LOCKED() ;
-
- uty_free_host_name() ;
- vty_host_name = XSTRDUP(MTYPE_HOST, name) ;
-
- vio = vio_list_base ;
- while (vio != NULL)
- {
- vio->cli_prompt_set = 0 ;
- vio = sdl_next(vio, vio_list) ;
- } ;
-} ;
-
/*==============================================================================
- * General mechanism for command execution.
- *
- * Command execution is driven by select/pselect -- which means that the
- * processing of commands is multiplexed with all other activity. In the
- * following:
- *
- * -- read_ready and write_ready are events signalled by select/pselect
- *
- * -- setting read or write on, means enabling the file for select/pselect to
- * consider it for read_ready or write_ready, respectively.
- *
- * State of the CLI:
- *
- * cli_blocked -- the CLI is unable to process any further keystrokes.
- *
- * cmd_in_progress -- a command has been dispatched and has not yet
- * completed (may have been queued).
- *
- * cmd_out_enabled -- the command FIFO is may be emptied.
- *
- * This is set when a command completes, and cleared when
- * everything is written away.
- *
- * cli_more_wait -- is in "--more--" wait state
- *
- * The following are the valid combinations:
- *
- * 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
- *
- * 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 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
- * invokes read_ready.
- *
- * Note also that after each command dispatch the CLI processor exits, to be
- * 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.
- *
- * 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
- *
- * * the output process is kicked off by setting write on
- *
- * 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 cli_more_wait flag and its handling.)
- *
- * 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.
- *
- * This mechanism means that the command output FIFO only ever contains the
- * output from the last command executed.
- *
- * If the user decides to abandon output at the "--more--" prompt, then the
- * contents of the command output FIFO are discarded.
- *
- *------------------------------------------------------------------------------
- * The qpthreads/qpnexus extension.
- *
- * When running in qnexus mode, many commands are not executed directly in the
- * CLI, but are queued for execution by the main "routeing" nexus.
- *
- * The parsing of commands is context sensitive. The context depends may
- * change during the execution of a command. So... it is not possible to
- * dispatch a command until the previous one has completed.
- *
- * In qnexus mode, when a command is queued, the CLI does not go cli_blocked,
- * even if some output has already been generated. This allows a further
- * command to be entered while the previous one is executed. However, if the
- * command is dispatched before the previous one completes, then the cli will
- * block.
- *
- * While the previous command is executing, the current command line has a
- * minimal prompt -- to show that the context is not known. When the previous
- * command completes, the command line is redrawn in the new context.
- *
- *------------------------------------------------------------------------------
- * Command line drawn state.
- *
- * When the cli_drawn flag is set, the current console line contains the
- * current prompt and the user input to date. The cursor is positioned where
- * the user last placed it.
- *
- * The command line can be "wiped" -- see uty_cli_wipe() -- which removes all
- * output and prompt, and leaves the console at the start of an empty line
- * where the command line used to be.
- *
- * On entry to the CLI, it will draw the command line again if it has been
- * wiped.
- *
- * This mechanism is used to support the partial command line that may be
- * entered while a queued command executes.
- *
- * It is also used for the command help/completion system.
- *
- * It is also used to support the "monitor" output.
- */
-
-/*==============================================================================
- * 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.
- *
- * It is assumed that the following have been initialised, empty or zero:
*
- * cli_prompt_for_node
- * cl
- * clx
- * cli_vbuf
- * cli_obuf
- *
- * cli_drawn
- * cli_dirty
- *
- * cli_prompt_set
- *
- * 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)
-{
- VTY_ASSERT_LOCKED() ;
- assert(vio->type == VTY_TERM) ;
-
- 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) ;
+#include "misc.h"
- if (vio->half_closed)
- return not_ready ; /* Nothing more if half closed */
+#include "command_local.h"
- /* Standard or "--more--" CLI ? */
- if (vio->cli_more_wait)
- return uty_cli_more_wait(vio) ;
- else
- return uty_cli_standard(vio) ;
-} ;
+#include "vty_io_pipe.h"
+#include "vty_io_file.h"
+#include "vty_io_basic.h"
/*==============================================================================
- * The Standard CLI
- */
-
-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 bool uty_cli_dispatch(vty_io vio) ;
-
-/*------------------------------------------------------------------------------
- * Standard CLI for VTY_TERM -- if not blocked, runs until:
- *
- * * runs out of keystrokes
- * * executes a command
- *
- * 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
- */
-static enum vty_readiness
-uty_cli_standard(vty_io vio)
-{
- VTY_ASSERT_LOCKED() ;
- assert(vio->type == VTY_TERM) ;
-
- /* cli_blocked is set when is waiting for a command, or its output to
- * complete -- unless either of those has happened, is still blocked.
- *
- * NB: in both these cases, assumes that other forces are at work to
- * keep things moving.
- */
- if (vio->cli_blocked)
- {
- assert(vio->cmd_in_progress || vio->cmd_out_enabled) ;
-
- 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)
- 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)
- {
- /* Reflect immediate response */
- uty_cli_response(vio, vio->cli_do) ;
-
- /* If command not already in progress, dispatch this one, which may
- * set the CLI blocked.
- *
- * Otherwise is now blocked until queued command completes.
- */
- if (!vio->cmd_in_progress)
- vio->cli_blocked = uty_cli_dispatch(vio) ;
- else
- vio->cli_blocked = 1 ;
- } ;
-
- /* Use write_ready as a proxy for read_ready on the keystroke stream.
- *
- * Also, if the command line is not drawn, then return write_ready, so
- * that
- *
- * 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 (keystroke_stream_empty(vio->key_stream))
- return read_ready ;
- else
- return write_ready ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Dispatch the current vio->cli_do -- queueing it if necessary.
- *
- * Requires that are NOT blocked and NO command is queued.
- *
- * Expects to be on new blank line, and when returns will be on new, blank
- * line.
- *
- * 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 bool
-uty_cli_dispatch(vty_io vio)
-{
- qstring_t tmp ;
- enum cli_do cli_do ;
- enum cmd_return_code ret ;
-
- struct vty* vty = vio->vty ;
-
- VTY_ASSERT_LOCKED() ;
- assert(!vio->cli_blocked && !vio->cmd_in_progress) ;
-
- /* Set vio->clx to the command about to execute.
- *
- * Clear vio->cl and vio->cl_do.
- */
- 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 ;
- vio->cl = tmp ;
-
- qs_term(&vio->clx) ; /* ensure string is terminated */
- vty->buf = qs_chars(&vio->clx) ; /* terminated command line */
- cli_do = vio->cli_do ; /* current operation */
-
- vio->cli_do = cli_do_nothing ; /* clear */
- qs_clear(vio->cl) ; /* set cl empty (with '\0') */
-
- /* Reset the command output FIFO and line_control */
- assert(vio_fifo_empty(&vio->cmd_obuf)) ;
- 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 */
- ret = uty_auth(vty, vty->buf, cli_do) ;
- }
- else
- {
- /* All other nodes... */
- switch (cli_do)
- {
- case cli_do_nothing:
- ret = CMD_SUCCESS ;
- break ;
-
- case cli_do_command:
- ret = uty_dispatch_command(vty) ;
- break ;
-
- case cli_do_ctrl_c:
- ret = uty_stop_input(vty) ;
- break ;
-
- case cli_do_ctrl_d:
- ret = uty_down_level(vty) ;
- break ;
-
- case cli_do_ctrl_z:
- ret = uty_command(vty) ;
- if (ret == CMD_QUEUED)
- vio->cli_do = cli_do_ctrl_z ; /* defer the ^Z action */
- else
- ret = uty_end_config(vty) ; /* do the ^Z now */
- break ;
-
- case cli_do_eof:
- ret = uty_cmd_close(vio->vty, "End") ;
- break ;
-
- default:
- zabort("unknown cli_do_xxx value") ;
- } ;
- } ;
-
- if (ret == CMD_QUEUED)
- {
- uty_cli_draw(vio) ; /* draw the prompt */
- return false ; /* command not complete */
- }
- else
- {
- uty_cli_cmd_complete(vio, ret) ;
- return true ; /* command complete */
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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.
- */
-extern void
-vty_queued_result(struct vty *vty, enum cmd_return_code ret)
-{
- vty_io vio ;
-
- VTY_LOCK() ;
-
- vio = vty->vio ;
-
- if (!vio->closed)
- {
- uty_cli_wipe(vio, 0) ; /* wipe any partly constructed line */
-
- /* 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) ;
-
- /* 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 */
- } ;
-
- 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.
+ * VTY File Output
*
- * This ensures some multiplexing at the command level.
+ * This is for input and output of configuration files and piped stuff.
*
- * It also means that the decision about whether there is anything to output
- * is left to the output code.
+ * When reading the configuration (and piped stuff in the configuration) I/O
+ * is blocking... nothing else can run while this is going on. Otherwise,
+ * all I/O is non-blocking.
*/
-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.
+ * Prototypes.
*/
-extern void
-uty_cli_enter_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--" */
-} ;
/*------------------------------------------------------------------------------
- * 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: !vio->cli_blocked (most of the time it will)
- * or: !vio_fifo_empty(&vio->cli_obuf)
- *
- * * 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_io vio)
+extern cmd_return_code_t
+uty_file_read_open(vty_io vio, qstring name, bool reflect)
{
- VTY_ASSERT_LOCKED() ;
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
- assert( (!vio->cli_blocked || !vio_fifo_empty(&vio->cli_obuf))
- && !vio->cmd_out_enabled && vio->cli_more_wait) ;
+ name_str = qs_make_string(name) ;
- uty_cli_wipe(vio, 0) ; /* wipe the prompt ('--more--')
- before changing the CLI state */
+ iot = vfd_io_read | vfd_io_blocking ; /* TODO blocking */
- 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 */
-} ;
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
-/*------------------------------------------------------------------------------
- * 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)
+ if (fd < 0)
{
- 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) ;
- } ;
+ uty_out(vio, "%% Could not open input file %s\n", name_str) ;
- /* 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_exit_more_wait(vio) ;
-
- return now_ready ;
-} ;
-
-/*==============================================================================
- * CLI VTY output
- *
- * This is buffered separately from the general (command) VTY output above.
- *
- * Has a dedicated buffer in the struct vty, which is flushed regularly during
- * command processing.
- *
- * 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.
- *
- * No actual I/O takes place here-- all "output" is to vio->cli_obuf
- * and/or vio->cli_ex_obuf
- *
- * The "cli_echo" functions discard the output if vio->cli_echo_suppress != 0.
- * 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 } ;
-
-typedef const char cli_rep_char[cli_rep_count] ;
-
-static const char telnet_backspaces[] =
- { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
- } ;
-CONFIRM(sizeof(telnet_backspaces) == sizeof(cli_rep_char)) ;
-
-static const char telnet_spaces[] =
- { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
- ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
- } ;
-CONFIRM(sizeof(telnet_spaces) == sizeof(cli_rep_char)) ;
+ return CMD_WARNING ;
+ }
-static const char* telnet_newline = "\r\n" ;
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vin_push(vio, vf, VIN_FILE, NULL, NULL, 16 * 1024) ;
-static void uty_cli_write(vty_io vio, const char *this, int len) ;
-static void uty_cli_write_n(vty_io vio, cli_rep_char chars, int n) ;
+ vf->parse_type = cmd_parse_strict ;
+ vf->reflect_enabled = reflect ;
-/*------------------------------------------------------------------------------
- * CLI VTY output -- cf fprintf()
- */
-static void
-uty_cli_out(vty_io vio, const char *format, ...)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->sock.write_open)
- {
- va_list args ;
-
- va_start (args, format);
- vio_fifo_vprintf(&vio->cli_obuf, format, args) ;
- va_end(args);
- } ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * 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_io vio, const char *this, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
-
- uty_cli_write(vio, 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_io vio, cli_rep_char chars, int n)
+extern cmd_return_code_t
+uty_file_write_open(vty_io vio, qstring name, bool append)
{
- VTY_ASSERT_LOCKED() ;
+ const char* name_str ;
+ int fd ;
+ vio_vf vf ;
+ vfd_io_type_t iot ;
- if (vio->cli_echo_suppress || !vio->sock.write_open)
- return ;
+ iot = vfd_io_write | vfd_io_blocking ; /* TODO blocking */
- uty_cli_write_n(vio, chars, n) ;
-}
+ if (append)
+ iot |= vfd_io_append ;
-/*------------------------------------------------------------------------------
- * CLI VTY output -- cf write()
- */
-inline static void
-uty_cli_write(vty_io vio, const char *this, int len)
-{
- VTY_ASSERT_LOCKED() ;
+ name_str = qs_make_string(name) ;
- if (vio->sock.write_open)
- vio_fifo_put(&vio->cli_obuf, this, len) ;
-} ;
+ /* Do the basic file open. */
+ fd = uty_vfd_file_open(name_str, iot) ;
-/*------------------------------------------------------------------------------
- * CLI VTY output -- write 'n' characters using a cli_rep_char string
- */
-static void
-uty_cli_write_n(vty_io vio, cli_rep_char chars, int n)
-{
- int len ;
-
- len = sizeof(cli_rep_char) ;
- while (n > 0)
+ if (fd < 0)
{
- if (n < len)
- len = n ;
- uty_cli_write(vio, chars, len) ;
- n -= len ;
- } ;
-} ;
+ uty_out(vio, "%% Could not open output file %s\n", name_str) ;
-/*------------------------------------------------------------------------------
- * CLI VTY output -- write given string
- *
- * Returns length of string.
- */
-static int
-uty_cli_write_s(vty_io vio, const char *str)
-{
- int len ;
+ return CMD_WARNING ;
+ }
- len = strlen(str) ;
- if (len != 0)
- uty_cli_write(vio, str, len) ;
+ /* OK -- now push the new input onto the vin_stack. */
+ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ;
+ uty_vout_push(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ;
- return len ;
+ return CMD_SUCCESS ;
} ;
/*==============================================================================
- * Standard Messages
- */
-
-/*------------------------------------------------------------------------------
- * Send newline to the console.
+ * Command line fetch from a file or pipe.
*
- * Clears the cli_drawn and the cli_dirty flags.
+ * Returns: CMD_SUCCESS -- have another command line ready to go
+ * CMD_WAITING -- do not have a command line at the moment
+ * CMD_EOF -- ran into EOF
+ * CMD_IO_ERROR -- ran into an I/O error
*/
-static void
-uty_cli_out_newline(vty_io vio)
+extern cmd_return_code_t
+uty_file_fetch_command_line(vio_vf vf, qstring* line)
{
- uty_cli_write(vio, telnet_newline, 2) ;
- vio->cli_drawn = 0 ;
- vio->cli_dirty = 0 ;
-} ;
+ assert(vf->vin_state == vf_open) ;
-/*------------------------------------------------------------------------------
- * Wipe 'n' characters.
- *
- * If 'n' < 0, wipes characters backwards and moves cursor back.
- * 'n' > 0, wipes characters forwards, leaving cursor where it is
- */
-static void
-uty_cli_out_wipe_n(vty_io vio, int n)
-{
- if (n < 0)
+ if (vf->line_complete)
{
- n = abs(n) ;
- uty_cli_write_n(vio, telnet_backspaces, n);
- } ;
-
- if (n > 0)
- {
- uty_cli_write_n(vio, telnet_spaces, n) ;
- uty_cli_write_n(vio, telnet_backspaces, n) ;
- } ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Send response to the given cli_do
- *
- * If no command is in progress, then will send newline to signal that the
- * command is about to be dispatched.
- *
- * If command is in progress, then leaves cursor on '^' to signal that is now
- * waiting for previous command to complete.
- */
-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_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_eof] = "^*"
- }
-} ;
-
-static void
-uty_cli_response(vty_io vio, enum cli_do cli_do)
-{
- const char* str ;
- int len ;
-
- if ((cli_do == cli_do_nothing) || (vio->half_closed))
- return ;
+ vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */
- str = (cli_do < cli_do_count)
- ? cli_response[vio->cmd_in_progress ? 1 : 0][cli_do] : NULL ;
- assert(str != NULL) ;
+ vf->line_complete = false ;
+ vf->line_number += vf->line_step ;
- len = uty_cli_write_s(vio, str) ;
-
- if (vio->cmd_in_progress)
- {
- vio->cli_extra_len = len ;
- uty_cli_write_n(vio, telnet_backspaces, len) ;
- }
- else
- {
- uty_cli_out_newline(vio) ;
+ qs_set_len_nn(vf->cl, 0) ;
+ vf->line_step = 0 ;
} ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Send various messages with trailing newline.
- */
-static void
-uty_cli_out_CMD_ERR_AMBIGUOUS(vty_io vio)
-{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_AMBIGUOUS ".") ;
- uty_cli_out_newline(vio) ;
-} ;
-
-static void
-uty_cli_out_CMD_ERR_NO_MATCH(vty_io vio)
-{
- uty_cli_write_s(vio, "% " MSG_CMD_ERR_NO_MATCH ".") ;
- uty_cli_out_newline(vio) ;
-} ;
-
-/*==============================================================================
- * Command line draw and wipe
- */
-
-/*------------------------------------------------------------------------------
- * Wipe the current console line -- if any.
- */
-static void
-uty_cli_wipe(vty_io vio, int len)
-{
- int a ;
- int b ;
-
- if (!vio->cli_drawn)
- return ; /* quit if already wiped */
-
- assert(vio->cl.cp <= vio->cl.len) ;
-
- /* 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)
- {
- a += vio->cl.len - vio->cl.cp ;
- b += vio->cl.cp ;
- } ;
-
- /* Wipe anything ahead of the current position and ahead of new len */
- if ((a + b) > len)
- uty_cli_out_wipe_n(vio, +a) ;
-
- /* Wipe anything behind current position, but ahead of new len */
- if (b > len)
+ while (1)
{
- uty_cli_out_wipe_n(vio, -(b - len)) ;
- b = len ; /* moved the cursor back */
- } ;
+ char* s, * p, * q, * e ;
+ size_t have ;
+ ulen len ;
- /* Back to the beginning of the line */
- uty_cli_write_n(vio, telnet_backspaces, b) ;
+ s = vio_fifo_get(vf->ibuf, &have) ;
- /* Nothing there any more */
- vio->cli_drawn = 0 ;
- vio->cli_dirty = 0 ;
-} ;
+ /* If nothing in hand, try and get some more.
+ *
+ * Either exits or loops back to set s & have.
+ */
+ if (have == 0)
+ {
+ int get ;
-/*------------------------------------------------------------------------------
- * 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 ;
+ get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ;
- uty_cli_draw(vio) ;
+ if (get > 0)
+ continue ; /* loop back */
- return true ;
-} ;
+ if (get == 0)
+ return CMD_WAITING ; /* need to set read ready ! */
-/*------------------------------------------------------------------------------
- * 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) ;
-} ;
+ if (get == -1)
+ ; /* register error */
-/*------------------------------------------------------------------------------
- * Draw prompt and entire command line, leaving current position where it
- * should be.
- *
- * If command line is currently drawn, this wipes and redraws.
- *
- * Otherwise, assumes is positioned at start of an empty line.
- *
- * 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_this(vty_io vio, enum node_type node)
-{
- const char* prompt ;
- size_t l_len ;
- int p_len ;
-
- if (vio->cli_dirty)
- uty_cli_out_newline(vio) ; /* clears cli_dirty and cli_drawn */
+ if (get == -2)
+ return (qs_len_nn(vf->cl) > 0) ? CMD_SUCCESS : CMD_EOF ;
+ } ;
- /* Sort out what the prompt is. */
- 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 = "~ " ;
- p_len = strlen(prompt) ;
- l_len = vio->cl.len ;
- }
- else
- {
- /* The prompt depends on the node, and is expected to include the
- * host name.
+ /* Try to find a '\n' -- converting all other control chars to ' '
*
- * Caches the prompt so doesn't re-evaluate it every time.
+ * When we find '\n' step back across any trailing ' ' (which includes
+ * any control chars before the '\n').
*
- * If the host name changes, the cli_prompt_set flag is cleared.
+ * This means that we cope with "\r\n" line terminators. But not anything
+ * more exotic.
*/
- if (!vio->cli_prompt_set || (node != vio->cli_prompt_node))
- {
- const char* prompt ;
-
- if (vty_host_name == NULL)
- uty_check_host_name() ; /* should never be required */
+ p = s ;
+ e = s + have ; /* have != 0 */
+ q = NULL ;
- prompt = cmd_prompt(node) ;
- if (prompt == NULL)
+ while (p < e)
+ {
+ if (*p++ < 0x20)
{
- zlog_err("vty %s has node %d", uty_get_name(vio), node) ;
- prompt = "%s ???: " ;
- } ;
+ if (*(p-1) != '\n')
+ {
+ *(p-1) = ' ' ; /* everything other than '\n' */
+ continue ;
+ } ;
- qs_printf(vio->cli_prompt_for_node, prompt, vty_host_name);
+ ++vf->line_step ; /* got a '\n' */
- vio->cli_prompt_node = node ;
- vio->cli_prompt_set = 1 ;
+ q = p ; /* point just past '\n' */
+ do --q ; while ((q > s) && (*(q-1) == ' ')) ;
+ /* discard trailing "spaces" */
+ break ;
+ } ;
} ;
- prompt = vio->cli_prompt_for_node.body ;
- p_len = vio->cli_prompt_for_node.len ;
- l_len = vio->cl.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 (l_len != 0)
- {
- 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) ;
- } ;
-} ;
-
-/*==============================================================================
- * 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_io vio, size_t len)
-{
- VTY_ASSERT_LOCKED() ;
-
- uty_cli_wipe(vio, 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_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) ;
-static int uty_cli_forwards(vty_io vio, int n) ;
-static int uty_cli_backwards(vty_io vio, int n) ;
-static int uty_cli_del_forwards(vty_io vio, int n) ;
-static int uty_cli_del_backwards(vty_io vio, int n) ;
-static int uty_cli_bol (vty_io vio) ;
-static int uty_cli_eol (vty_io vio) ;
-static int uty_cli_word_forwards_delta(vty_io vio) ;
-static int uty_cli_word_forwards(vty_io vio) ;
-static int uty_cli_word_backwards_delta(vty_io vio, int eat_spaces) ;
-static int uty_cli_word_backwards_pure (vty_io vio) ;
-static int uty_cli_word_backwards (vty_io vio) ;
-static int uty_cli_del_word_forwards(vty_io vio) ;
-static int uty_cli_del_word_backwards(vty_io vio) ;
-static int uty_cli_del_to_eol (vty_io vio) ;
-static int uty_cli_clear_line(vty_io vio) ;
-static int uty_cli_transpose_chars(vty_io vio) ;
-static void uty_cli_history_use(vty_io vio, int step) ;
-static void uty_cli_next_line(vty_io vio) ;
-static void uty_cli_previous_line (vty_io vio) ;
-static void uty_cli_complete_command (vty_io vio, enum node_type node) ;
-static void uty_cli_describe_command (vty_io vio, enum node_type node) ;
-
-/*------------------------------------------------------------------------------
- * Fetch next keystroke, reading from the file if required.
- */
-static inline bool
-uty_cli_get_keystroke(vty_io vio, keystroke stroke)
-{
- if (keystroke_get(vio->key_stream, stroke))
- return 1 ;
-
- uty_read(vio, NULL) ; /* not stealing */
-
- return keystroke_get(vio->key_stream, stroke) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Process keystrokes until run out of input, or get something to cli_do.
- *
- * If required, draw the prompt and command line.
- *
- * Process keystrokes until run out of stuff to do, or have a "command line"
- * that must now be executed.
- *
- * Processes the contents of the keystroke stream. If exhausts that, will set
- * ready to read and return. (To give some "sharing".)
- *
- * Returns: cli_do_xxxx
- *
- * When returns the cl is '\0' terminated.
- */
-static enum cli_do
-uty_cli_process(vty_io vio, enum node_type node)
-{
- struct keystroke stroke ;
- uint8_t u ;
- int auth ;
- enum cli_do ret ;
-
- auth = (node == AUTH_NODE || node == AUTH_ENABLE_NODE) ;
-
- /* Now process as much as possible of what there is */
- ret = cli_do_nothing ;
- while (1)
- {
- if (!vio->cli_drawn)
- uty_cli_draw_this(vio, node) ;
-
- if (!uty_cli_get_keystroke(vio, &stroke))
- {
- ret = (stroke.value == knull_eof) ? cli_do_eof : cli_do_nothing ;
- break ;
- } ;
+ /* Step past what have just consumed -- we have a hold_mark, so
+ * stuff is still in the fifo.
+ */
+ vio_fifo_step(vf->ibuf, p - s) ;
- if (stroke.flags != 0)
+ /* If not found '\n', then we have a line fragment that needs to be
+ * appended to any previous line fragments.
+ *
+ * Loops back to try to get some more form the fifo.
+ */
+ if (q == NULL)
{
- /* TODO: deal with broken keystrokes */
+ qs_append_str_n(vf->cl, s, p - s) ;
continue ;
} ;
- 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('A'):
- uty_cli_bol (vio);
- break;
-
- case CONTROL('B'):
- uty_cli_backwards(vio, 1);
- break;
-
- case CONTROL('C'):
- ret = cli_do_ctrl_c ; /* Exit on ^C ..................*/
- break ;
-
- case CONTROL('D'):
- if (vio->cl.len == 0) /* if at start of empty line */
- ret = cli_do_ctrl_d ; /* Exit on ^D ..................*/
- else
- uty_cli_del_forwards(vio, 1);
- break;
-
- case CONTROL('E'):
- uty_cli_eol (vio);
- break;
-
- case CONTROL('F'):
- uty_cli_forwards(vio, 1);
- break;
-
- case CONTROL('H'):
- case 0x7f:
- uty_cli_del_backwards(vio, 1);
- break;
-
- case CONTROL('K'):
- uty_cli_del_to_eol (vio);
- break;
-
- case CONTROL('N'):
- uty_cli_next_line (vio);
- break;
-
- case CONTROL('P'):
- uty_cli_previous_line (vio);
- break;
-
- case CONTROL('T'):
- uty_cli_transpose_chars (vio);
- break;
-
- case CONTROL('U'):
- uty_cli_clear_line(vio);
- break;
-
- case CONTROL('W'):
- uty_cli_del_word_backwards (vio);
- break;
-
- case CONTROL('Z'):
- ret = cli_do_ctrl_z ; /* Exit on ^Z ..................*/
- break;
-
- case '\n':
- case '\r':
- ret = cli_do_command ; /* Exit on CR or LF.............*/
- break ;
-
- case '\t':
- if (auth)
- break ;
- else
- uty_cli_complete_command (vio, node);
- break;
-
- case '?':
- if (auth)
- uty_cli_insert (vio, (char*)&u, 1);
- else
- uty_cli_describe_command (vio, node);
- break;
-
- default:
- if ((stroke.value >= 0x20) && (stroke.value < 0x7F))
- uty_cli_insert (vio, (char*)&u, 1) ;
- break;
- }
- break ;
-
- /* ESC X -------------------------------------------------------------*/
- case ks_esc:
- switch (stroke.value)
- {
- case 'b':
- uty_cli_word_backwards (vio);
- break;
-
- case 'f':
- uty_cli_word_forwards (vio);
- break;
-
- case 'd':
- uty_cli_del_word_forwards (vio);
- break;
-
- case CONTROL('H'):
- case 0x7f:
- uty_cli_del_word_backwards (vio);
- break;
-
- default:
- break;
- } ;
- break ;
-
- /* ESC [ X -----------------------------------------------------------*/
- case ks_csi:
- if (stroke.len != 0)
- break ; /* only recognise 3 byte sequences */
-
- switch (stroke.value)
- {
- case ('A'):
- uty_cli_previous_line (vio);
- break;
-
- case ('B'):
- uty_cli_next_line (vio);
- break;
-
- case ('C'):
- uty_cli_forwards(vio, 1);
- break;
-
- case ('D'):
- uty_cli_backwards(vio, 1);
- break;
- default:
- break ;
- } ;
- break ;
-
- /* Telnet Command ----------------------------------------------------*/
- case ks_iac:
- uty_telnet_command(vio, &stroke, false) ;
- break ;
-
- /* Unknown -----------------------------------------------------------*/
- default:
- zabort("unknown keystroke type") ;
- } ;
-
- /* After each keystroke..... */
-
- if (ret != cli_do_nothing)
+ /* If we have nothing so far, set alias to point at what we have in
+ * the fifo. Otherwise, append to what we have.
+ *
+ * End up with: s = start of entire line, so far.
+ * p = end of entire line so far.
+ */
+ len = q - s ; /* length to add */
+ if (qs_len_nn(vf->cl) == 0)
{
- uty_cli_eol (vio) ; /* go to the end of the line */
- break ; /* stop processing */
- } ;
- } ;
-
- /* Tidy up and return where got to. */
-
- qs_term(&vio->cl) ; /* add '\0' */
-
- return ret ;
-} ;
-
-/*==============================================================================
- * Command line operations
- */
-
-/*------------------------------------------------------------------------------
- * Insert 'n' characters at current position in the command line
- *
- * Returns number of characters inserted -- ie 'n'
- */
-static int
-uty_cli_insert (vty_io vio, const char* chars, int n)
-{
- int after ;
-
- VTY_ASSERT_LOCKED() ;
-
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
-
- if (n <= 0)
- return n ; /* avoid trouble */
-
- after = qs_insert(vio->cl, chars, n) ;
-
- uty_cli_echo(vio, qs_cp_char(vio->cl), after) ;
-
- if ((after - n) != 0)
- uty_cli_echo_n(vio, telnet_backspaces, after - n) ;
-
- vio->cl.cp += n ;
-
- return n ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Overstrike 'n' characters at current position in the command line
- *
- * Move current position forwards.
- *
- * Returns number of characters inserted -- ie 'n'
- */
-static int
-uty_cli_overwrite (vty_io vio, char* chars, int n)
-{
- VTY_ASSERT_LOCKED() ;
-
- assert((vio->cl.cp <= vio->cl.len) && (n >= 0)) ;
-
- if (n > 0)
- {
- qs_replace(vio->cl, n, chars, n) ;
- uty_cli_echo(vio, chars, n) ;
-
- vio->cl.cp += n ;
- } ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Insert a word into vty interface with overwrite mode.
- *
- * NB: Assumes result will then be the end of the line.
- *
- * Returns number of characters inserted -- ie length of string
- */
-static int
-uty_cli_word_overwrite (vty_io vio, char *str)
-{
- int n ;
- VTY_ASSERT_LOCKED() ;
-
- n = uty_cli_overwrite(vio, str, strlen(str)) ;
-
- vio->cl.len = vio->cl.cp ;
-
- return n ;
-}
+ qs_set_alias_n(vf->cl, s, len) ;
+ p = q ;
+ }
+ else
+ {
+ if (len != 0)
+ qs_append_str_n(vf->cl, s, len) ;
-/*------------------------------------------------------------------------------
- * Forward 'n' characters -- stop at end of line.
- *
- * Returns number of characters actually moved
- */
-static int
-uty_cli_forwards(vty_io vio, int n)
-{
- int have ;
- VTY_ASSERT_LOCKED() ;
+ s = qs_char_nn(vf->cl) ;
+ p = s + qs_len_nn(vf->cl) ;
- have = vio->cl.len - vio->cl.cp ;
- if (have < n)
- n = have ;
+ if ((len == 0) && (p > s) && (*(p-1) == ' '))
+ {
+ /* Have an empty end of line section, and the last character
+ * of what we have so far is ' ', so need now to trim trailing
+ * spaces off the stored stuff.
+ */
+ do --p ; while ((p > s) && (*(p-1) == ' ')) ;
- assert(n >= 0) ;
+ qs_set_len_nn(vf->cl, p - s) ;
+ } ;
+ } ;
- if (n > 0)
- {
- uty_cli_echo(vio, qs_cp_char(vio->cl), n) ;
- vio->cl.cp += n ;
- } ;
+ /* Now worry about we have a trailing '\'. */
- return n ;
-}
+ if ((p == s) || (*(p-1) != '\\'))
+ break ; /* no \ => no continuation => success */
-/*------------------------------------------------------------------------------
- * Backwards 'n' characters -- stop at start of line.
- *
- * Returns number of characters actually moved
- */
-static int
-uty_cli_backwards(vty_io vio, int n)
-{
- VTY_ASSERT_LOCKED() ;
+ /* Have a trailing '\'.
+ *
+ * If there are an odd number of '\', strip the last one and loop
+ * round to collect the continuation.
+ *
+ * If there are an even number of '\', then this is not a continuation.
+ *
+ * Note that this rule deals with the case of the continuation line
+ * being empty... e.g. ....\\\ n n -- where n is '\n'
+ */
+ q = p ;
+ do --q ; while ((q > s) && (*(q-1) == '\\')) ;
- if ((int)vio->cl.cp < n)
- n = vio->cl.cp ;
+ if (((p - q) & 1) == 0)
+ break ; /* even => no continuation => success */
- assert(n >= 0) ;
+ qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */
- if (n > 0)
- {
- uty_cli_echo_n(vio, telnet_backspaces, n) ;
- vio->cl.cp -= n ;
+ continue ; /* loop back to fetch more */
} ;
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Delete 'n' characters -- forwards -- stop at end of line.
- *
- * Returns number of characters actually deleted.
- */
-static int
-uty_cli_del_forwards(vty_io vio, int n)
-{
- int after ;
- int have ;
-
- VTY_ASSERT_LOCKED() ;
+ /* Success have a line in hand */
- have = vio->cl.len - vio->cl.cp ;
- if (have < n)
- n = have ; /* cannot delete more than have */
+ vf->line_complete = true ;
+ *line = vf->cl ;
- assert(n >= 0) ;
-
- if (n <= 0)
- return 0 ;
-
- after = qs_delete(vio->cl, n) ;
-
- if (after > 0)
- uty_cli_echo(vio, qs_cp_char(vio->cl), after) ;
-
- uty_cli_echo_n(vio, telnet_spaces, n) ;
- uty_cli_echo_n(vio, telnet_backspaces, after + n) ;
-
- return n ;
-}
-
-/*------------------------------------------------------------------------------
- * Delete 'n' characters before the point.
- *
- * Returns number of characters actually deleted.
- */
-static int
-uty_cli_del_backwards(vty_io vio, int n)
-{
- return uty_cli_del_forwards(vio, uty_cli_backwards(vio, n)) ;
-}
-
-/*------------------------------------------------------------------------------
- * Move to the beginning of the line.
- *
- * Returns number of characters moved over.
- */
-static int
-uty_cli_bol (vty_io vio)
-{
- return uty_cli_backwards(vio, vio->cl.cp) ;
+ return CMD_SUCCESS ;
} ;
/*------------------------------------------------------------------------------
- * Move to the end of the line.
+ * Command output push to a file or pipe.
*
- * Returns number of characters moved over
+ * Returns: CMD_SUCCESS -- done
+ * CMD_IO_ERROR -- ran into an I/O error
*/
-static int
-uty_cli_eol (vty_io vio)
+extern cmd_return_code_t
+uty_file_out_push(vio_vf vf)
{
- return uty_cli_forwards(vio, vio->cl.len - vio->cl.cp) ;
-} ;
+ assert(vf->vout_state == vf_open) ;
-/*------------------------------------------------------------------------------
- * Forward word delta -- distance to start of next word.
- *
- * Return number of characters to step over to reach next word.
- *
- * Steps over non-space characters and then any spaces.
- */
-static int
-uty_cli_word_forwards_delta(vty_io vio)
-{
- char* cp ;
- char* tp ;
- char* ep ;
-
- VTY_ASSERT_LOCKED() ; ;
-
- assert(vio->cl.cp <= vio->cl.len) ;
-
- cp = qs_cp_char(vio->cl) ;
- ep = qs_ep_char(vio->cl) ;
-
- tp = cp ;
-
- while ((tp < ep) && (*tp != ' '))
- ++tp ;
-
- while ((tp < ep) && (*tp == ' '))
- ++tp ;
-
- return tp - cp ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Forward word -- move to start of next word.
- *
- * Moves past any non-spaces, then past any spaces.
- */
-static int
-uty_cli_word_forwards(vty_io vio)
-{
- return uty_cli_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Backward word delta -- distance to start of next word, back.
- *
- * Return number of characters to step over to reach next word.
- *
- * If "eat_spaces", starts by stepping over spaces.
- * Steps back until next (backwards) character is space, or hits start of line.
- */
-static int
-uty_cli_word_backwards_delta(vty_io vio, int eat_spaces)
-{
- char* cp ;
- char* tp ;
- char* sp ;
-
- VTY_ASSERT_LOCKED() ; ;
-
- assert(vio->cl.cp <= vio->cl.len) ;
-
- cp = qs_cp_char(vio->cl) ;
- sp = qs_chars(vio->cl) ;
-
- tp = cp ;
-
- if (eat_spaces)
- while ((tp > sp) && (*(tp - 1) == ' '))
- --tp ;
-
- while ((tp > sp) && (*(tp - 1) != ' '))
- --tp ;
-
- return cp - tp ;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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_io vio)
-{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 0)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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.
- */
-static int
-uty_cli_word_backwards (vty_io vio)
-{
- return uty_cli_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Delete to end of word -- forwards.
- *
- * Deletes any leading spaces, then deletes upto next space or end of line.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cli_del_word_forwards(vty_io vio)
-{
- return uty_cli_del_forwards(vio, uty_cli_word_forwards_delta(vio)) ;
-}
-
-/*------------------------------------------------------------------------------
- * Delete to start of word -- backwards.
- *
- * Deletes any trailing spaces, then deletes upto next space or start of line.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cli_del_word_backwards(vty_io vio)
-{
- return uty_cli_del_backwards(vio, uty_cli_word_backwards_delta(vio, 1)) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Kill rest of line from current point.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cli_del_to_eol (vty_io vio)
-{
- return uty_cli_del_forwards(vio, vio->cl.len - vio->cl.cp) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Kill line from the beginning.
- *
- * Returns number of characters deleted.
- */
-static int
-uty_cli_clear_line(vty_io vio)
-{
- uty_cli_bol(vio) ;
- return uty_cli_del_to_eol(vio) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Transpose chars before or at the point.
- *
- * Return number of characters affected.
- */
-static int
-uty_cli_transpose_chars(vty_io vio)
-{
- char chars[2] ;
- char* cp ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Give up if < 2 characters or at start of line. */
- if ((vio->cl.len < 2) || (vio->cl.cp < 1))
- return 0 ;
-
- /* Move back to first of characters to exchange */
- if (vio->cl.cp == vio->cl.len)
- uty_cli_backwards(vio, 2) ;
+ if (vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), false) >= 0)
+ return CMD_SUCCESS ;
else
- uty_cli_backwards(vio, 1) ;
-
- /* Pick up in the new order */
- cp = qs_cp_char(vio->cl) ;
- chars[1] = *cp++ ;
- chars[0] = *cp ;
-
- /* And overwrite */
- return uty_cli_overwrite(vio, chars, 2) ;
+ return CMD_IO_ERROR ;
} ;
-/*==============================================================================
- * Command line history handling
- */
-
/*------------------------------------------------------------------------------
- * Add given command line to the history buffer.
- *
- * This is inserting the vty->buf line into the history.
+ * Tidy up after input file has been closed
*/
extern void
-uty_cli_hist_add (vty_io vio, const char* cmd_line)
-{
- 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) ;
-
- /* find the previous command line in the history */
- prev_index = vio->hindex - 1 ;
- if (prev_index < 0)
- prev_index = VTY_MAXHIST - 1 ;
-
- prev_line = vector_get_item(vio->hist, prev_index) ;
-
- /* 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) ;
-
- /* Now replace the hindex entry */
- vector_set_item(vio->hist, vio->hindex, qs_copy(prev_line, 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;
-} ;
-
-/*------------------------------------------------------------------------------
- * 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)
+uty_file_read_close(vio_vf vf)
{
- int index ;
- unsigned old_len ;
- unsigned after ;
- unsigned back ;
- qstring hist ;
-
- VTY_ASSERT_LOCKED() ;
-
- assert((step == +1) || (step == -1)) ;
-
- index = vio->hp ;
-
- /* 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)
- index = 0 ;
-
- hist = vector_get_item(vio->hist, index) ;
-
- /* 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 */
- uty_cli_bol(vio) ;
-
- /* Get previous line from history buffer and echo that */
- old_len = vio->cl.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 ;
-
- 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 (back > 0)
- uty_cli_echo_n(vio, telnet_backspaces, back) ;
-
return ;
} ;
/*------------------------------------------------------------------------------
- * Use next history line, if any.
- */
-static void
-uty_cli_next_line(vty_io vio)
-{
- uty_cli_history_use(vio, +1) ;
-}
-
-/*------------------------------------------------------------------------------
- * Use previous history line, if any.
- */
-static void
-uty_cli_previous_line (vty_io vio)
-{
- uty_cli_history_use(vio, -1) ;
-}
-
-/*==============================================================================
- * Command Completion and Command Description
+ * Flush output buffer and close.
*
- */
-static void uty_cli_describe_show(vty_io vio, vector describe) ;
-static void uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc) ;
-static void uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str) ;
-
-static vector uty_cli_cmd_prepare(vty_io vio, int help) ;
-
-/*------------------------------------------------------------------------------
- * Command completion
- */
-static void
-uty_cli_complete_command (vty_io vio, enum node_type node)
-{
- unsigned i ;
- int ret ;
- int len ;
- int n ;
- vector matched ;
- vector vline ;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Try and match the tokenised command line */
- vline = uty_cli_cmd_prepare(vio, 1) ;
- matched = cmd_complete_command (vline, node, &ret);
- cmd_free_strvec (vline);
-
- /* Show the result. */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
-
- case CMD_ERR_NO_MATCH:
- uty_cli_out_newline(vio) ; /* clears cli_drawn */
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
-
- case CMD_COMPLETE_FULL_MATCH:
- uty_cli_eol (vio) ;
- uty_cli_word_backwards_pure (vio);
- uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
- uty_cli_insert(vio, " ", 1);
- break ;
-
- case CMD_COMPLETE_MATCH:
- uty_cli_eol (vio) ;
- uty_cli_word_backwards_pure (vio);
- uty_cli_word_overwrite (vio, vector_get_item(matched, 0));
- break ;
-
- case CMD_COMPLETE_LIST_MATCH:
- len = 6 ;
- for (i = 0; i < vector_end(matched); i++)
- {
- 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, "%-*s ", len, (char*)vector_get_item(matched, i));
- }
- uty_cli_out_newline(vio) ;
-
- break;
-
- case CMD_COMPLETE_ALREADY:
- default:
- break;
- } ;
-
- cmd_free_strvec(matched);
-} ;
-
-/*------------------------------------------------------------------------------
- * Command Description
- */
-static void
-uty_cli_describe_command (vty_io vio, enum node_type node)
-{
- int ret;
- vector vline;
- vector describe;
-
- VTY_ASSERT_LOCKED() ;
-
- /* Try and match the tokenised command line */
- vline = uty_cli_cmd_prepare(vio, 1) ;
- describe = cmd_describe_command (vline, node, &ret);
- cmd_free_strvec (vline);
-
- uty_cli_out_newline(vio); /* clears cli_drawn */
-
- /* Deal with result. */
- switch (ret)
- {
- case CMD_ERR_AMBIGUOUS:
- uty_cli_out_CMD_ERR_AMBIGUOUS(vio) ;
- break ;
-
- case CMD_ERR_NO_MATCH:
- uty_cli_out_CMD_ERR_NO_MATCH(vio) ;
- break ;
-
- default:
- uty_cli_describe_show(vio, describe) ;
- break ;
- } ;
-
- if (describe != NULL)
- vector_free (describe);
-}
-
-/*------------------------------------------------------------------------------
- * Show the command description.
- *
- * Generates lines of the form:
- *
- * word description text
- *
- * Where the word field is adjusted to suit the longest word, and the
- * description text is wrapped if required (if the width of the console is
- * known) so that get:
- *
- * word description ..................................
- * .............text
- *
- * If one of the options is '<cr>', that is always shown last.
- */
-static void
-uty_cli_describe_show(vty_io vio, vector describe)
-{
- unsigned int i, cmd_width, desc_width;
- struct desc *desc, *cr_item ;
-
- /* Get width of the longest "word" */
- cmd_width = 0;
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- unsigned int len;
-
- if (desc->cmd[0] == '\0')
- continue;
-
- len = strlen (desc->cmd);
- if (desc->cmd[0] == '.')
- len--;
-
- if (cmd_width < len)
- cmd_width = len;
- }
-
- /* Set width of description string. */
- desc_width = vio->width - (cmd_width + 6);
-
- /* Print out description. */
- cr_item = NULL ; /* put <cr> last if it appears */
-
- for (i = 0; i < vector_active (describe); i++)
- if ((desc = vector_slot (describe, i)) != NULL)
- {
- if (desc->cmd[0] == '\0')
- continue;
-
- if (strcmp (desc->cmd, cr_string) == 0)
- {
- cr_item = desc;
- continue;
- }
-
- uty_cli_describe_fold (vio, cmd_width, desc_width, desc);
- }
-
- if (cr_item != NULL)
- uty_cli_describe_fold (vio, cmd_width, desc_width, cr_item);
-} ;
-
-/*------------------------------------------------------------------------------
- * Show one word and the description, folding the description as required.
- */
-static void
-uty_cli_describe_fold (vty_io vio, int cmd_width,
- unsigned int desc_width, struct desc *desc)
-{
- char *buf;
- const char *cmd, *p;
- int pos;
-
- VTY_ASSERT_LOCKED() ;
-
- cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
- p = desc->str ;
-
- /* If have a sensible description width */
- if (desc_width > 20)
- {
- buf = XCALLOC (MTYPE_TMP, strlen (desc->str) + 1);
-
- while (strlen (p) > desc_width)
- {
- /* move back to first space */
- for (pos = desc_width; pos > 0; pos--)
- if (*(p + pos) == ' ')
- break;
-
- /* if did not find a space, break at width */
- if (pos == 0)
- pos = desc_width ;
-
- strncpy (buf, p, pos);
- buf[pos] = '\0';
- uty_cli_describe_line(vio, cmd_width, cmd, buf) ;
-
- cmd = ""; /* for 2nd and subsequent lines */
-
- p += pos ; /* step past what just wrote */
- while (*p == ' ')
- ++p ; /* skip spaces */
- } ;
-
- XFREE (MTYPE_TMP, buf);
- } ;
-
- uty_cli_describe_line(vio, cmd_width, cmd, p) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Show one description line.
- */
-static void
-uty_cli_describe_line(vty_io vio, int cmd_width, const char* cmd,
- const char* str)
-{
- if (str != NULL)
- uty_cli_out (vio, " %-*s %s", cmd_width, cmd, str) ;
- else
- uty_cli_out (vio, " %-s", cmd) ;
- uty_cli_out_newline(vio) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Prepare "vline" token array for command handler.
- *
- * For "help" (command completion/description), if the command line is empty,
- * or ends in ' ', adds an empty token to the end of the token array.
- */
-static vector
-uty_cli_cmd_prepare(vty_io vio, int help)
-{
- vector vline ;
-
- vline = cmd_make_strvec(qs_term(&vio->cl)) ;
-
- /* Note that if there is a vector of tokens, then there is at least one
- * token, so can guarantee that vio->cl.len >= 1 !
- */
- if (help)
- if ((vline == NULL) || isspace(*qs_chars_at(&vio->cl, vio->cl.len - 1)))
- vline = cmd_add_to_strvec(vline, "") ;
-
- return vline ;
-} ;
-
-/*==============================================================================
- * VTY telnet stuff
- */
-
-#define TELNET_OPTION_DEBUG 0 /* 0 to turn off */
-
-static const char* telnet_commands[256] =
-{
- [tn_IAC ] = "IAC",
- [tn_DONT ] = "DONT",
- [tn_DO ] = "DO",
- [tn_WONT ] = "WONT",
- [tn_WILL ] = "WILL",
- [tn_SB ] = "SB",
- [tn_GA ] = "GA",
- [tn_EL ] = "EL",
- [tn_EC ] = "EC",
- [tn_AYT ] = "AYT",
- [tn_AO ] = "AO",
- [tn_IP ] = "IP",
- [tn_BREAK] = "BREAK",
- [tn_DM ] = "DM",
- [tn_NOP ] = "NOP",
- [tn_SE ] = "SE",
- [tn_EOR ] = "EOR",
- [tn_ABORT] = "ABORT",
- [tn_SUSP ] = "SUSP",
- [tn_EOF ] = "EOF",
-} ;
-
-static const char* telnet_options[256] =
-{
- [to_BINARY] = "BINARY", /* 8-bit data path */
- [to_ECHO] = "ECHO", /* echo */
- [to_RCP] = "RCP", /* prepare to reconnect */
- [to_SGA] = "SGA", /* suppress go ahead */
- [to_NAMS] = "NAMS", /* approximate message size */
- [to_STATUS] = "STATUS", /* give status */
- [to_TM] = "TM", /* timing mark */
- [to_RCTE] = "RCTE", /* remote controlled tx and echo */
- [to_NAOL] = "NAOL", /* neg. about output line width */
- [to_NAOP] = "NAOP", /* neg. about output page size */
- [to_NAOCRD] = "NAOCRD", /* neg. about CR disposition */
- [to_NAOHTS] = "NAOHTS", /* neg. about horizontal tabstops */
- [to_NAOHTD] = "NAOHTD", /* neg. about horizontal tab disp. */
- [to_NAOFFD] = "NAOFFD", /* neg. about formfeed disposition */
- [to_NAOVTS] = "NAOVTS", /* neg. about vertical tab stops */
- [to_NAOVTD] = "NAOVTD", /* neg. about vertical tab disp. */
- [to_NAOLFD] = "NAOLFD", /* neg. about output LF disposition */
- [to_XASCII] = "XASCII", /* extended ascii character set */
- [to_LOGOUT] = "LOGOUT", /* force logout */
- [to_BM] = "BM", /* byte macro */
- [to_DET] = "DET", /* data entry terminal */
- [to_SUPDUP] = "SUPDUP", /* supdup protocol */
- [to_SUPDUPOUTPUT] = "SUPDUPOUTPUT",/* supdup output */
- [to_SNDLOC] = "SNDLOC", /* send location */
- [to_TTYPE] = "TTYPE", /* terminal type */
- [to_EOR] = "EOR", /* end or record */
- [to_TUID] = "TUID", /* TACACS user identification */
- [to_OUTMRK] = "OUTMRK", /* output marking */
- [to_TTYLOC] = "TTYLOC", /* terminal location number */
- [to_3270REGIME] = "3270REGIME", /* 3270 regime */
- [to_X3PAD] = "X3PAD", /* X.3 PAD */
- [to_NAWS] = "NAWS", /* window size */
- [to_TSPEED] = "TSPEED", /* terminal speed */
- [to_LFLOW] = "LFLOW", /* remote flow control */
- [to_LINEMODE] = "LINEMODE", /* Linemode option */
- [to_XDISPLOC] = "XDISPLOC", /* X Display Location */
- [to_OLD_ENVIRON] = "OLD_ENVIRON", /* Old - Environment variables */
- [to_AUTHENTICATION] = "AUTHENTICATION", /* Authenticate */
- [to_ENCRYPT] = "ENCRYPT", /* Encryption option */
- [to_NEW_ENVIRON] = "NEW_ENVIRON", /* New - Environment variables */
- [to_EXOPL] = "EXOPL", /* extended-options-list */
-} ;
-
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as decimal.
- */
-static void
-uty_cli_out_dec(vty_io vio, const char* str, unsigned char u)
-{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "%d ", (int)u) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * For debug. Put string or value as hex.
- */
-static void
-uty_cli_out_hex(vty_io vio, const char* str, unsigned char u)
-{
- if (str != NULL)
- uty_cli_out(vio, "%s ", str) ;
- else
- uty_cli_out(vio, "0x%02x ", (unsigned)u) ;
-} ;
-
-/*------------------------------------------------------------------------------
- * Send telnet: "WILL TELOPT_ECHO"
- */
-static void
-uty_will_echo (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_ECHO };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "suppress Go-Ahead"
- */
-static void
-uty_will_suppress_go_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_WILL, to_SGA };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use linemode"
- */
-static void
-uty_dont_linemode (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LINEMODE };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "Use window size"
- */
-static void
-uty_do_window_size (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DO, to_NAWS };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * Send telnet: "don't use lflow" -- not currently used
- */
-static void
-uty_dont_lflow_ahead (vty_io vio)
-{
- unsigned char cmd[] = { tn_IAC, tn_DONT, to_LFLOW };
- VTY_ASSERT_LOCKED() ;
- uty_cli_write (vio, (char*)cmd, (int)sizeof(cmd));
-}
-
-/*------------------------------------------------------------------------------
- * 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.
+ * Returns: true <=> buffer (now) empty
*/
extern bool
-uty_cli_iac_callback(keystroke_iac_callback_args)
+uty_file_write_close(vio_vf vf, bool final)
{
- return uty_telnet_command((vty_io)context, stroke, true) ;
+ return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), true) == 0 ;
} ;
-
-/*------------------------------------------------------------------------------
- * 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 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 ;
-
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
-
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_commands[*p], *p) ;
- ++p ;
-
- if (left-- > 0)
- uty_cli_out_dec(vio, telnet_options[*p], *p) ;
- ++p ;
-
- if (left > 0)
- {
- while(left-- > 0)
- uty_cli_out_hex(vio, NULL, *p++) ;
-
- if (stroke->flags & kf_truncated)
- uty_cli_out(vio, "... ") ;
-
- if (!(stroke->flags & kf_broken))
- {
- uty_cli_out_hex(vio, telnet_commands[tn_IAC], tn_IAC) ;
- uty_cli_out_hex(vio, telnet_commands[tn_SE], tn_SE) ;
- }
- } ;
-
- 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 dealt_with ; /* go no further if broken */
-
- p = stroke->buf ;
- left = stroke->len ;
-
- passert(left >= 1) ; /* must be if not broken ! */
- passert(stroke->value == *p) ; /* or something is wrong */
-
- ++p ; /* step past X of IAC X */
- --left ;
-
- /* Decode the one command that is interesting -- "NAWS" */
- switch (stroke->value)
- {
- case tn_SB:
- passert(left > 0) ; /* or parser failed */
-
- o = *p++ ; /* the option byte */
- --left ;
- switch(o)
- {
- case to_NAWS:
- if (left != 4)
- {
- uzlog(NULL, LOG_WARNING,
- "RFC 1073 violation detected: telnet NAWS option "
- "should send %d characters, but we received %d",
- (3 + 4 + 2), (3 + left + 2)) ;
- }
- else
- {
- vio->width = *p++ << 8 ;
- vio->width += *p++ ;
- vio->height = *p++ << 8 ;
- vio->height += *p ;
-
- if (TELNET_OPTION_DEBUG)
- 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 ;
-
- default: /* no other IAC SB <option> */
- break ;
- } ;
- break ;
-
- default: /* no other IAC X */
- break ;
- } ;
-
- return dealt_with ;
-} ;
-
#endif