Files
EMS-ESP32/lib/uuid-console/src/uuid/console.h
2022-09-28 20:27:48 +02:00

1520 lines
48 KiB
C++

/*
* uuid-console - Microcontroller console shell
* Copyright 2019 Simon Arlott
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UUID_CONSOLE_H_
#define UUID_CONSOLE_H_
#include <Arduino.h>
#include <cstdarg>
#include <cstdint>
#include <deque>
#include <functional>
#include <limits>
#include <list>
#include <memory>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <uuid/common.h>
#include <uuid/log.h>
namespace uuid {
/**
* Console shell.
*
* - <a href="https://github.com/nomis/mcu-uuid-console/">Git Repository</a>
* - <a href="https://mcu-uuid-console.readthedocs.io/">Documentation</a>
*/
namespace console {
class Commands;
/**
* Base class for a command shell.
*
* Must be constructed within a std::shared_ptr.
*
* Requires a derived class to provide input/output. Derived classes
* should use virtual inheritance to allow the behaviour to be further
* extended.
*
* @since 0.1.0
*/
class Shell : public std::enable_shared_from_this<Shell>, public uuid::log::Handler, public ::Stream {
public:
static constexpr size_t MAX_COMMAND_LINE_LENGTH = 80; /*!< Maximum length of a command line. @since 0.1.0 */
static constexpr size_t MAX_LOG_MESSAGES = 20; /*!< Maximum number of log messages to buffer before they are output. @since 0.1.0 */
static constexpr uint8_t MAX_LINES = 5; /*!< Maximum lines in buffer */
/**
* Function to handle the response to a password entry prompt.
*
* @param[in] shell Shell instance where the password entry prompt
* was requested.
* @param[in] completed Password entry at the prompt was either
* completed (true) or aborted (false).
* @param[in] password Password entered at the prompt (may be
* empty).
* @since 0.1.0
*/
using password_function = std::function<void(Shell & shell, bool completed, const std::string & password)>;
/**
* Function to handle the end of an execution delay.
*
* @param[in] shell Shell instance where execution was delayed.
* @since 0.1.0
*/
using delay_function = std::function<void(Shell & shell)>;
/**
* Function to handle a blocking operation.
*
* @param[in] shell Shell instance where execution is blocked.
* @param[in] stop Request to return so that the shell can stop
* (true) or continue (false).
* @return True if the blocking operation is finished, otherwise
* false.
* @since 0.2.0
*/
using blocking_function = std::function<bool(Shell & shell, bool stop)>;
~Shell() override;
/**
* Loop through all registered shell objects.
*
* Call loop_one() on every Shell (if it has not been stopped).
* Any Shell that is stopped is then unregistered.
*
* @since 0.1.0
*/
static void loop_all();
/**
* Perform startup process for this shell.
*
* Register as a uuid::log::Handler at the uuid::log::Level::NOTICE
* log level, output the banner and register this Shell with the
* loop_all() set.
*
* The started() function will be called after startup is complete.
*
* Do not call this function more than once.
* Do not call this function from a static initializer.
*
* @since 0.1.0
*/
void start();
/**
* Perform one execution step of this shell.
*
* Depending on the current mode, either read input characters and
* process them or check if an execution delay has passed.
*
* @since 0.1.0
*/
void loop_one();
/**
* Determine if this shell is still running.
*
* @return Running status of this Shell, false if the Shell has
* been stopped using stop().
* @since 0.1.0
*/
bool running() const;
/**
* Stop this shell from running.
*
* If the shell is currently executing a blocking function, that
* must complete before the shell will stop.
*
* It is not possible to restart the Shell, which must be destroyed
* after it has been stopped.
*
* @since 0.1.0
*/
void stop();
/**
* Get the built-in uuid::log::Logger instance for shells.
*
* @return Logger instance.
* @since 0.1.0
*/
static inline const uuid::log::Logger & logger() {
return logger_;
}
/**
* Add a new log message.
*
* This will be put in a queue for output at the next loop_one()
* process. The queue has a maximum size of maximum_log_messages()
* and will discard the oldest message first.
*
* @param[in] message New log message, shared by all handlers.
* @since 0.1.0
*/
virtual void operator<<(std::shared_ptr<uuid::log::Message> message) override;
/**
* Get the current log level.
*
* This only affects newly received log messages, not messages that
* have already been queued.
*
* @return The current log level.
* @since 0.6.0
*/
uuid::log::Level log_level() const;
/**
* Set the current log level.
*
* This only affects newly received log messages, not messages that
* have already been queued.
*
* @param[in] level Minimum log level that the shell will receive
* messages for.
* @since 0.6.0
*/
void log_level(uuid::log::Level level);
/**
* Get the maximum length of a command line.
*
* @return The maximum length of a command line in bytes.
* @since 0.6.0
*/
size_t maximum_command_line_length() const;
/**
* Set the maximum length of a command line.
*
* Defaults to Shell::MAX_COMMAND_LINE_LENGTH.
*
* @param[in] length The maximum length of a command line in bytes.
* @since 0.6.0
*/
void maximum_command_line_length(size_t length);
/**
* Get the maximum number of queued log messages.
*
* @return The maximum number of queued log messages.
* @since 0.6.0
*/
size_t maximum_log_messages() const;
/**
* Set the maximum number of queued log messages.
*
* Defaults to Shell::MAX_LOG_MESSAGES.
*
* @param[in] count The maximum number of queued log messages.
* @since 0.6.0
*/
void maximum_log_messages(size_t count);
/**
* Get the idle timeout.
*
* @return The idle timeout in seconds.
* @since 0.7.0
*/
unsigned long idle_timeout() const;
/**
* Set the idle timeout.
*
* Defaults to 0 (no idle timeout).
*
* @param[in] timeout Idle timeout in seconds.
* @since 0.7.0
*/
void idle_timeout(unsigned long timeout);
/**
* Get the context at the top of the stack.
*
* The current context affects which commands are available.
*
* @return Current shell context.
* @since 0.1.0
*/
inline unsigned int context() const {
if (!context_.empty()) {
return context_.back();
} else {
return 0;
}
}
/**
* Push a new context onto the stack.
*
* The current context affects which commands are available.
*
* @param[in] context New context.
* @since 0.1.0
*/
inline void enter_context(unsigned int context) {
context_.emplace_back(context);
}
/**
* Pop a context off the stack.
*
* The current context affects which commands are available.
*
* @return True if there was a context to be exited, false if there
* are no more contexts to exit.
* @since 0.1.0
*/
virtual bool exit_context();
/**
* Add one or more flags to the current flags.
*
* Flags are not affected by execution context. The current flags
* affect which commands are available (for access control).
*
* @param[in] flags Flag bits to add.
* @since 0.1.0
*/
inline void add_flags(unsigned int flags) {
flags_ |= flags;
}
/**
* Check if the current flags include all of the specified flags.
*
* Flags are not affected by execution context. The current flags
* affect which commands are available (for access control).
*
* @param[in] flags Flag bits to check for.
* @return True if the current flags includes all of the specified
* flags, otherwise false.
* @since 0.1.0
*/
inline bool has_flags(unsigned int flags) const {
return (flags_ & flags) == flags;
}
/**
* Remove one or more flags from the current flags.
*
* Flags are not affected by execution context. The current flags
* affect which commands are available (for access control).
*
* @param[in] flags Flag bits to remove.
* @since 0.1.0
*/
inline void remove_flags(unsigned int flags) {
flags_ &= ~flags;
}
/**
* Prompt for a password to be entered on this shell.
*
* Password entry is not visible and can be interrupted by the
* user.
*
* The shell must not be currently executing a blocking function.
*
* @param[in] prompt Message to display prompting for password
* input.
* @param[in] function Function to be executed after the password
* has been entered prior to resuming normal
* execution.
* @since 0.1.0
*/
void enter_password(const char * prompt, password_function function);
/**
* Stop executing anything on this shell for a period of time.
*
* There is an assumption that 2^64 milliseconds uptime will always
* be enough time for this delay process.
*
* The shell must not be currently executing a blocking function.
*
* @param[in] ms Time in milliseconds to delay execution for.
* @param[in] function Function to be executed at a future time,
* prior to resuming normal execution.
* @since 0.1.0
*/
void delay_for(unsigned long ms, delay_function function);
/**
* Stop executing anything on this shell until a future time is
* reached.
*
* There is an assumption that 2^64 milliseconds uptime will always
* be enough time for this delay process.
*
* The reference time is uuid::get_uptime_ms().
*
* The shell must not be currently executing a blocking function.
*
* @param[in] ms Uptime in the future (in milliseconds) when the
* function should be executed.
* @param[in] function Function to be executed at a future time,
* prior to resuming normal execution.
* @since 0.1.0
*/
void delay_until(uint64_t ms, delay_function function);
/**
* Execute a blocking function on this shell until it returns true.
*
* The function will be executed every time loop_one() is called,
* which allows commands that do not complete immediately to be run
* asynchronously.
*
* The shell must not be currently executing a blocking function.
*
* @param[in] function Function to be executed on every loop_one()
* until normal execution resumes.
*/
void block_with(blocking_function function);
/**
* Check for available input.
*
* The shell must be currently executing a blocking function
* otherwise it will always return 0.
*
* @return The number of bytes available to read.
* @since 0.2.0
*/
int available() final override;
/**
* Read one byte from the available input.
*
* The shell must be currently executing a blocking function
* otherwise it will always return -1.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.2.0
*/
int read() final override;
/**
* Read one byte from the available input without advancing to the
* next one.
*
* The shell must be currently executing a blocking function
* otherwise it will always return -1.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.2.0
*/
int peek() final override;
/**
* Write one byte to the output stream.
*
* @param[in] data Data to be output.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t write(uint8_t data) override = 0;
/**
* Write an array of bytes to the output stream.
*
* @param[in] buffer Buffer to be output.
* @param[in] size Length of the buffer.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t write(const uint8_t * buffer, size_t size) override = 0;
/**
* Does nothing.
*
* This is a pure virtual function in Arduino's Stream class, which
* makes no sense because that class is for input and this is an
* output function. Later versions move it to Print as an empty
* virtual function so this is here for backward compatibility.
*
* @since 0.2.0
*/
void flush() override;
using ::Print::print; /*!< Include standard Arduino print() functions. */
/**
* Output a string.
*
* @param[in] data String to be output.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t print(const std::string & data);
using ::Print::println; /*!< Include standard Arduino println() functions. */
/**
* Output a string followed by CRLF end of line characters.
*
* @param[in] data String to be output.
* @return The number of bytes that were output, including CRLF.
* @since 0.1.0
*/
size_t println(const std::string & data);
/**
* Output a message.
*
* @param[in] format Format string.
* @param[in] ... Format string arguments.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t printf(const char * format, ...) /* __attribute__((format(printf, 2, 3))) */;
/**
* Output a message.
*
* @param[in] format Format string (flash string).
* @param[in] ... Format string arguments.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t printf(const __FlashStringHelper * format, ...) /* __attribute__((format(printf, 2, 3))) */;
/**
* Output a message followed by CRLF end of line characters.
*
* @param[in] format Format string.
* @param[in] ... Format string arguments.
* @return The number of bytes that were output, including CRLF.
* @since 0.1.0
*/
size_t printfln(const char * format, ...) /* __attribute__((format (printf, 2, 3))) */;
/**
* Output a message followed by CRLF end of line characters.
*
* @param[in] format Format string (flash string).
* @param[in] ... Format string arguments.
* @return The number of bytes that were output, including CRLF.
* @since 0.1.0
*/
size_t printfln(const __FlashStringHelper * format, ...) /* __attribute__((format(printf, 2, 3))) */;
/**
* Output a list of all available commands with their arguments.
*
* @since 0.4.0
*/
void print_all_available_commands();
/**
* Invoke a command on the shell.
*
* This will output a prompt with the provided command line and
* then try to execute it.
*
* Intended for use from end_of_transmission() to execute an "exit"
* or "logout" command.
*
* @param[in] line The command line to be executed.
* @since 0.1.0
*/
void invoke_command(const std::string & line);
protected:
/**
* Default constructor used by intermediate derived classes for
* multiple inheritance.
*
* This does not initialise the shell completely so the outer
* derived class must call the public constructor or there will be
* no commands. Does not put any default context on the stack.
*
* @since 0.1.0
*/
Shell() = default;
/**
* Create a new Shell with the given commands, default context and
* initial flags.
*
* The default context is put on the stack and cannot be removed.
*
* @param[in] commands Commands available for execution in this shell.
* @param[in] context Default context for the shell.
* @param[in] flags Initial flags for the shell.
* @since 0.1.0
*/
Shell(std::shared_ptr<Commands> commands, unsigned int context = 0, unsigned int flags = 0);
/**
* Output ANSI escape sequence to erase the current line.
*
* @since 0.1.0
*/
virtual void erase_current_line();
/**
* Output ANSI escape sequence to erase characters.
*
* @param[in] count The number of characters to erase.
* @since 0.1.0
*/
virtual void erase_characters(size_t count);
/**
* Startup complete event.
*
* The shell is ready to start executing commands.
* @since 0.1.0
*/
virtual void started();
/**
* Output the startup banner.
*
* There is no default banner.
* @since 0.1.0
*/
virtual void display_banner();
/**
* Get the hostname to be included in the command prompt.
*
* Defaults to "".
*
* @return The current hostname of the device, or an empty string
* for no hostname.
* @since 0.1.0
*/
virtual std::string hostname_text();
/**
* Get the text indicating the current context, to be included in
* the command prompt.
*
* Defaults to "".
*
* @return Text indicating the current context, or an empty string
* for no context description.
* @since 0.1.0
*/
virtual std::string context_text();
/**
* Get the prefix to be used at the beginning of the command
* prompt.
*
* Defaults to "".
*
* @return Text for the beginning of the command prompt, or an
* empty string.
* @since 0.1.0
*/
virtual std::string prompt_prefix();
/**
* Get the prefix to be used at the end of the command prompt.
*
* Defaults to "$".
*
* @return Text for the end of the command prompt, or an empty
* string.
* @since 0.1.0
*/
virtual std::string prompt_suffix();
/**
* The end of transmission character has been received.
*
* A command can be invoked using invoke_command(). All other
* actions may result in an inconsistent display of the command
* prompt. If the shell is stopped without invoking a command then
* a blank line should usually be printed first.
*
* If an idle timeout is set then the default action (since 0.7.2)
* is to stop the shell, otherwise there is no default action.
* @since 0.1.0
*/
virtual void end_of_transmission();
/**
* Stop request event.
*
* The shell is going to stop executing.
* @since 0.1.0
*/
virtual void stopped();
private:
/**
* Current mode of the shell.
*
* @since 0.1.0
*/
enum class Mode : uint8_t {
NORMAL, /*!< Normal command execution. @since 0.1.0 */
PASSWORD, /*!< Password entry prompt. @since 0.1.0 */
DELAY, /*!< Delay execution until a future time. @since 0.1.0 */
BLOCKING, /*!< Block execution by calling a function repeatedly. @since 0.2.0 */
};
/**
* Base class of data for a shell mode.
*
* @since 0.1.0
*/
class ModeData {
public:
virtual ~ModeData() = default;
protected:
ModeData() = default;
};
/**
* Data for the Mode::PASSWORD shell mode.
*
* @since 0.1.0
*/
class PasswordData : public ModeData {
public:
/**
* Create Mode::PASSWORD shell mode data.
*
* @param[in] password_prompt Message to display prompting for
* password input.
* @param[in] password_function Function to be executed after
* the password has been entered
* prior to resuming normal
* execution.
* @since 0.1.0
*/
PasswordData(const char * password_prompt, password_function && password_function);
~PasswordData() override = default;
const char * password_prompt_; /*!< Prompt requesting password input. @since 0.1.0 */
password_function password_function_; /*!< Function to execute after password entry. @since 0.1.0 */
};
/**
* Data for the Mode::DELAY shell mode.
*
* @since 0.1.0
*/
class DelayData : public ModeData {
public:
/**
* Create Mode::DELAY shell mode data.
*
* @param[in] delay_time Uptime in the future (in milliseconds)
* when the function should be executed.
* @param[in] delay_function Function to be executed at a
* future time, prior to resuming
* normal execution.
* @since 0.1.0
*/
DelayData(uint64_t delay_time, delay_function && delay_function);
~DelayData() override = default;
uint64_t delay_time_; /*!< Future uptime to resume execution (in milliseconds). @since 0.1.0 */
delay_function delay_function_; /*!< Function execute after delay. @since 0.1.0 */
};
/**
* Data for the Mode::BLOCKING shell mode.
*
* @since 0.2.0
*/
class BlockingData : public ModeData {
public:
/**
* Create Mode::DELAY shell mode data.
*
* @param[in] blocking_function Function to be executed on
* every loop_one() until it
* returns true.
* @since 0.2.0
*/
explicit BlockingData(blocking_function && blocking_function);
~BlockingData() override = default;
blocking_function blocking_function_; /*!< Function execute on every loop_one(). @since 0.2.0 */
bool consume_line_feed_ = true; /*!< Stream input should try to consume the first line feed following a carriage return. @since 0.2.0 */
bool stop_ = false; /*!< There is a stop pending for the shell. @since 0.2.0 */
};
/**
* Log message that has been queued.
*
* Contains an identifier sequence to indicate when log messages
* could not be output because the queue discarded one or more
* messages.
*
* @since 0.1.0
*/
class QueuedLogMessage {
public:
/**
* Create a queued log message.
*
* @param[in] id Identifier to use for the log message on the queue.
* @param[in] content Log message content.
* @since 0.1.0
*/
QueuedLogMessage(unsigned long id, std::shared_ptr<uuid::log::Message> && content);
~QueuedLogMessage() = default;
const unsigned long id_; /*!< Sequential identifier for this log message. @since 0.1.0 */
const std::shared_ptr<const uuid::log::Message> content_; /*!< Log message content. @since 0.1.0 */
};
Shell(const Shell &) = delete;
Shell & operator=(const Shell &) = delete;
/**
* Perform one execution step in Mode::NORMAL mode.
*
* Read characters and execute commands or invoke tab completion.
*
* @since 0.1.0
*/
void loop_normal();
/**
* Perform one execution step in Mode::PASSWORD mode.
*
* Read characters until interrupted or password entry is complete.
*
* @since 0.1.0
*/
void loop_password();
/**
* Perform one execution step in Mode::DELAY mode.
*
* Wait until the delay uptime is reached.
*
* @since 0.1.0
*/
void loop_delay();
/**
* Perform one execution step in Mode::BLOCKING mode.
*
* Wait until the function returns true.
*
* @since 0.2.0
*/
void loop_blocking();
/**
* Check for at least one character of available input.
*
* @return True if a character is available, otherwise false.
* @since 0.2.0
*/
virtual bool available_char() = 0;
/**
* Read one character from the available input.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.1.0
*/
virtual int read_one_char() = 0;
/**
* Read one character from the available input without advancing to
* the next one.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.2.0
*/
virtual int peek_one_char() = 0;
/**
* Output a prompt on the shell.
*
* Based on the current mode this will output the appropriate text,
* e.g. the command prompt with the current command line (for
* Mode::NORMAL mode).
*
* @since 0.1.0
*/
void display_prompt();
/**
* Output queued log messages for this shell.
*
* @since 0.1.0
*/
void output_logs();
/**
* Try to execute a command with the current command line.
*
* @since 0.1.0
*/
void process_command();
/**
* Try to complete a command from the current command line.
*
* @since 0.1.0
*/
void process_completion();
/**
* Finish password entry.
*
* The entered password is in the command line buffer.
*
* @param[in] completed Password entry at the prompt was either
* completed (true) or aborted (false).
* @since 0.1.0
*/
void process_password(bool completed);
/**
* Check idle timeout expiry.
*
* @since 0.7.2
*/
void check_idle_timeout();
/**
* Delete a word from the command line buffer.
*
* @param[in] display True if output to erased characters is
* required, false for no output.
* @since 0.1.0
*/
void delete_buffer_word(bool display);
/**
* Output a message.
*
* @param[in] format Format string.
* @param[in] ap Variable arguments pointer for format string.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t vprintf(const char * format, va_list ap);
/**
* Output a message.
*
* @param[in] format Format string (flash string).
* @param[in] ap Variable arguments pointer for format string.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t vprintf(const __FlashStringHelper * format, va_list ap);
void set_command_str(const char * str);
static const uuid::log::Logger logger_; /*!< uuid::log::Logger instance for shells. @since 0.1.0 */
static std::set<std::shared_ptr<Shell>> shells_; /*!< Registered running shells to be executed. @since 0.1.0 */
std::shared_ptr<Commands> commands_; /*!< Commands available for execution in this shell. @since 0.1.0 */
std::deque<unsigned int> context_; /*!< Context stack for this shell. Should never be empty. @since 0.1.0 */
unsigned int flags_ = 0; /*!< Current flags for this shell. Affects which commands are available. @since 0.1.0 */
unsigned long log_message_id_ = 0; /*!< The next identifier to use for queued log messages. @since 0.1.0 */
std::list<QueuedLogMessage> log_messages_; /*!< Queued log messages, in the order they were received. @since 0.1.0 */
size_t maximum_log_messages_ = MAX_LOG_MESSAGES; /*!< Maximum command line length in bytes. @since 0.6.0 */
std::string line_buffer_; /*!< Command line buffer. Limited to maximum_command_line_length() bytes. @since 0.1.0 */
std::string line_old_[MAX_LINES]; /*!< old Command line buffer.*/
uint8_t line_no_ = 0;
size_t maximum_command_line_length_ = MAX_COMMAND_LINE_LENGTH; /*!< Maximum command line length in bytes. @since 0.6.0 */
unsigned char previous_ = 0; /*!< Previous character that was entered on the command line. Used to detect CRLF line endings. @since 0.1.0 */
uint8_t cursor_ = 0; /*!< cursor position from end of line */
uint8_t esc_ = 0; /*!< esc sequence running */
Mode mode_ = Mode::NORMAL; /*!< Current execution mode. @since 0.1.0 */
std::unique_ptr<ModeData> mode_data_ = nullptr; /*!< Data associated with the current execution mode. @since 0.1.0 */
bool stopped_ = false; /*!< Indicates that the shell has been stopped. @since 0.1.0 */
bool prompt_displayed_ = false; /*!< Indicates that a command prompt has been displayed, so that the output of invoke_command() is correct. @since 0.1.0 */
uint64_t idle_time_ = 0; /*!< Time the shell became idle. @since 0.7.0 */
uint64_t idle_timeout_ = 0; /*!< Idle timeout (in milliseconds). @since 0.7.0 */
};
/**
* Representation of a command line, with parameters separated by
* spaces and an optional trailing space.
*
* @since 0.4.0
*/
class CommandLine {
public:
/**
* Create an empty command line.
*
* @since 0.4.0
*/
CommandLine() = default;
/**
* Parse a command line into separate parameters using built-in
* escaping rules.
*
* @param[in] line Command line to parse.
* @since 0.4.0
*/
explicit CommandLine(const std::string & line);
/**
* Create a command line from one or more vectors of parameters.
*
* @param[in] arguments Vectors of parameters to add to the command
* line.
* @since 0.4.0
*/
explicit CommandLine(std::initializer_list<const std::vector<std::string>> arguments);
~CommandLine() = default;
#ifdef UNIT_TEST
CommandLine(CommandLine &&) = default;
CommandLine & operator=(CommandLine &&) = default;
CommandLine(const CommandLine &) __attribute__((deprecated)) = default;
CommandLine & operator=(const CommandLine &) __attribute__((deprecated)) = default;
#endif
/**
* Format a command line from separate parameters using built-in
* escaping rules.
*
* @param[in] reserve String buffer size to preallocate.
* @return A command line, with escaping of characters sufficient
* to reproduce the same command line parameters when
* parsed.
* @since 0.4.0
*/
std::string to_string(size_t reserve = Shell::MAX_COMMAND_LINE_LENGTH) const;
/**
* Get the total size of the command line parameters, taking into
* account the trailing space.
*
* @return The size of the command line parameters, plus 1 for
* a trailing space.
* @since 0.4.0
*/
inline size_t total_size() const {
return parameters_.size() + (trailing_space ? 1 : 0);
}
/**
* Reset this command line's data.
*
* Clears the parameter list and removes any trailing space.
*
* @since 0.4.0
*/
void reset();
/**
* Escape all parameters when formatting the command line for
* output.
*
* This is the default.
*
* @since 0.5.0
*/
inline void escape_all_parameters() {
escape_parameters_ = std::numeric_limits<size_t>::max();
}
/**
* Only escape the number of parameters that currently exist when
* formatting the command line for output.
*
* Use this before appending argument help that should not be
* escaped.
*
* Empty parameters will always be escaped.
*
* @since 0.5.0
*/
inline void escape_initial_parameters() {
escape_parameters_ = parameters_.size();
}
/**
* Escape the first count parameters when formatting the command
* line for output.
*
* Use this to prevent argument help from being escaped.
*
* Empty parameters will always be escaped.
*
* @param[in] count Number of parameters to escape.
* @since 0.5.0
*/
inline void escape_initial_parameters(size_t count) {
escape_parameters_ = count;
}
/**
* Obtain the parameters for this command line.
*
* @return A reference to the parameters.
* @since 0.6.0
*/
inline std::vector<std::string> & operator*() {
return parameters_;
}
/**
* Obtain the parameters for this command line.
*
* @return A reference to the parameters.
* @since 0.6.0
*/
inline const std::vector<std::string> & operator*() const {
return parameters_;
}
/**
* Obtain the parameters for this command line.
*
* @return A pointer to the parameters.
* @since 0.4.0
*/
inline std::vector<std::string> * operator->() {
return &parameters_;
}
/**
* Obtain the parameters for this command line.
*
* @return A pointer to the parameters.
* @since 0.4.0
*/
inline const std::vector<std::string> * operator->() const {
return &parameters_;
}
/**
* Compare a command line to another command line for equality.
*
* @param[in] lhs Left-hand side command line.
* @param[in] rhs Right-hand side command line.
* @return True if the command lines are equal, otherwise false.
* @since 0.4.0
*/
friend inline bool operator==(const CommandLine & lhs, const CommandLine & rhs) {
return (lhs.trailing_space == rhs.trailing_space) && (lhs.parameters_ == rhs.parameters_);
}
/**
* Compare this command line to another command line for inequality.
*
* @param[in] lhs Left-hand side command line.
* @param[in] rhs Right-hand side command line.
* @return True if the command lines are not equal, otherwise false.
* @since 0.4.0
*/
friend inline bool operator!=(const CommandLine & lhs, const CommandLine & rhs) {
return !(lhs == rhs);
}
bool trailing_space = false; /*!< Command line has a trailing space. @since 0.4.0 */
private:
std::vector<std::string> parameters_; /*!< Separate command line parameters. @since 0.4.0 */
size_t escape_parameters_ = std::numeric_limits<size_t>::max(); /*!< Number of initial arguments to escape in output. @since 0.5.0 */
};
/**
* Container of commands for use by a Shell.
*
* These should normally be stored in a std::shared_ptr and reused.
*
* @since 0.1.0
*/
class Commands {
public:
/**
* Result of a command completion operation.
*
* Each space-delimited parameter is a separate string.
*
* @since 0.1.0
*/
struct Completion {
std::list<CommandLine> help; /*!< Suggestions for matching commands. @since 0.1.0 */
CommandLine replacement; /*!< Replacement matching full or partial command line. @since 0.1.0 */
};
/**
* Result of a command execution operation.
*
* @since 0.1.0
*/
struct Execution {
const __FlashStringHelper * error; /*!< Error message if the command could not be executed. @since 0.1.0 */
};
/**
* Function to handle a command.
*
* @param[in] shell Shell instance that is executing the command.
* @param[in] arguments Command line arguments.
* @since 0.1.0
*/
using command_function = std::function<void(Shell & shell, std::vector<std::string> & arguments)>;
/**
* Function to obtain completions for a command line.
*
* This is a vector instead of set so that a custom sort order can
* be used. It should normally be sorted lexicographically so that
* the list of options is not confusing.
*
* @param[in] shell Shell instance that has a command line matching
* this command.
* @param[in] arguments Command line arguments prior to (but
* excluding) the argument being completed.
* @return Possible values for the next argument on the command
* line.
* @since 0.1.0
*/
using argument_completion_function = std::function<const std::vector<std::string>(Shell & shell, const std::vector<std::string> & arguments)>;
/**
* Function to apply an operation to a command.
*
* @param[in] name Name of the command as a std::vector of strings.
* @param[in] arguments Help text for arguments that the command
* accepts as a std::vector of strings.
* @since 0.4.0
*/
using apply_function = std::function<void(std::vector<std::string> & name, std::vector<std::string> & arguments)>;
/**
* Construct a new container of commands for use by a Shell.
*
* This should normally be stored in a std::shared_ptr and reused.
*
* @since 0.1.0
*/
Commands() = default;
~Commands() = default;
/**
* Add a command with no arguments to the list of commands in this
* container.
*
* The shell context for the command will default to 0 and not
* require any flags for it to be available.
*
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] function Function to be used when the command is
* executed.
* @since 0.2.0
*/
void add_command(const string_vector & name, command_function function);
/**
* Add a command with arguments to the list of commands in this
* container.
*
* The shell context for the command will default to 0 and not
* require any flags for it to be available.
*
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] arguments Help text for arguments that the command
* accepts as a std::vector of flash strings
* (use "<" to indicate a required argument).
* @param[in] function Function to be used when the command is
* executed.
* @since 0.2.0
*/
void add_command(const string_vector & name, const string_vector & arguments, command_function function);
/**
* Add a command with arguments and automatic argument completion
* to the list of commands in this container.
*
* The shell context for the command will default to 0 and not
* require any flags for it to be available.
*
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] arguments Help text for arguments that the command
* accepts as a std::vector of flash strings
* (use "<" to indicate a required argument).
* @param[in] function Function to be used when the command is
* executed.
* @param[in] arg_function Function to be used to perform argument
* completions for this command.
* @since 0.2.0
*/
void add_command(const string_vector & name, const string_vector & arguments, command_function function, argument_completion_function arg_function);
/**
* Add a command with no arguments to the list of commands in this
* container.
*
* @param[in] context Shell context in which this command is
* available.
* @param[in] flags Shell flags that must be set for this command
* to be available.
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] function Function to be used when the command is
* executed.
* @since 0.1.0
*/
void add_command(unsigned int context, unsigned int flags, const string_vector & name, command_function function);
/**
* Add a command with arguments to the list of commands in this
* container.
*
* @param[in] context Shell context in which this command is
* available.
* @param[in] flags Shell flags that must be set for this command
* to be available.
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] arguments Help text for arguments that the command
* accepts as a std::vector of flash strings
* (use "<" to indicate a required argument).
* @param[in] function Function to be used when the command is
* executed.
* @since 0.1.0
*/
void add_command(unsigned int context, unsigned int flags, const string_vector & name, const string_vector & arguments, command_function function);
/**
* Add a command with arguments and automatic argument completion
* to the list of commands in this container.
*
* @param[in] context Shell context in which this command is
* available.
* @param[in] flags Shell flags that must be set for this command
* to be available.
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] arguments Help text for arguments that the command
* accepts as a std::vector of flash strings
* (use "<" to indicate a required argument).
* @param[in] function Function to be used when the command is
* executed.
* @param[in] arg_function Function to be used to perform argument
* completions for this command.
* @since 0.1.0
*/
void add_command(unsigned int context,
unsigned int flags,
const string_vector & name,
const string_vector & arguments,
command_function function,
argument_completion_function arg_function);
/**
* Execute a command for a Shell if it exists in the current
* context and with the current flags.
*
* @param[in] shell Shell that is executing the command.
* @param[in] command_line Command line parameters.
* @return An object describing the result of the command execution
* operation.
* @since 0.1.0
*/
Execution execute_command(Shell & shell, CommandLine && command_line);
/**
* Complete a partial command for a Shell if it exists in the
* current context and with the current flags.
*
* @param[in] shell Shell that is completing the command.
* @param[in] command_line Command line parameters.
* @return An object describing the result of the command
* completion operation.
* @since 0.1.0
*/
Completion complete_command(Shell & shell, const CommandLine & command_line);
/**
* Applies the given function object f to all commands in the
* current context and with the current flags.
*
* @param[in] shell Shell that is accessing commands.
* @param[in] f Function to apply to each command.
* @since 0.4.0
*/
void for_each_available_command(Shell & shell, apply_function f) const;
void remove_context_commands(unsigned int context); // added by proddy
void remove_all_commands(); // added by proddy
private:
/**
* Command for execution on a Shell.
* @since 0.1.0
*/
class Command {
public:
/**
* Create a command for execution on a Shell.
*
* @param[in] flags Shell flags that must be set for this command
* to be available.
* @param[in] name Name of the command as a std::vector of flash
* strings.
* @param[in] arguments Help text for arguments that the command
* accepts as a std::vector of flash strings
* (use "<" to indicate a required argument).
* @param[in] function Function to be used when the command is
* executed.
* @param[in] arg_function Function to be used to perform argument
* completions for this command.
* @since 0.1.0
*/
Command(unsigned int flags, const string_vector name, const string_vector arguments, command_function function, argument_completion_function arg_function);
~Command();
/**
* Determine the minimum number of arguments for this command
* based on the help text for the arguments that begin with the
* "<" character.
*
* @return The minimum number of arguments for this command.
* @since 0.1.0
*/
size_t minimum_arguments() const;
/**
* Determine the maximum number of arguments for this command
* based on the length of help text for the arguments.
*
* @return The maximum number of arguments for this command.
* @since 0.1.0
*/
inline size_t maximum_arguments() const {
return arguments_.size();
}
unsigned int flags_; /*!< Shell flags that must be set for this command to be available. @since 0.1.0 */
const string_vector name_; /*!< Name of the command as a std::vector of flash strings. @since 0.1.0 */
const string_vector arguments_; /*!< Help text for arguments that the command accepts as a std::vector of flash strings. @since 0.1.0 */
command_function function_; /*!< Function to be used when the command is executed. @since 0.1.0 */
argument_completion_function arg_function_; /*!< Function to be used to perform argument completions for this command. @since 0.1.0 */
private:
Command(const Command &) = delete;
Command & operator=(const Command &) = delete;
};
/**
* Result of a command find operation.
*
* Each matching command is returned in a map grouped by size.
* @since 0.1.0
*/
struct Match {
std::multimap<size_t, const Command *> exact; /*!< Commands that match the command line exactly, grouped by the size of the command names. @since 0.1.0 */
std::multimap<size_t, const Command *> partial; /*!< Commands that the command line partially matches, grouped by the size of the command names. @since 0.1.0 */
};
/**
* Find commands by matching them against the command line.
*
* @param[in] shell Shell that is accessing commands.
* @param[in] command_line Command line parmeters.
* @return An object describing the result of the command find
* operation.
* @since 0.1.0
*/
Match find_command(Shell & shell, const CommandLine & command_line);
/**
* Find the longest common prefix from a shortest match of commands.
*
* @param[in] commands All commands that matched (at least 2).
* @param[out] longest_name The longest common prefix as a list of
* strings.
* @return True if the longest common prefix is made up of whole
* components, false if the last part is constructed from
* a partial component.
* @since 0.1.0
*/
static bool find_longest_common_prefix(const std::multimap<size_t, const Command *> & commands, std::vector<std::string> & longest_name);
/**
* Find the longest common prefix from a list of potential arguments.
*
* @param[in] arguments All potential arguments (at least 2).
* @return The longest common prefix, which could be empty.
* @since 0.1.0
*/
static std::string find_longest_common_prefix(const std::vector<std::string> & arguments);
std::multimap<unsigned int, Command> commands_; /*!< Commands stored in this container, separated by context. @since 0.1.0 */
};
/**
* A command shell console using a Stream for input/output.
*
* Must be constructed within a std::shared_ptr.
*
* Derived classes must call the Shell constructor explicitly.
*
* @since 0.1.0
*/
class StreamConsole : virtual public Shell {
public:
/**
* Create a new StreamConsole shell with the given commands,
* default context and initial flags.
*
* The default context is put on the stack and cannot be removed.
*
* @param[in] commands Commands available for execution in this shell.
* @param[in] stream Stream used for the input/output of this shell.
* @param[in] context Default context for the shell.
* @param[in] flags Initial flags for the shell.
* @since 0.1.0
*/
StreamConsole(std::shared_ptr<Commands> commands, Stream & stream, unsigned int context = 0, unsigned int flags = 0);
~StreamConsole() override = default;
/**
* Write one byte to the output stream.
*
* @param[in] data Data to be output.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t write(uint8_t data) override;
/**
* Write an array of bytes to the output stream.
*
* @param[in] buffer Buffer to be output.
* @param[in] size Length of the buffer.
* @return The number of bytes that were output.
* @since 0.1.0
*/
size_t write(const uint8_t * buffer, size_t size) override;
protected:
/**
* Constructor used by intermediate derived classes for multiple
* inheritance.
*
* This does not initialise the shell completely so the outer
* derived class must call the public constructor or there will be
* no commands. Does not put any default context on the stack.
*
* @since 0.1.0
*/
explicit StreamConsole(Stream & stream);
private:
StreamConsole(const StreamConsole &) = delete;
StreamConsole & operator=(const StreamConsole &) = delete;
/**
* Check for at least one character of available input.
*
* @return True if a character is available, otherwise false.
* @since 0.2.0
*/
bool available_char() override;
/**
* Read one character from the available input.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.1.0
*/
int read_one_char() override;
/**
* Read one character from the available input without advancing to
* the next one.
*
* @return An unsigned char if input is available, otherwise -1.
* @since 0.2.0
*/
int peek_one_char() override;
Stream & stream_; /*!< Stream used for the input/output of this shell. @since 0.1.0 */
};
} // namespace console
} // namespace uuid
#endif