diff options
Diffstat (limited to 'lib/vty_cli.c')
-rw-r--r-- | lib/vty_cli.c | 525 |
1 files changed, 265 insertions, 260 deletions
diff --git a/lib/vty_cli.c b/lib/vty_cli.c index cf85fb6c..83f156ef 100644 --- a/lib/vty_cli.c +++ b/lib/vty_cli.c @@ -40,6 +40,12 @@ #include "memory.h" +/*============================================================================== + * This is the CLI which is part of a VIN_TERM vf. + * + * With a few exceptions, the externs here are called by vty_io_term. + */ + /*------------------------------------------------------------------------------ * Essential stuff. */ @@ -49,11 +55,9 @@ static const char* telnet_newline = TELNET_NEWLINE ; /*============================================================================== * Construct and destroy CLI object - * - * - * */ -static bool uty_cli_iac_callback(keystroke_iac_callback_args) ; + +static bool uty_cli_callback(keystroke_callback_args) ; static void uty_cli_update_more(vty_cli cli) ; static void uty_cli_cancel(vty_cli cli) ; @@ -79,67 +83,64 @@ uty_cli_new(vio_vf vf) /* Zeroising has initialised: * - * vf = NULL -- set below - * - * hist = NULL -- set up when first used. - * hp = 0 -- hp == h_now => in the present ... - * h_now = 0 -- ... see uty_cli_hist_add() - * h_repeat = false -- ... see uty_cli_hist_add() + * vf = NULL -- set below * - * width = 0 -- unknown width ) Telnet window size - * height = 0 -- unknown height ) + * hist = NULL -- set up when first used. + * hp = 0 -- hp == h_now => in the present ... + * h_now = 0 -- ... see uty_cli_hist_add() + * h_repeat = false -- ... see uty_cli_hist_add() * - * lines = 0 -- set below - * lines_set = false -- set below + * width = 0 -- unknown width ) Telnet window size + * height = 0 -- unknown height ) * - * monitor = false -- not a "monitor" - * monitor_busy = false -- so not busy either + * lines = 0 -- set below + * lines_set = false -- set below * - * v_timeout = ??? + * monitor = false -- not a "monitor" + * monitor_busy = false -- so not busy either * - * key_stream = X -- set below + * key_stream = X -- set below * - * drawn = false - * dirty = false + * drawn = false * - * tilde_prompt = false -- tilde prompt is not drawn - * tilde_enabled = false -- set below if ! multi-threaded + * tilde_prompt = false -- tilde prompt is not drawn + * tilde_enabled = false -- set below if ! multi-threaded * - * prompt_len = 0 -- not drawn, in any case - * extra_len = 0 -- not drawn, in any case + * prompt_len = 0 -- not drawn, in any case + * extra_len = 0 -- not drawn, in any case * - * prompt_node = NULL_NODE -- so not set ! - * prompt_gen = 0 -- not a generation number - * prompt_for_node = NULL -- not set, yet + * prompt_node = NULL_NODE -- so not set ! + * prompt_gen = 0 -- not a generation number + * prompt_for_node = NULL -- not set, yet * - * dispatched = false -- see below - * in_progress = false -- see below - * blocked = false -- see below - * paused = false + * dispatched = false -- see below + * in_progress = false -- see below + * blocked = false -- see below + * paused = false * - * out_active = false - * flush = false + * mon_active = false + * out_active = false * - * more_wait = false - * more_enter = false + * more_wait = false + * more_enter = false * - * more_enabled = false -- not in "--More--" state + * more_enabled = false -- not in "--More--" state * - * pause_timer = NULL -- set below if multi-threaded + * pause_timer = NULL -- set below if multi-threaded * - * context = NULL -- see below - * context_auth = false -- set by uty_cli_want_command() + * context = NULL -- see below + * auth_context = false -- set by uty_cli_want_command() * - * parsed = NULL -- see below - * to_do = cmd_do_nothing - * cl = NULL qstring -- set below - * clo = NULL qstring -- set below - * clx = NULL qstring -- set below - * dispatch = all zeros -- nothing to dispatch + * parsed = NULL -- see below + * to_do = cmd_do_nothing + * cl = NULL qstring -- set below + * cls = NULL qstring -- set below + * clx = NULL qstring -- set below + * dispatch = all zeros -- nothing to dispatch * - * cbuf = NULL -- see below + * cbuf = NULL -- see below * - * olc = NULL -- see below + * olc = NULL -- see below */ confirm(NULL_NODE == 0) ; /* prompt_node & node */ confirm(cmd_do_nothing == 0) ; /* to_do */ @@ -148,9 +149,11 @@ uty_cli_new(vio_vf vf) cli->vf = vf ; /* Allocate and initialise a keystroke stream TODO: CSI ?? */ - cli->key_stream = keystroke_stream_new('\0', uty_cli_iac_callback, cli) ; + cli->key_stream = keystroke_stream_new('\0', "\x03", uty_cli_callback, cli) ; - /* Set up cl and clx qstrings and the command line output fifo */ + /* Set up cl, cls and clx qstrings, the command line output fifo and + * the output line control. + */ cli->cl = qs_new(120) ; /* reasonable line length */ cli->cls = qs_new(120) ; cli->clx = qs_new(120) ; @@ -167,15 +170,19 @@ uty_cli_new(vio_vf vf) uty_cli_set_lines(cli, host.lines, false) ; uty_cli_update_more(cli) ; /* and update the olc etc to suit. */ - /* Enable "~ " prompt and pause timer if multi-threaded */ + /* Enable "~ " prompt and pause timer if multi-threaded + * + * TODO decide whether to ditch the '~' prompt, given the priority given + * to commands. + */ if (vty_nexus) { cli->pause_timer = qtimer_init_new(NULL, vty_cli_nexus->pile, vty_term_pause_timeout, cli) ; - cli->tilde_enabled = vty_multi_nexus ; + cli->tilde_enabled = vty_multi_nexus && false ; } ; - /* Ready to be started -- paused, out_active, flush & more_wait are false. + /* Ready to be started -- paused, out_active & more_wait are false. * * Is started by the first call of uty_cli_want_command(), which (inter alia) * set the cli->context so CLI knows how to prompt etc. @@ -229,7 +236,6 @@ uty_cli_close(vty_cli cli, bool final) cli->blocked = true ; /* do not renter the CLI */ cli->out_active = true ; /* if there is any output, it can go */ - cli->flush = true ; /* all of it can go */ /* Ream out the history. */ { @@ -242,18 +248,18 @@ uty_cli_close(vty_cli cli, bool final) /* Empty the keystroke handling. */ cli->key_stream = keystroke_stream_free(cli->key_stream) ; - /* Can discard active command line if not dispatched TODO ?? */ + /* Can discard active command line if not dispatched */ if (!cli->dispatched || final) cli->clx = qs_reset(cli->clx, free_it) ; /* Can discard context and parsed objects */ cli->context = cmd_context_free(cli->context, true) ; /* its a copy */ - cli->parsed = cmd_parsed_free(cli->parsed) ; + cli->parsed = cmd_parsed_free(cli->parsed) ; /* Discard any pause_timer, and suppress */ cli->pause_timer = qtimer_free(cli->pause_timer) ; cli->paused = false ; - cli->tilde_enabled = false ; + cli->tilde_enabled = false ; /* If final, free the CLI object. */ if (final) @@ -272,15 +278,43 @@ uty_cli_close(vty_cli cli, bool final) } ; /*------------------------------------------------------------------------------ - * The keystroke iac callback function. + * The keystroke callback function. + * + * This deals with interrupts and IAC sequences that should be dealt with as + * soon as they are read -- not stored in the keystroke stream for later + * processing. * - * 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 <=> dealt with keystroke, do not store in keystroke stream */ static bool -uty_cli_iac_callback(keystroke_iac_callback_args) +uty_cli_callback(keystroke_callback_args /* void* context, keystroke stroke */) { - return uty_telnet_command(((vty_cli)context)->vf, stroke, true) ; + vty_cli cli = context ; + + switch (stroke->type) + { + case ks_char: /* character -- uint32_t */ + if ((cli->in_progress || cli->out_active) && !cli->more_wait) + { + vty_io vio = cli->vf->vio ; + + uty_cmd_signal(vio, CMD_CANCEL) ; /* ^C */ + + return true ; + } ; + + return false ; + + case ks_iac: /* Telnet command */ + return uty_telnet_command(cli->vf, stroke, true) ; + + case ks_esc: /* ESC xx */ + case ks_csi: /* ESC [ ... or CSI ... */ + zabort("invalid interrupt keystroke type") ; + + default: + zabort("unknown keystroke type") ; + } ; } ; /*------------------------------------------------------------------------------ @@ -407,8 +441,8 @@ uty_cli_update_more(vty_cli cli) * (inp) running -- or at least no further command has been * fetched by the command loop. * - * The command may be a in_pipe, so there may be many commands - * to be completed before the CLI level command is. + * The command may be an in_pipe, so there may be many + * commands to be completed before the CLI level command is. * * or: the CLI has been closed. * @@ -420,18 +454,13 @@ uty_cli_update_more(vty_cli cli) * out_active -- the command output FIFO is being emptied. * (oa) * This is set when output is pushed, and cleared when - * everything is written away and flush is set. When it - * is set, any current command line is wiped. + * everything is written away and in_progress is not set. + * When it is set, any current command line is wiped. * * Note that what this flag does is prevent the CLI from * running until the output completes, and in particular * prevents it from writing anything to the CLI buffer. * - * flush -- is set when the CLI is ready for the next command (so - * (fsh) when in_progress is cleared) to cause any incomplete - * command output to be flushed, and to signal that - * out_active should be cleared when all output is complete. - * * more_wait -- is in "--more--" wait state. => out_active ! * (mwt) * The "--more--" CLI uses the CLI output buffer to draw @@ -444,16 +473,16 @@ uty_cli_update_more(vty_cli cli) * * The following are the valid combinations: * - * dsp:inp:bkd: oa:fsh:mwt:men: - * ---:---:---:---:---:---:---:----------------------------------------- - * 0 : 0 : 0 : 0 : 0 : 0 : 0 : collecting a new command - * 1 : 0 : 0 : 0 : 0 : 0 : 0 : waiting for command to be fetched - * 1 : 1 : 0 : X : 0 : 0 : 0 : command fetched and running - * 1 : 1 : 1 : X : 0 : 0 : 0 : waiting for command to complete - * 0 : 0 : 0 : 1 : 1 : 0 : 0 : waiting for command output to finish - * 1 : 1 : X : 1 : X : 1 : 1 : waiting for "--more--" to start - * 1 : 1 : X : 1 : X : 1 : 0 : waiting for "--more--" response - * 1 : 1 : 1 : 1 : 1 : 0 : 0 : waiting for command to complete, + * dsp:inp:bkd: oa:mwt:men: + * ---:---:---:---:---:---:----------------------------------------- + * 0 : 0 : 0 : 0 : 0 : 0 : collecting a new command + * 1 : 0 : 0 : 0 : 0 : 0 : waiting for command to be fetched + * 1 : 1 : 0 : X : 0 : 0 : command fetched and running + * 1 : 1 : 1 : X : 0 : 0 : waiting for command to complete + * 0 : 0 : 0 : 1 : 0 : 0 : waiting for command output to finish + * 1 : X : X : 1 : 1 : 1 : waiting for "--more--" to start + * 1 : X : X : 1 : 1 : 0 : waiting for "--more--" response + * 1 : 1 : 1 : 1 : 0 : 0 : waiting for command to complete, * after the CLI has been closed * * There are two output FIFOs: @@ -466,20 +495,21 @@ uty_cli_update_more(vty_cli cli) * FIFO. The command FIFO is emptied when out_active. While a command is * in_progress all its output is collected in the command output buffer, * to be written away when the command completes, or if it pushes the output - * explicitly. Note that where the CLI level command is a pipe-in, the first + * explicitly. Note that where the CLI level command is an in_pipe, the first * output will set out_active, and that will persist until returns to the CLI * level. Between commands in the pipe, the output will be pushed, so will * output stuff more or less as it is generated. * * It is expected that each command's output will end with a newline. However, * the line control imposes some discipline, and holds on to incomplete lines - * until a newline arrives, or the output if flushed. -- so that when the - * CLI is kicked, the cursor will be at the start of an empty line. - * + * until a newline arrives, or the output is because in_progress is false -- so + * that when the CLI is kicked, the cursor will be at the start of an empty + * line. * - * Note that only sets read on when the keystroke stream is empty and has not - * yet hit eof. The CLI process is driven mostly by write_ready -- which - * invokes read_ready. + * Note that is read ready until the keystroke stream hits eof. So any input + * will be hoovered up as soon as it is available. The CLI process is driven + * mostly by write_ready, except for when all output is complete and the + * input keystroke buffer has been emptied. * * 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 @@ -593,6 +623,7 @@ static cmd_do_t uty_cli_command(vty_cli cli) ; static void uty_cli_hist_add (vty_cli cli, qstring clx) ; static void uty_cli_pause_start(vty_cli cli) ; +static void uty_cli_goto_end_if_drawn(vty_cli cli) ; /*------------------------------------------------------------------------------ * Standard CLI for VTY_TERM -- if not blocked, runs until: @@ -660,11 +691,12 @@ uty_cli_standard(vty_cli cli) /* If blocked, must wait for some other event to continue in CLI. * * Note that will be blocked if have just dispatched a command, and is - * "tilde_enabled" -- which will be true if single threaded, and may be - * set for other reasons. + * not "tilde_enabled" -- which is the case if single threaded, and may be + * so for other reasons. * * If the keystroke stream is not empty, use write_ready as a proxy for - * CLI ready -- no point doing anything until any buffered. + * CLI ready -- no point doing anything until any buffered output has been + * written away. * * If command prompt has been redrawn, need to kick writer to deal with * that -- will reenter to then process any keystrokes. @@ -685,7 +717,7 @@ uty_cli_standard(vty_cli cli) * command completes, or something else happens). * * Note that if a command has been dispatched, and is !tilde_enabled, then - * will now be blocked. + * will now be blocked, so won't be here. */ if (cli->dispatched && !cli->out_active && !cli->drawn) uty_cli_pause_start(cli) ; @@ -718,7 +750,7 @@ uty_cli_dispatch(vty_cli cli) VTY_ASSERT_LOCKED() ; /* About to dispatch a command, so must be in the following state. */ - assert(!cli->dispatched && !cli->in_progress + qassert(!cli->dispatched && !cli->in_progress && !cli->blocked && !cli->out_active) ; qassert(cli->context->node == vio->vty->exec->context->node) ; @@ -735,15 +767,12 @@ uty_cli_dispatch(vty_cli cli) cli->to_do = cmd_do_nothing ; /* clear */ qs_clear(cli->cl) ; /* set cl empty */ - /* Reset the command output FIFO and line_control TODO */ -//uty_out_clear(cli->vio) ; /* clears FIFO and line control */ - - /* Dispatch command */ + /* Dispatch command */ if (cli->auth_context) to_do_now |= cmd_do_auth ; else { - /* All other nodes... */ + /* All other nodes... */ switch (to_do_now) { case cmd_do_nothing: @@ -827,8 +856,21 @@ uty_cli_command(vty_cli cli) * * May be in more_wait state -- so avoids touching that. * - * If not in_progress, then if dispatched, that is a new command ready to pass + * If not in_progress, then if dispatched, we have a new command ready to pass * to the command loop -- which we do here, and set cli->in_progress. + * + * Returns: CMD_SUCCESS -- the given action has been set to next command + * or: CMD_WAITING -- no command available (yet) + * + * Note that for the CLI eof and read time-out are handled as cmd_do_eof and + * cmd_do_timed_out -- so will float through as CMD_SUCCESS and be processed + * as commands. + * + * Write I/O errors and time-outs are signalled by uty_vf_error(), and + * therefore caught in the command loop. + * + * Read I/O errors are signalled by uty_vf_error(). Read timeout is treated + * as above. */ extern cmd_return_code_t uty_cli_want_command(vty_cli cli, cmd_action action, cmd_context context) @@ -851,18 +893,17 @@ uty_cli_want_command(vty_cli cli, cmd_action action, cmd_context context) cli->auth_context = ( (cli->context->node == AUTH_NODE) || (cli->context->node == AUTH_ENABLE_NODE) ) ; - /* If the output is owned by command output, then set flush flag, so - * that when buffers empty, the output will be released. + /* If the output is owned by command output, then when the buffers + * empty, and in_progress is seen to be false, out_active will be + * cleared. * - * If the output side is not owned by command output, wipe any temporary - * prompt. + * If the output side is not owned by command output, rewrite the + * command line, so that prompt is up to date and visible. * * In any case, kick write_ready to ensure output clears and prompt is * written and so on. */ - if (cli->out_active) - cli->flush = true ; - else + if (!cli->out_active) uty_cli_draw(cli) ; uty_term_set_readiness(cli->vf, write_ready) ; @@ -916,7 +957,7 @@ uty_cli_enter_more_wait(vty_cli cli) { VTY_ASSERT_LOCKED() ; - assert(cli->out_active && !cli->more_wait && !cli->drawn) ; + qassert(cli->out_active && !cli->more_wait && !cli->drawn) ; cli->more_wait = true ; /* new state */ cli->more_enter = true ; /* drawing the "--more--" etc. */ @@ -925,10 +966,19 @@ uty_cli_enter_more_wait(vty_cli cli) /*------------------------------------------------------------------------------ * Handle the "--more--" state. * - * Deals with the first stage if cli_blocked. + * If is paused or the monitor is active, do nothing -- those override the + * "--more--" state. + * + * If more_enter is set, then need to make sure the "--more--" prompt is + * written, and that any keystrokes which arrived before the prompt is + * written are taken into the keystroke stream -- so not stolen. * - * Tries to steal a keystroke, and when succeeds wipes the "--more--" - * prompt and exits cli_more_wait -- and may cancel all outstanding output. + * Note that the "--more--" state may be interrupted by monitor output, + * which, once complete, sets more_enter again. + * + * If more_enter is not set, tries to steal a keystroke, and if 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. * @@ -940,7 +990,7 @@ static vty_readiness_t uty_cli_more_wait(vty_cli cli) { keystroke_t steal ; - bool cancel ; + bool cancel, stolen ; VTY_ASSERT_LOCKED() ; @@ -954,7 +1004,8 @@ uty_cli_more_wait(vty_cli cli) { int get ; - uty_cli_draw_if_required(cli) ; /* draw the "--more--" */ + if (!cli->drawn) + uty_cli_draw(cli) ; /* draw the "--more--" */ /* If the CLI buffer is not yet empty, then is waiting for the * initial prompt to clear, so nothing to be done here. @@ -967,52 +1018,36 @@ uty_cli_more_wait(vty_cli cli) /* empty the input buffer into the keystroke stream */ do { - get = uty_term_read(cli->vf, NULL) ; + get = uty_term_read(cli->vf) ; } while (get > 0) ; - - return read_ready ; } ; - /* Go through the "--more--" process, unless closing */ - /* The read fetches a reasonable lump from the I/O -- so if there - * is a complete keystroke available, expect to get it. + /* Try to get a stolen keystroke. If the keystroke stream has hit + * EOF (for any reason, including error or timed-out), will get a ks_null + * stolen keystroke. * - * If no complete keystroke available to steal, returns ks_null. - * - * If has hit EOF or timeout (or error etc), returns knull_eof. + * If nothing to be stolen exit. */ - uty_term_read(cli->vf, steal) ; + stolen = keystroke_steal(cli->key_stream, steal) ; - /* If nothing stolen, make sure prompt is drawn and wait for more - * input. - * - * If anything at all has been stolen, then continue or cancel. + if (!stolen) + return read_ready ; + + /* Something has been stolen, so exit "--more--" state, and continue + * or cancel. */ cancel = false ; switch(steal->type) { - case ks_null: - switch(steal->value) - { - case knull_not_eof: - // TODO need to refresh "--more--" in case of monitor ?? - return read_ready; /* <<< exit: no keystroke */ - break ; - - case knull_eof: - case knull_timed_out: - cancel = true ; - break ; - - default: - break ; - } ; + case ks_null: /* at EOF for whatever reason */ + cancel = true ; break ; case ks_char: switch (steal->value) { case CONTROL('C'): + case CONTROL('Z'): case 'q': case 'Q': cancel = true ; @@ -1023,22 +1058,35 @@ uty_cli_more_wait(vty_cli cli) } ; break ; + case ks_esc: + cancel = steal->value == '\x1B' ; + break ; + default: break ; } ; /* End of "--more--" process * - * Wipe out the prompt (unless "cancel") and update state. + * If cancelling, make sure is at end of "--more--" prompt, and then + * clear the "drawn". The cancel process will append a " ^C\n" ! + * + * If not cancelling, wipe out the prompt and update state. * * Return write_ready to tidy up the screen and, unless cleared, write * some more. */ if (cancel) { - uty_out_clear(cli->vf->vio) ; - vio_lc_clear(cli->olc) ; /* clear & reset counter */ - uty_cli_cancel(cli) ; + vio_fifo_clear(cli->vf->obuf, false) ; + vio_lc_clear(cli->olc) ; /* clear & reset counter */ + + uty_cli_goto_end_if_drawn(cli) ; + cli->drawn = false ; + + qassert(cli->out_active) ; + + uty_cmd_signal(cli->vf->vio, CMD_CANCEL) ; /* ^C */ } else { @@ -1046,8 +1094,8 @@ uty_cli_more_wait(vty_cli cli) uty_cli_wipe(cli, 0) ; } ; - cli->more_wait = false ; /* exit more_wait */ - cli->more_enter = false ; /* tidy */ + cli->more_wait = false ; /* exit more_wait */ + cli->more_enter = false ; /* tidy */ return write_ready ; } ; @@ -1147,6 +1195,7 @@ static cli_rep telnet_stars = "********************************" ; CONFIRM(sizeof(telnet_spaces) == (sizeof(cli_rep_char) + 1)) ; CONFIRM(sizeof(telnet_dots) == (sizeof(cli_rep_char) + 1)) ; +CONFIRM(sizeof(telnet_stars) == (sizeof(cli_rep_char) + 1)) ; static void uty_cli_write_n(vty_cli cli, cli_rep_char chars, int n) ; @@ -1166,15 +1215,6 @@ uty_cli_out(vty_cli cli, const char *format, ...) } ; /*------------------------------------------------------------------------------ - * Completely empty the cli command buffer - */ -extern void -uty_cli_out_clear(vty_cli cli) -{ - vio_fifo_clear(cli->cbuf, true) ; -} ; - -/*------------------------------------------------------------------------------ * CLI VTY output -- cf write() */ extern void @@ -1221,12 +1261,11 @@ uty_cli_write_s(vty_cli cli, const char *str) /*============================================================================== * Prompts and responses */ -static void uty_cli_goto_end_if_drawn(vty_cli cli) ; /*------------------------------------------------------------------------------ * Send newline to the console. * - * Clears the cli_drawn and the cli_dirty flags. + * Clears the cli_drawn flag. */ extern void uty_cli_out_newline(vty_cli cli) @@ -1235,7 +1274,6 @@ uty_cli_out_newline(vty_cli cli) uty_cli_write(cli, telnet_newline, 2) ; cli->drawn = false ; - cli->dirty = false ; } ; /*------------------------------------------------------------------------------ @@ -1263,22 +1301,19 @@ uty_cli_out_wipe_n(vty_cli cli, int n) /*------------------------------------------------------------------------------ * If the command line is drawn, then show that it has been cancelled. * - * If the command line is dirty, then cancel it and start new line. - * - * Sets: cli_drawn = false - * cli_dirty = false + * Clears cli->drawn. */ static void uty_cli_cancel(vty_cli cli) { - if (cli->drawn || cli->dirty) + if (cli->drawn) { uty_cli_goto_end_if_drawn(cli) ; + uty_cli_write_s(cli, " ^C" TELNET_NEWLINE) ; - } ; - cli->drawn = false ; - cli->dirty = false ; + cli->drawn = false ; + } ; } ; /*------------------------------------------------------------------------------ @@ -1404,24 +1439,6 @@ uty_cli_wipe(vty_cli cli, int len) /* Nothing there any more */ cli->drawn = false ; - cli->dirty = false ; -} ; - -/*------------------------------------------------------------------------------ - * 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_cli cli) -{ - if (cli->drawn) - return false ; - - uty_cli_draw(cli) ; - - return true ; } ; /*------------------------------------------------------------------------------ @@ -1444,7 +1461,6 @@ uty_cli_draw_if_required(vty_cli cli) * the current prompt may be invalid. * * Sets: cli->drawn = true - * cli->dirty = false * cli->prompt_len = length of prompt used * cli->extra_len = 0 */ @@ -1455,9 +1471,6 @@ uty_cli_draw(vty_cli cli) size_t l_len ; int p_len ; - if (cli->dirty) - uty_cli_out_newline(cli) ; /* clears cli_dirty and cli_drawn */ - /* Sort out what the prompt is. */ if (cli->vf->vin_state != vf_open) { @@ -1572,15 +1585,18 @@ static void uty_cli_hist_use(vty_cli cli, enum hist_step) ; /*------------------------------------------------------------------------------ * Process keystrokes until run out of input, or get something to cmd_do. * - * Expects the command line to have been drawn. + * Expects the command line to have been drawn, so that what is in cli->cl + * is what is on the screen. * * Process keystrokes until run out of stuff to do, or have a "command line" - * that must now be executed. + * that must now be executed. Updates cli->cl. * * Processes the contents of the keystroke stream. If exhausts that, will set * ready to read and return. (To give some "sharing".) * * Returns: cmd_do_xxxx + * + * Note that will return cmd_do_eof or cmd_do_timed_out any number of times. */ static cmd_do_t uty_cli_process(vty_cli cli) @@ -1589,7 +1605,7 @@ uty_cli_process(vty_cli cli) uint8_t u ; cmd_do_t to_do ; - qs_copy(cli->cls, cli->cl) ; + qs_copy(cli->cls, cli->cl) ; /* current screen line */ assert(cli->drawn) ; @@ -1776,6 +1792,9 @@ uty_cli_process(vty_cli cli) * Update the command line to reflect the difference between old line and the * new line. * + * cli->cls is the old line (currently on screen) + * cli->cl is the new line (possible changed in some way) + * * Leave the screen cursor at the given required cursor position. */ static void @@ -1836,10 +1855,9 @@ uty_cli_update_line(vty_cli cli, uint rc) } ; /* Now move cursor to the required cursor position */ - if (sc > rc) + if (sc > rc) uty_cli_write_n(cli, telnet_backspaces, sc - rc) ; - - if (sc < rc) /* => lines unchanged, but cursor moved */ + else if (sc < rc) /* => lines unchanged, but cursor moved */ uty_cli_write(cli, np + sc, rc - sc) ; } ; @@ -1849,7 +1867,12 @@ uty_cli_update_line(vty_cli cli, uint rc) * * Similar to uty_cli_auth, except accepts a limited number of keystrokes. * + * Does not accept cursor moves, so does not do forwards delete, and ^D means + * exit. + * * Returns: cmd_do_xxxx + * + * Note that will return cmd_do_eof or cmd_do_timed_out any number of times. */ static cmd_do_t uty_cli_auth(vty_cli cli) @@ -1944,9 +1967,9 @@ uty_cli_auth(vty_cli cli) nlen = qs_len_nn(cli->cl) ; - if (nlen < olen) + if (nlen < olen) uty_cli_out_wipe_n(cli, nlen - olen) ; - if (nlen > olen) + else if (nlen > olen) uty_cli_write_n(cli, telnet_stars, nlen - olen) ; return to_do ; @@ -1955,11 +1978,22 @@ uty_cli_auth(vty_cli cli) /*------------------------------------------------------------------------------ * Fetch next keystroke, reading from the file if required. * - * Returns: cmd_do_t + * Returns: cmd_do_keystroke: have a keystroke -- stroke != ks_null + * cmd_do_eof : eof in keystream -- stroke == knull_eof + * cmd_do_timed_out: timed_ keystream -- stroke == knull_timed_out + * cmd_do_nothing : nothing available -- stroke == knull_not_eof + * + * Note that will return cmd_do_eof or cmd_do_timed_out any number of times. + * + * Note that any I/O error is signalled to the command loop, and is passed + * out of here as "nothing available". If the command loop is running, it + * will see the CMD_IO_ERROR signal and deal with the error. */ static cmd_do_t uty_cli_get_keystroke(vty_cli cli, keystroke stroke) { + qassert(cli->vf->vin_state == vf_open) ; + while (1) { if (keystroke_get(cli->key_stream, stroke)) @@ -1967,7 +2001,7 @@ uty_cli_get_keystroke(vty_cli cli, keystroke stroke) if (stroke->flags != 0) { /* TODO: deal with broken keystrokes */ - } + } ; if (stroke->type != ks_iac) return cmd_do_keystroke ; /* have a keystroke */ @@ -1979,7 +2013,7 @@ uty_cli_get_keystroke(vty_cli cli, keystroke stroke) { int get ; - assert(stroke->type == ks_null) ; + qassert(stroke->type == ks_null) ; switch (stroke->value) { @@ -1997,28 +2031,29 @@ uty_cli_get_keystroke(vty_cli cli, keystroke stroke) break ; } ; - get = uty_term_read(cli->vf, NULL) ; /* sets eof in key_stream + get = uty_term_read(cli->vf) ; /* sets eof in key_stream if hit eof or error */ - if (get <= 0) + if ((get == 0) || (get == -1)) return cmd_do_nothing ; } ; } ; } ; /*============================================================================== - * Command line operations + * Command line operations. + * + * These all affect the given command line, only. The effect on the screen + * is taken care of elsewhere -- see uty_cli_update_line(). */ /*------------------------------------------------------------------------------ * Insert 'n' characters at current position in the command line, leaving * cursor after the inserted characters. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_insert(qstring cl, const char* chars, int n) { - assert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0)) ; + qassert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0)) ; if (n > 0) { @@ -2030,13 +2065,11 @@ uty_cli_insert(qstring cl, const char* chars, int n) /*------------------------------------------------------------------------------ * Replace 'm' characters at the current position, by 'n' characters and leave * cursor at the end of the inserted characters. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_replace(qstring cl, int m, const char* chars, int n) { - assert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0) && (m >= 0)) ; + qassert((qs_cp_nn(cl) <= qs_len_nn(cl)) && (n >= 0) && (m >= 0)) ; qs_replace(cl, m, chars, n) ; @@ -2047,8 +2080,6 @@ uty_cli_replace(qstring cl, int m, const char* chars, int n) * Forward 'n' characters -- stop at end of line. * * Returns number of characters actually moved - * - * NB: assumes line will be updated by uty_cli_update_line() */ static int uty_cli_forwards(qstring cl, int n) @@ -2059,7 +2090,7 @@ uty_cli_forwards(qstring cl, int n) if (have < n) n = have ; - assert(n >= 0) ; + qassert(n >= 0) ; qs_move_cp_nn(cl, n) ; @@ -2070,8 +2101,6 @@ uty_cli_forwards(qstring cl, int n) * Backwards 'n' characters -- stop at start of line. * * Returns number of characters actually moved - * - * NB: assumes line will be updated by uty_cli_update_line() */ static int uty_cli_backwards(qstring cl, int n) @@ -2079,7 +2108,7 @@ uty_cli_backwards(qstring cl, int n) if ((int)qs_cp_nn(cl) < n) n = qs_cp_nn(cl) ; - assert(n >= 0) ; + qassert(n >= 0) ; qs_move_cp_nn(cl, -n) ; @@ -2089,23 +2118,18 @@ uty_cli_backwards(qstring cl, int n) /*------------------------------------------------------------------------------ * Move forwards (if n > 0) or backwards (if n < 0) -- stop at start or end of * line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_move(qstring cl, int n) { - if (n < 0) + if (n < 0) uty_cli_backwards(cl, -n) ; - - if (n > 0) + else if (n > 0) uty_cli_forwards(cl, +n) ; } ; /*------------------------------------------------------------------------------ * Delete 'n' characters -- forwards -- stop at end of line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_del_forwards(qstring cl, int n) @@ -2116,16 +2140,14 @@ uty_cli_del_forwards(qstring cl, int n) if (have < n) n = have ; /* cannot delete more than have */ - assert(n >= 0) ; + qassert(n >= 0) ; if (n > 0) qs_delete(cl, n) ; } /*------------------------------------------------------------------------------ - * Delete 'n' characters before the point. - * - * NB: assumes line will be updated by uty_cli_update_line() + * Delete 'n' characters before the point -- stopping at start of line. */ static void uty_cli_del_backwards(qstring cl, int n) @@ -2135,8 +2157,6 @@ uty_cli_del_backwards(qstring cl, int n) /*------------------------------------------------------------------------------ * Move to the beginning of the line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_bol(qstring cl) @@ -2146,8 +2166,6 @@ uty_cli_bol(qstring cl) /*------------------------------------------------------------------------------ * Move to the end of the line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_eol(qstring cl) @@ -2176,10 +2194,10 @@ uty_cli_word_forwards_delta(qstring cl) tp = cp ; - while ((tp < ep) && (*tp != ' ')) /* step over spaces */ + while ((tp < ep) && (*tp != ' ')) ++tp ; - while ((tp < ep) && (*tp == ' ')) /* step to space */ + while ((tp < ep) && (*tp == ' ')) ++tp ; return tp - cp ; @@ -2189,8 +2207,6 @@ uty_cli_word_forwards_delta(qstring cl) * Forward word -- move to start of next word. * * Moves past any non-spaces, then past any spaces. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_word_forwards(qstring cl) @@ -2203,8 +2219,8 @@ uty_cli_word_forwards(qstring cl) * * 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. + * Steps back over spaces, and then until next (backwards) character is space, + * or hits start of line. */ static int uty_cli_word_backwards_delta(qstring cl) @@ -2234,8 +2250,6 @@ uty_cli_word_backwards_delta(qstring cl) * * Moves past any spaces, then move back until next (backwards) character is * space or start of line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_word_backwards(qstring cl) @@ -2247,8 +2261,6 @@ uty_cli_word_backwards(qstring cl) * Delete to end of word -- forwards. * * Deletes any leading spaces, then deletes upto next space or end of line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_del_word_forwards(qstring cl) @@ -2260,8 +2272,6 @@ uty_cli_del_word_forwards(qstring cl) * Delete to start of word -- backwards. * * Deletes any trailing spaces, then deletes upto next space or start of line. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_del_word_backwards(qstring cl) @@ -2271,8 +2281,6 @@ uty_cli_del_word_backwards(qstring cl) /*------------------------------------------------------------------------------ * Kill rest of line from current point. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_del_to_eol(qstring cl) @@ -2282,8 +2290,6 @@ uty_cli_del_to_eol(qstring cl) /*------------------------------------------------------------------------------ * Kill line from the beginning. - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_clear_line(qstring cl) @@ -2293,9 +2299,9 @@ uty_cli_clear_line(qstring cl) } ; /*------------------------------------------------------------------------------ - * Transpose chars before or at the point. + * Transpose current character with previous one, and step forward one. * - * NB: assumes line will be updated by uty_cli_update_line() + * If at end of line, transpose the last two characters. */ static void uty_cli_transpose_chars(qstring cl) @@ -2389,7 +2395,8 @@ uty_cli_hist_make(vty_cli cli) /*------------------------------------------------------------------------------ * Add given command line to the history buffer. * - * This is inserting the vty->buf line into the history. + * The 'cp' stored with the line is set to be the end of the line, so that is + * all ready for when the stored line is used. * * Resets hp == h_now. */ @@ -2431,10 +2438,7 @@ uty_cli_hist_add (vty_cli cli, qstring clx) cli->h_repeat = false ; /* latest history is novel */ } ; - /* Now replace the h_now entry - * - * Note that the line inserted in the history has it's 'cp' set to the end of - * the line -- so that it is there when it comes back out again. + /* Now replace the h_now entry -- setting 'cp' to end of line */ hist_line = qs_copy(hist_line, clx) ; qs_set_cp_nn(hist_line, qs_len_nn(hist_line)) ; @@ -2471,12 +2475,12 @@ uty_cli_hist_use(vty_cli cli, enum hist_step step) * * Cannot step forwards from the present. * - * before stepping back from the present, take a copy of the current + * Before stepping back from the present, take a copy of the current * command line -- so can get back to it. * * Note that the 'cp' is stored with the line. So if return to the present, * the cursor returns to its current position. (When lines are added to - * the history, the cursor is at the end of the line.) + * the history, the cursor is set to the end of the line.) */ if (hp == cli->h_now) { @@ -2553,6 +2557,9 @@ uty_cli_hist_show(vty_cli cli) /*============================================================================== * Command Completion and Command Description * + * Any changes to the command line are made to cli->cl. If the command line + * is redrawn, updates cli->cls. Otherwise, the screen may need updating to + * reflect differences between cli->cl and cli->cls. */ static uint uty_cli_help_parse(vty_cli cli) ; @@ -2576,8 +2583,6 @@ static void uty_cli_help_finish(vty_cli cli) ; * is in a "special" place. * * This is called from inside "uty_cli_process()". - * - * NB: assumes line will be updated by uty_cli_update_line() */ static void uty_cli_complete_command (vty_cli cli) @@ -2782,7 +2787,8 @@ uty_cli_complete_list(vty_cli cli, vector item_v) pad = (str_len < str_width) ? str_width - str_len : 0 ; uty_cli_write_n(cli, telnet_spaces, pad + 2) ; - } + } ; + uty_cli_help_newline(cli) ; } ; @@ -2954,7 +2960,7 @@ uty_cli_help_message(vty_cli cli, const char* msg) * If the command line is drawn, make sure it is up to date, leaving cursor * at the end of the line, and then issue newline. * - * Clears cli->drawn and cli->dirty. + * Clears cli->drawn. */ static void uty_cli_help_newline(vty_cli cli) @@ -2965,7 +2971,6 @@ uty_cli_help_newline(vty_cli cli) uty_cli_write(cli, telnet_newline, 2) ; cli->drawn = false ; - cli->dirty = false ; } ; /*------------------------------------------------------------------------------ |