diff options
Diffstat (limited to 'lib/vty_cli.c')
-rw-r--r-- | lib/vty_cli.c | 777 |
1 files changed, 356 insertions, 421 deletions
diff --git a/lib/vty_cli.c b/lib/vty_cli.c index a4ab8de3..d06e455e 100644 --- a/lib/vty_cli.c +++ b/lib/vty_cli.c @@ -60,8 +60,6 @@ static const char* telnet_newline = TELNET_NEWLINE ; 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) ; - /*------------------------------------------------------------------------------ * Construct and initialise a new CLI object -- never embedded. * @@ -102,27 +100,16 @@ uty_cli_new(vio_vf vf) * key_stream = X -- set below * * drawn = false - * - * tilde_prompt = false -- tilde prompt is not drawn - * tilde_enabled = false -- set below if ! multi-threaded + * more_drawn = false * * 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 * - * dispatched = false -- see below - * in_progress = false -- see below - * blocked = false -- see below - * paused = false - * - * mon_active = false - * out_active = false - * - * more_wait = false - * more_enter = false + * state = X -- set below + * out_state = cos_idle * * more_enabled = false -- not in "--More--" state * @@ -142,6 +129,7 @@ uty_cli_new(vio_vf vf) * * olc = NULL -- see below */ + confirm(cos_idle == 0) ; confirm(NULL_NODE == 0) ; /* prompt_node & node */ confirm(cmd_do_nothing == 0) ; /* to_do */ confirm(CMD_ACTION_ALL_ZEROS) ; /* dispatch */ @@ -170,28 +158,18 @@ 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 - * - * TODO decide whether to ditch the '~' prompt, given the priority given - * to commands. + /* Enable pause timer if multi-threaded */ if (vty_nexus) - { - cli->pause_timer = qtimer_init_new(NULL, vty_cli_nexus->pile, - vty_term_pause_timeout, cli) ; -#if 0 - cli->tilde_enabled = vty_multi_nexus ; -#endif - } ; + cli->pause_timer = qtimer_init_new(NULL, vty_cli_nexus->pile, + vty_term_pause_timeout, cli) ; - /* Ready to be started -- paused, out_active & more_wait are false. + /* Ready to be started -- note that out_state is cos_idle. * * Is started by the first call of uty_cli_want_command(), which (inter alia) * set the cli->context so CLI knows how to prompt etc. */ - cli->dispatched = true ; - cli->in_progress = true ; - cli->blocked = true ; + cli->state = cst_in_progress ; /* start up "command" is running */ return cli ; } ; @@ -227,17 +205,24 @@ uty_cli_close(vty_cli cli, bool final) /* Bring as much of the command handling to a stop as possible, and * turn off "---more--" etc. so that output can proceed. + * + * If it is drawn, we wipe the command line (issuing ^C). We can, then, + * enable output -- which turns off any "--more--" stuff, but keep any + * existing pending monitor output (though forget any pause). + * + * We set the CLI stopped, so that nothing further will happen, including + * when any in_progress command completes. */ cli->more_enabled = false ; cli->lines = 0 ; /* so will not be re-enabled */ - uty_cli_cancel(cli) ; /* " ^C\r\n" unless believed blank */ + uty_cli_cancel(cli, true) ; /* " ^C\r\n" unless believed blank */ - cli->more_wait = false ; /* exit more_wait (if was in it) */ - cli->more_enter = false ; /* and so cannot be this */ - - cli->blocked = true ; /* do not renter the CLI */ - cli->out_active = true ; /* if there is any output, it can go */ + if (cli->state == cst_dispatched) + { + cmd_action_clear(cli->dispatch) ; + cli->state = cst_active ; /* never happened */ + } ; /* Ream out the history. */ { @@ -247,32 +232,33 @@ uty_cli_close(vty_cli cli, bool final) cli->hist = NULL ; } ; - /* Empty the keystroke handling. */ + /* Empty the keystroke handling. + */ cli->key_stream = keystroke_stream_free(cli->key_stream) ; - /* 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 */ + /* Can discard parsed objects + */ cli->parsed = cmd_parsed_free(cli->parsed) ; - /* Discard any pause_timer, and suppress */ + /* Discard any pause_timer + */ cli->pause_timer = qtimer_free(cli->pause_timer) ; - cli->paused = false ; - cli->tilde_enabled = false ; - /* If final, free the CLI object. */ + /* If final, free the CLI object and stuff that has been kept while + * the output side closes. + */ if (final) { cli->prompt_for_node = qs_free(cli->prompt_for_node) ; cli->cl = qs_free(cli->cl) ; + cli->clx = qs_free(cli->clx) ; cli->cls = qs_free(cli->cls) ; cli->cbuf = vio_fifo_free(cli->cbuf) ; cli->olc = vio_lc_free(cli->olc) ; + cli->context = cmd_context_free(cli->context, true) ; /* its a copy */ + XFREE(MTYPE_VTY_CLI, cli) ; /* sets cli = NULL */ } ; @@ -292,20 +278,60 @@ static bool uty_cli_callback(keystroke_callback_args /* void* context, keystroke stroke */) { vty_cli cli = context ; + cli_out_state_t out_state ; switch (stroke->type) { case ks_char: /* character -- uint32_t */ - if ((cli->in_progress || cli->out_active) && !cli->more_wait) + /* We pick up the ^C as a cancel iff: + * + * is already cos_cancel -- has no further effect + * + * is cst_dispatched -- cancels before even started + * + * is cst_in_progress or cst_complete, provided is not in either + * cos_more_enter or cos_more_wait -- which deal with ^C themselves. + */ + out_state = cli->out_state & cos_mask ; + + if (out_state == cos_cancel) + return true ; /* eat duplicates */ + + switch (cli->state) { - vty_io vio = cli->vf->vio ; + case cst_active: + qassert(out_state == cos_idle) ; + return false ; + + case cst_dispatched: + /* Managing to cancel before even started to run !! + * + * Clear the dispatched command, and then promote to + * cst_in_progress -- the current line contains the prompt + * and command, which the cancel handling will append ^C to. + */ + qassert(out_state == cos_idle) ; + + cmd_action_clear(cli->dispatch) ; + cli->state = cst_in_progress ; + + break ; + + case cst_in_progress: + case cst_complete: + if ((out_state == cos_more_enter) || (out_state == cos_more_wait)) + return false ; - uty_cmd_signal(vio, CMD_CANCEL) ; /* ^C */ + break ; - return true ; + default: + return false ; } ; - return false ; + cli->out_state ^= (out_state ^ cos_cancel) ; + uty_cmd_signal(cli->vf->vio, CMD_CANCEL) ; + + return true ; case ks_iac: /* Telnet command */ return uty_telnet_command(cli->vf, stroke, true) ; @@ -436,69 +462,84 @@ uty_cli_update_more(vty_cli cli) * * State of the CLI: * - * dispatched -- a command line has been dispatched, and is waiting for - * (dsp) command loop to fetch it. + * cst_active -- is busy drawing prompt, fetching input, processing same, + * etc. -- preparing the next command. + * + * cst_dispatched -- a command line has been dispatched, and is waiting for + * command loop to fetch it. + * + * Note that command line is drawn, and cursor is at + * the end of the line. + * + * cst_in_progress -- a command has been taken by the command loop, and is + * still running -- or at least no further command has + * been fetched by the command loop. + * + * The command may be an in_pipe, so there may be many + * commands to be completed before the CLI level command + * is. + * + * cst_complete -- the last CLI level command has completed, and we are + * waiting for the output to go cos_idle before continuing + * to cst_active. + * + * State of the output: * - * in_progress -- a command has been taken by the command loop, and is still - * (inp) running -- or at least no further command has been - * fetched by the command loop. + * cos_idle -- all output (other than the CLI line) is idle. * - * The command may be an in_pipe, so there may be many - * commands to be completed before the CLI level command is. + * cos_active -- the command output FIFO is being emptied. * - * or: the CLI has been closed. + * This is set when output is pushed, and cleared when + * everything is written away and is cst_complete. * - * blocked -- is in_progress and a further command is now ready to be - * (bkd) dispatched. + * cos_cancel -- all command output is frozen until the command loop + * deals with the cancel action. * - * or: the CLI has been closed. + * cos_more_enter -- in the process of entering "--more--" state * - * 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 in_progress is not set. - * When it is set, any current command line is wiped. + * cos_more_wait -- in the "--more--" state * - * 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. + * cos_monitor -- is busy outputting monitor stuff. * - * more_wait -- is in "--more--" wait state. => out_active ! - * (mwt) - * The "--more--" CLI uses the CLI output buffer to draw - * and undraw the "--more--" prompt. The buffer will - * otherwise be empty because is out_active. + * This state "qualifier" may be applied to any of the + * above states. * - * more_enter -- is in the process of entering the "--more--" wait state, - * (men) waiting to write the "--more--" prompt and prepare the - * keystroke input. + * This is set when monitor output is ready, and when it + * is set any current command line is wiped. * - * The following are the valid combinations: + * cos_paused -- is pausing after monitor output, before continuing + * to output stuff, or redraw CLI line -- in case more + * monitor stuff rolls up. * - * 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 + * This state "qualifier" may be applied to any of the + * above states. * - * There are two output FIFOs: + * If running threaded, this is set and the associated + * timer is started, when monitor output completes. + * + * NB: cos_monitor and cos_paused are mutually exclusive. + * + * NB: cos_monitor and cos_paused take priority over + * other output state. + * + * There are three output FIFOs: * * 1. for the CLI itself -- uty_cli_out and friends * - * 2. for output generated by commands -- vty_out and friends. + * 2. for monitor output + * + * 3. for output generated by commands -- vty_out and friends. + * + * The CLI FIFO is emptied whenever possible, in preference to the other + * FIFOs. + * + * The monitor FIFO is emptied in preference to the command FIFO. * - * The CLI FIFO is emptied whenever possible, in preference to the command - * FIFO. The command FIFO is emptied when out_active. While a command is - * in_progress all its output is collected in the command output buffer, - * to be written away when the command completes, or if it pushes the output + * The command FIFO is emptied when cos_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 an in_pipe, the first - * output will set out_active, and that will persist until returns to the CLI + * output will set cos_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. * @@ -527,7 +568,8 @@ uty_cli_update_more(vty_cli cli) * * The output process used the line_control structure to manage the output, and * occasionally enter the trivial "--more--" CLI. This is invisible to the - * main CLI. (See the more_wait and more_enter flags and their handling.) + * main CLI. (See the cos_more_wait and cos_more_enter states and their + * handling.) * * If the user decides to abandon output at the "--more--" prompt, then the * then contents of the command output FIFO are discarded. @@ -538,19 +580,10 @@ uty_cli_update_more(vty_cli cli) * 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. + * The parsing of commands is context sensitive, as is the prompt. The context + * depends may change during the execution of a command. So... it is not + * possible to read and dispatch a command until the previous one has + * completed. * *------------------------------------------------------------------------------ * Command line drawn state. @@ -561,17 +594,11 @@ uty_cli_update_more(vty_cli cli) * * 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. + * where the command line used to be. On entry to the CLI, it will draw the + * command line again if it has been wiped. * - * It is also used to support the "monitor" output. + * This is used for the command help/completion system and for the "monitor" + * output. */ /*============================================================================== @@ -583,7 +610,7 @@ uty_cli_update_more(vty_cli cli) static enum vty_readiness uty_cli_standard(vty_cli cli) ; static cmd_do_t uty_cli_auth(vty_cli cli) ; static enum vty_readiness uty_cli_more_wait(vty_cli cli) ; -static void uty_cli_draw(vty_cli cli) ; +static void uty_cli_draw(vty_cli cli, bool more_prompt) ; /*------------------------------------------------------------------------------ * CLI for VTY_TERMINAL @@ -606,11 +633,34 @@ uty_cli(vty_cli cli) if (cli->vf->vin_state == vf_closed) return not_ready ; /* Nothing more from CLI if closed */ - /* Standard or "--more--" CLI ? */ - if (cli->more_wait) - return uty_cli_more_wait(cli) ; - else - return uty_cli_standard(cli) ; + /* Standard or "--more--" CLI ? + * + * If cos_idle, then can run standard CLI if is cst_active or cst_complete. + * + * If cos_more_wait or cos_more_enter, then can run more_wait CLI. + * + * Note that cos_cancel, cos_monitor and cos_paused preclude entry to any + * CLI. + */ + switch (cli->out_state) + { + case cos_idle: + if (cli->state == cst_complete) + cli->state = cst_active ; + + if (cli->state == cst_active) + return uty_cli_standard(cli) ; + break ; + + case cos_more_enter: + case cos_more_wait: + return uty_cli_more_wait(cli) ; + + default: + break ; + } ; + + return not_ready ; } ; /*============================================================================== @@ -618,17 +668,18 @@ uty_cli(vty_cli cli) */ static cmd_do_t uty_cli_process(vty_cli cli) ; -static void uty_cli_response(vty_cli cli, cmd_do_t cmd_do) ; +static void uty_cli_response(vty_cli cli, cmd_do_t to_do) ; -static bool uty_cli_dispatch(vty_cli cli) ; +static void uty_cli_dispatch(vty_cli cli, cmd_do_t to_do) ; 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: + * Standard CLI for VTY_TERM + * + * Entered only if cst_active && cos_idle. Runs until: * * * runs out of keystrokes * * executes a command @@ -637,126 +688,86 @@ static void uty_cli_goto_end_if_drawn(vty_cli cli) ; * 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 + * NB: it is expected that the caller will attempt to flush any output + * generated. + * + * Returns: write_ready if the keystroke buffer is not empty and is not + * blocked, which uses write_ready to return here. */ static vty_readiness_t uty_cli_standard(vty_cli cli) { - bool need_prompt ; + cmd_do_t to_do ; VTY_ASSERT_LOCKED() ; - assert(!cli->more_wait) ; /* cannot be here in more_wait state ! */ - - /* cli_blocked is set when is waiting for a command, or some output to - * complete. - * - * NB: in both these cases, assumes that other forces are at work to - * keep things moving. - */ - if (cli->blocked || cli->out_active || cli->paused || cli->mon_active) - return not_ready ; + qassert((cli->state == cst_active) && (cli->out_state == cos_idle)) ; /* Make sure that the command line is drawn. * * If there is nothing pending, then can run the CLI until there is * something to do, or runs out of input. */ - if (!cli->drawn) - uty_cli_draw(cli) ; - - if (cli->to_do == cmd_do_nothing) - cli->to_do = cli->auth_context ? uty_cli_auth(cli) - : uty_cli_process(cli) ; + if (!cli->drawn || cli->more_drawn) + uty_cli_draw(cli, false) ; - /* If have something to do, do it if we can. */ - need_prompt = false ; + to_do = cli->to_do ; /* pick up any pending "to_do" */ - if (cli->to_do != cmd_do_nothing) - { - /* Reflect immediate response */ - uty_cli_response(cli, cli->to_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 (cli->dispatched) - cli->blocked = true ; /* waiting for previous */ - else - need_prompt = uty_cli_dispatch(cli) ; /* dispatch latest */ - } ; + if (to_do == cmd_do_nothing) + to_do = cli->auth_context ? uty_cli_auth(cli) + : uty_cli_process(cli) ; + else + cli->to_do = cmd_do_nothing ; /* clear pending "to_do" */ - /* 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 - * 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 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. + /* If we have something to do, now is the time to do it. */ - assert(!cli->paused) ; - - if (cli->blocked) - return not_ready ; - - if (!keystroke_stream_empty(cli->key_stream) || need_prompt) - return write_ready ; + if (to_do != cmd_do_nothing) + uty_cli_dispatch(cli, to_do) ; - /* The keystroke stream is empty, but CLI is not blocked. + /* If not active, must wait for some other event to continue in CLI. * - * If a command is dispatched, and output is not active, and the command - * line is not drawn, then go to paused state, so that will delay output - * of the prompt slightly (or until a keystroke arrives, or the current - * command completes, or something else happens). - * - * Note that if a command has been dispatched, and is !tilde_enabled, then - * will now be blocked, so won't be here. + * Otherwise, if the keystroke stream is not empty, use write_ready as a + * proxy for CLI ready -- no point doing anything until any buffered output + * has been written away. */ - if (cli->dispatched && !cli->out_active && !cli->drawn) - uty_cli_pause_start(cli) ; - - return read_ready ; + if ((cli->state != cst_active) || keystroke_stream_empty(cli->key_stream)) + return read_ready ; + else + return write_ready ; } ; /*------------------------------------------------------------------------------ - * Dispatch the current cli->to_do -- queueing it if necessary. + * Dispatch the given "to_do". * - * Requires that are NOT blocked and NO command is queued. + * Requires that are NOT blocked and NO command is queued etc. * - * Expects to be on new blank line, and when returns will be on new, blank - * line. + * Expects to be at the end of the command line for the command to be + * executed -- issues suitable response on the command line.. * * Generally sets cli->to_do = cmd_do_nothing and clears cli->cl to empty. * - * Can set cli->cl_do = and cli->cl to be a follow-on command. + * Can set cli->to_do = and cli->cl to be a follow-on command. * - * Returns: true <=> nothing, in fact, to do: prompt has been redrawn. + * If there is something to do, it is queued for the command loop to deal with, + * will then be cst_dispatched. NB: the current line is still drawn, and + * cursor is at the end of the line. + * + * If there is nothing to do, issues newline and the prompt, and returns. */ -static bool -uty_cli_dispatch(vty_cli cli) +static void +uty_cli_dispatch(vty_cli cli, cmd_do_t to_do) { qstring tmp ; - cmd_do_t to_do_now ; vty_io vio = cli->vf->vio ; VTY_ASSERT_LOCKED() ; /* About to dispatch a command, so must be in the following state. */ - qassert(!cli->dispatched && !cli->in_progress - && !cli->blocked && !cli->out_active) ; + qassert((cli->state == cst_active) && (cli->out_state == cos_idle)) ; qassert(cli->context->node == vio->vty->exec->context->node) ; - /* Set cli->clx to the command about to execute & pick up cli->to_do. + /* Set cli->clx to the command about to execute. * * Clear cli->cl and cli->to_do. */ @@ -764,18 +775,19 @@ uty_cli_dispatch(vty_cli cli) cli->clx = cli->cl ; cli->cl = tmp ; - to_do_now = cli->to_do ; /* current operation */ - cli->to_do = cmd_do_nothing ; /* clear */ qs_clear(cli->cl) ; /* set cl empty */ + /* Produce suitable response for the to_do_now */ + uty_cli_response(cli, to_do) ; + /* Dispatch command */ if (cli->auth_context) - to_do_now |= cmd_do_auth ; + to_do |= cmd_do_auth ; else { /* All other nodes... */ - switch (to_do_now) + switch (to_do) { case cmd_do_nothing: case cmd_do_ctrl_c: @@ -784,7 +796,7 @@ uty_cli_dispatch(vty_cli cli) break ; case cmd_do_command: - to_do_now = uty_cli_command(cli) ; + to_do = uty_cli_command(cli) ; break ; case cmd_do_ctrl_d: @@ -792,12 +804,12 @@ uty_cli_dispatch(vty_cli cli) break ; case cmd_do_ctrl_z: - to_do_now = uty_cli_command(cli) ; + to_do = uty_cli_command(cli) ; - if (to_do_now == cmd_do_nothing) - to_do_now = cmd_do_ctrl_z ; + if (to_do == cmd_do_nothing) + to_do = cmd_do_ctrl_z ; /* restore ^Z if line empty */ else - cli->to_do = cmd_do_ctrl_z ; /* defer the ^Z */ + cli->to_do = cmd_do_ctrl_z ; /* defer the ^Z */ break ; @@ -808,24 +820,19 @@ uty_cli_dispatch(vty_cli cli) cli->hp = cli->h_now ; /* in any event, back to the present */ - if (to_do_now != cmd_do_nothing) + if (to_do != cmd_do_nothing) { - cmd_action_set(cli->dispatch, to_do_now, cli->clx) ; - cli->dispatched = true ; + cmd_action_set(cli->dispatch, to_do, cli->clx) ; + cli->state = cst_dispatched ; uty_cmd_signal(vio, CMD_SUCCESS) ; - - cli->blocked = (to_do_now != cmd_do_command) || !cli->tilde_enabled ; } else { cmd_action_clear(cli->dispatch) ; - cli->dispatched = false ; - - uty_cli_draw(cli) ; + uty_cli_out_newline(cli) ; + uty_cli_draw(cli, false) ; } ; - - return !cli->dispatched ; } ; /*------------------------------------------------------------------------------ @@ -849,89 +856,35 @@ uty_cli_command(vty_cli cli) /*------------------------------------------------------------------------------ * Want another command line from the CLI. * - * If in_progress this <=> any previous command has completed, and output may - * be active writing the results away. Will: - * - * * clear cli->dispatched - * * clear cli->in_progress - * * clear cli->blocked - * - * May be in more_wait state -- so avoids touching that. - * - * 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. + * If have one, return it and CMD_SUCCESS and set cst_in_progress. + * Otherwise, return CMD_WAITING. */ extern cmd_return_code_t -uty_cli_want_command(vty_cli cli, cmd_action action, cmd_context context) +uty_cli_want_command(vty_cli cli, cmd_action action) { VTY_ASSERT_LOCKED() ; - if (cli->in_progress) - { - /* Previous command has completed - * - * Make sure state reflects the fact that we are now waiting for a - * command. - */ - cli->dispatched = false ; /* no longer have a dispatched command */ - cli->in_progress = false ; /* command complete */ - cli->blocked = false ; /* releases possibly blocked command */ - cli->paused = false ; /* override paused state */ - - *cli->context = *context ; /* make sure is up to date */ - cli->auth_context = ( (cli->context->node == AUTH_NODE) - || (cli->context->node == AUTH_ENABLE_NODE) ) ; - - /* If 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, 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) - uty_cli_draw(cli) ; - - uty_term_set_readiness(cli->vf, write_ready) ; - } - else if (cli->dispatched) + if (cli->state == cst_dispatched) { /* New command has been dispatched -- can now pass that to the * command loop -- setting it in_progress. + * + * This is the moment we issue the newline, which we want written away. */ - assert(cli->dispatch->to_do != cmd_do_nothing) ; + qassert(cli->dispatch->to_do != cmd_do_nothing) ; + qassert((cli->out_state & cos_mask) == cos_idle) ; + cmd_action_take(action, cli->dispatch) ; - cli->in_progress = true ; - } ; + cli->state = cst_in_progress ; - return cli->in_progress ? CMD_SUCCESS : CMD_WAITING ; -} ; + uty_cli_out_newline(cli) ; + uty_term_set_readiness(cli->vf, write_ready) ; -/*------------------------------------------------------------------------------ - * Start pause timer and set paused. - */ -static void -uty_cli_pause_start(vty_cli cli) -{ - qtimer_set(cli->pause_timer, qt_add_monotonic(QTIME(0.2)), NULL) ; - cli->paused = true ; + return CMD_SUCCESS ; + } ; + + return CMD_WAITING ; } ; /*============================================================================== @@ -940,13 +893,14 @@ uty_cli_pause_start(vty_cli cli) * While command output is being cleared from its FIFO, the CLI is blocked. * * When the output side signals that "--more--" is required, it sets the - * more_wait and more_enter flags. + * cos_more_enter out state. * * 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. + * "--more--" response which was typed before the prompt was issued. This is + * the cos_more_enter state. * - * The more_enter flag indicates that the CLI is in this first stage. + * Once is waiting for a keystroke, is in cos_more_wait state. */ /*------------------------------------------------------------------------------ @@ -959,30 +913,29 @@ uty_cli_enter_more_wait(vty_cli cli) { VTY_ASSERT_LOCKED() ; - qassert(cli->out_active && !cli->more_wait && !cli->drawn) ; + qassert((cli->out_state == cos_active) && !cli->drawn) ; - cli->more_wait = true ; /* new state */ - cli->more_enter = true ; /* drawing the "--more--" etc. */ + cli->out_state = cos_more_enter ; /* draw the "--more--" etc. */ } ; /*------------------------------------------------------------------------------ * Handle the "--more--" state. * - * If is paused or the monitor is active, do nothing -- those override the - * "--more--" state. + * Entered only if cos_more_enter or cos_more_wait. So does not get here if + * cos_monitor or cos_paused are set. * - * If more_enter is set, then need to make sure the "--more--" prompt is + * If cos_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. * * Note that the "--more--" state may be interrupted by monitor output, - * which, once complete, sets more_enter again. + * which, once complete, sets cos_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 + * If cos_more_wait is set, tries to steal a keystroke, and if succeeds wipes + * the "--more--" prompt and exits cos_more_wait -- and may cancel all * outstanding output. * - * EOF on input causes immediate exit from cli_more_state. + * EOF on input causes immediate exit from "--more--" * * Returns: read_ready -- waiting to steal a keystroke * write_ready -- waiting to draw or undraw the "--more--" prompt @@ -996,32 +949,32 @@ uty_cli_more_wait(vty_cli cli) VTY_ASSERT_LOCKED() ; - assert(cli->more_wait) ; /* must be in more_wait state ! */ + qassert( (cli->out_state == cos_more_enter) || + (cli->out_state == cos_more_wait) ) ; - if (cli->paused || cli->mon_active) - return not_ready ; - - /* Deal with the first stage of "--more--" */ - if (cli->more_enter) + /* Deal with the first stage of "--more--" + * + * If the "--more--" is not drawn, draw it. + * + * Hoover up any input there is pending -- probably none, but this seems + * tidy. + * + * If the "--more--" prompt has been written away, we are ready to steal + * a keystroke -- ie go to cos_more_wait. + * + * In any event, exit write_ready ready to keep things moving. + */ + if (cli->out_state == cos_more_enter) { - int get ; + if (!cli->drawn || !cli->more_drawn) + uty_cli_draw(cli, true) ; /* 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. - */ - if (!vio_fifo_empty(cli->cbuf)) - return write_ready ; + uty_term_read(cli->vf) ; /* hoover up. */ - cli->more_enter = false ; + if (vio_fifo_empty(cli->cbuf)) + cli->out_state = cos_more_wait ; - /* empty the input buffer into the keystroke stream */ - do - { - get = uty_term_read(cli->vf) ; - } while (get > 0) ; + return write_ready ; } ; /* Try to get a stolen keystroke. If the keystroke stream has hit @@ -1078,15 +1031,13 @@ uty_cli_more_wait(vty_cli cli) * Return write_ready to tidy up the screen and, unless cleared, write * some more. */ + cli->out_state = cos_active ; /* revert to active state */ if (cancel) { 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 ; + vio_lc_clear(cli->olc) ; /* clear & reset counter */ - qassert(cli->out_active) ; + cli->out_state = cos_cancel ; uty_cmd_signal(cli->vf->vio, CMD_CANCEL) ; /* ^C */ } @@ -1096,9 +1047,6 @@ uty_cli_more_wait(vty_cli cli) uty_cli_wipe(cli, 0) ; } ; - cli->more_wait = false ; /* exit more_wait */ - cli->more_enter = false ; /* tidy */ - return write_ready ; } ; @@ -1124,7 +1072,7 @@ uty_cli_more_wait(vty_cli cli) */ /*----------------------------------------------------------------------------- - * Prepare for new monitor output line. + * Prepare for new monitor output line (s). * * Wipe any existing command line. */ @@ -1135,7 +1083,7 @@ uty_cli_pre_monitor(vty_cli cli) uty_cli_wipe(cli, 0) ; - cli->mon_active = true ; /* block cli & enable empty of fifo */ + cli->out_state = (cli->out_state | cos_monitor) & ~cos_paused ; } ; /*------------------------------------------------------------------------------ @@ -1143,7 +1091,8 @@ uty_cli_pre_monitor(vty_cli cli) * * 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 + * + * If have a non-empty command line, or is cos_more_wait, redraw the command * line. * * Returns: 0 => rump was empty and no command line stuff written @@ -1154,12 +1103,31 @@ uty_cli_post_monitor(vty_cli cli) { VTY_ASSERT_LOCKED() ; - uty_cli_pause_start(cli) ; /* do not draw prompt immediately */ + /* Clear the monitor output state. + * + * Should not be cos_paused -- but clear that too. + */ + qassert((cli->out_state & (cos_monitor | cos_paused)) == cos_monitor) ; - cli->more_enter = cli->more_wait ; - /* revert to more_enter state */ + cli->out_state &= ~(cos_monitor | cos_paused) ; + + /* If was cos_more_wait, then fall back to cos_more_enter. + * + * Note that does not set cos_cancel on top of cos_more_wait/cos_more_enter ! + */ + if (cli->out_state == cos_more_wait) + cli->out_state = cos_more_enter ; + + /* If not cos_cancel, if we have a pause_timer, then set that going, now. + * + * Note that when the cli is closed any timer is freed. + */ + if ((cli->out_state != cos_cancel) && (cli->pause_timer != NULL)) + { + qtimer_set(cli->pause_timer, qt_add_monotonic(QTIME(0.2)), NULL) ; + cli->out_state |= cos_paused ; + } ; - cli->mon_active = false ; /* unblock cli & turn off output */ } ; /*============================================================================== @@ -1272,10 +1240,9 @@ uty_cli_write_s(vty_cli cli, const char *str) extern void uty_cli_out_newline(vty_cli cli) { - uty_cli_goto_end_if_drawn(cli) ; + uty_cli_goto_end_if_drawn(cli) ; /* clears cli->drawn */ uty_cli_write(cli, telnet_newline, 2) ; - cli->drawn = false ; } ; /*------------------------------------------------------------------------------ @@ -1305,16 +1272,15 @@ uty_cli_out_wipe_n(vty_cli cli, int n) * * Clears cli->drawn. */ -static void -uty_cli_cancel(vty_cli cli) +extern void +uty_cli_cancel(vty_cli cli, bool cntrl_c) { if (cli->drawn) { - uty_cli_goto_end_if_drawn(cli) ; + uty_cli_goto_end_if_drawn(cli) ; /* clears cli->drawn */ - uty_cli_write_s(cli, " ^C" TELNET_NEWLINE) ; - - cli->drawn = false ; + if (cntrl_c) + uty_cli_write_s(cli, " ^C" TELNET_NEWLINE) ; } ; } ; @@ -1322,70 +1288,52 @@ uty_cli_cancel(vty_cli cli) * If the command line is drawn and the cursor is not at the end of the line, * move the physical cursor to the end of the line. * - * Assumes about to issue newline. + * Assumes about to issue newline, so clears cli->drawn */ static void uty_cli_goto_end_if_drawn(vty_cli cli) { - ulen after ; + if (cli->drawn && !cli->more_drawn) + { + ulen after ; + + after = qs_after_cp_nn(cli->cl) ; + + if (after != 0) + uty_cli_write(cli, qs_cp_char_nn(cli->cl), after) ; + } ; - if (cli->drawn && ( (after = qs_after_cp_nn(cli->cl)) != 0 )) - uty_cli_write(cli, qs_cp_char_nn(cli->cl), after) ; + cli->drawn = false ; } ; /*------------------------------------------------------------------------------ - * Send response to the given cmd_do - * - * If no command is in progress, then will send newline to signal that the - * command is about to be dispatched. + * Send any special response -- depending on what's to_do. * - * If command is in progress, then leaves cursor on '^' to signal that is now - * waiting for previous command to complete. + * NB: at this point MUST be at end of cli->cl. */ -static const char* cli_response [2][cmd_do_count] = +static const char* cli_response [cmd_do_count] = { - { /* when not waiting for previous command to complete */ - [cmd_do_command] = "", - [cmd_do_ctrl_c] = "^C", - [cmd_do_ctrl_d] = "^D", - [cmd_do_ctrl_z] = "^Z", - [cmd_do_eof] = "^*", - [cmd_do_timed_out] = "^!" - }, - { /* when waiting for a previous command to complete */ - [cmd_do_command] = "^", + [cmd_do_command] = NULL, [cmd_do_ctrl_c] = "^C", [cmd_do_ctrl_d] = "^D", [cmd_do_ctrl_z] = "^Z", [cmd_do_eof] = "^*", - [cmd_do_timed_out] = "^!" - } + [cmd_do_timed_out] = "^!", } ; static void uty_cli_response(vty_cli cli, cmd_do_t to_do) { const char* str ; - int len ; - assert((to_do != cmd_do_nothing) && cli->drawn + qassert((to_do != cmd_do_nothing) && cli->drawn && (qs_cp_nn(cli->cl) == qs_len_nn(cli->cl))) ; + qassert(to_do < cmd_do_count) ; - str = (to_do < cmd_do_count) - ? cli_response[cli->dispatched ? 1 : 0][to_do] : NULL ; - assert(str != NULL) ; - - len = uty_cli_write_s(cli, str) ; + str = (to_do < cmd_do_count) ? cli_response[to_do] : NULL ; - if (cli->dispatched) - { - cli->extra_len = len ; - uty_cli_write_n(cli, telnet_backspaces, len) ; - } - else - { - uty_cli_out_newline(cli) ; - } ; + if (str != NULL) + uty_cli_write_s(cli, str) ; } ; /*------------------------------------------------------------------------------ @@ -1416,10 +1364,10 @@ uty_cli_wipe(vty_cli cli, int len) assert(qs_cp_nn(cli->cl) <= qs_len_nn(cli->cl)) ; /* Establish how much ahead and how much behind the cursor */ - a = cli->extra_len ; + a = 0 ; b = cli->prompt_len ; - if (!cli->more_wait) + if (!cli->more_drawn) { a += qs_len_nn(cli->cl) - qs_cp_nn(cli->cl) ; b += qs_cp_nn(cli->cl) ; @@ -1440,7 +1388,7 @@ uty_cli_wipe(vty_cli cli, int len) uty_cli_write_n(cli, telnet_backspaces, b) ; /* Nothing there any more */ - cli->drawn = false ; + cli->drawn = false ; } ; /*------------------------------------------------------------------------------ @@ -1464,10 +1412,9 @@ uty_cli_wipe(vty_cli cli, int len) * * Sets: cli->drawn = true * cli->prompt_len = length of prompt used - * cli->extra_len = 0 */ static void -uty_cli_draw(vty_cli cli) +uty_cli_draw(vty_cli cli, bool more_prompt) { const char* prompt ; size_t l_len ; @@ -1480,19 +1427,12 @@ uty_cli_draw(vty_cli cli) p_len = 0 ; l_len = 0 ; } - else if (cli->more_wait) + else if (more_prompt) { prompt = "--more--" ; p_len = strlen(prompt) ; l_len = 0 ; } - else if (cli->dispatched) - { - /* If there is a queued command, the prompt is a minimal affair. */ - prompt = "~ " ; - p_len = strlen(prompt) ; - l_len = qs_len_nn(cli->cl) ; - } else { /* The prompt depends on the node, and is expected to include the @@ -1533,8 +1473,7 @@ uty_cli_draw(vty_cli cli) /* Set about writing the prompt and the line */ cli->drawn = true ; - cli->extra_len = false ; - + cli->more_drawn = more_prompt ; cli->prompt_len = p_len ; uty_cli_write(cli, prompt, p_len) ; @@ -2008,13 +1947,12 @@ uty_cli_get_keystroke(vty_cli cli, keystroke stroke) if (stroke->type != ks_iac) return cmd_do_keystroke ; /* have a keystroke */ - /* Deal with telnet command, so invisible to upper level */ + /* Deal with telnet command, so invisible to upper level + */ uty_telnet_command(cli->vf, stroke, false) ; } else { - int get ; - qassert(stroke->type == ks_null) ; switch (stroke->value) @@ -2033,10 +1971,7 @@ uty_cli_get_keystroke(vty_cli cli, keystroke stroke) break ; } ; - get = uty_term_read(cli->vf) ; /* sets eof in key_stream - if hit eof or error */ - if ((get == 0) || (get == -1)) - return cmd_do_nothing ; + return cmd_do_nothing ; } ; } ; } ; @@ -2986,7 +2921,7 @@ uty_cli_help_finish(vty_cli cli) { if (!cli->drawn) { - uty_cli_draw(cli) ; + uty_cli_draw(cli, false) ; qs_copy(cli->cls, cli->cl) ; } ; } ; |