/** * Console - VCOM command engine * * Created on 2020/02/28 by Ondrej Hruska * * Parts are based on the console component from esp-idf * licensed under the Apache 2 license. */ #ifndef LIBCONSOLE_H #define LIBCONSOLE_H #include #include #include #include typedef enum { /* Colors */ COLOR_RESET = 0xF0, COLOR_BLACK = 0x01, COLOR_RED = 0x02, COLOR_GREEN = 0x03, COLOR_YELLOW = 0x04, COLOR_BLUE = 0x05, COLOR_MAGENTA = 0x06, COLOR_CYAN = 0x07, COLOR_WHITE = 0x08, /* Modifiers */ COLOR_NORMAL = 0x0F, COLOR_BOLD = 0x10, COLOR_UNDERLINE = 0x20, COLOR_BLINK = 0x30, COLOR_HIDE = 0x40, } console_color_t; #if CONSOLE_USE_FREERTOS #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" typedef SemaphoreHandle_t console_mutex_t; #endif #if CONSOLE_USE_PTHREADS #include typedef pthread_mutex_t console_mutex_t; #endif #if CONSOLE_USE_TERMIOS #include #endif /** * Console config struct */ struct console_config { /** * Timeout waiting for execution lock when handling a command. * This should be longer than the slowest command in the system. */ uint32_t execution_lock_timeout_ms; }; /** * Macro to init the console config struct */ #define CONSOLE_CONFIG_DEFAULTS() { \ .execution_lock_timeout_ms = 10000, \ } typedef struct console_config console_config_t; struct console_ctx; // early declaration /** * Console context */ typedef struct console_ctx console_ctx_t; /** * Console errors - return codes */ enum console_err { CONSOLE_OK = 0, /** unspecified error */ CONSOLE_ERROR = 1, /** Allocation failed */ CONSOLE_ERR_NO_MEM, /** Function call not allowed (e.g. console not inited) */ CONSOLE_ERR_BAD_CALL, /** Argument validation failed */ CONSOLE_ERR_INVALID_ARG, /** Command not recognized */ CONSOLE_ERR_UNKNOWN_CMD, /** Timeout */ CONSOLE_ERR_TIMEOUT, /** IO error (file open fail, etc.) */ CONSOLE_ERR_IO, /** Operation denied (not allowed / insufficient rights) */ CONSOLE_ERR_NOT_POSSIBLE, /** End marker */ _CONSOLE_ERR_MAX, }; /** * Print string describing console error. * In case of unknown error, the number is shown. * * The argument should be `enum console_err`, but any number is valid. */ void console_err_print_ctx(struct console_ctx *ctx, int e); // TODO error-to-string function typedef enum console_err console_err_t; // early decl's struct cmd_signature; typedef struct cmd_signature cmd_signature_t; /** * Command signature, passed as the last argument to the command handler * to perform registration. * * \note Fill only fields that differ from default (zeros/NULLs) */ struct cmd_signature { const char* command; //!< Command name, used in invocations (filled internally, do not set) const char* help; //!< Command help text, shown when called with -h const char* hint; //!< Hint text, generated from argtable if hint==NULL & argtable!=NULL bool no_history; //!< Command skips history bool custom_args; //!< Disable argtable parsing in the handler function, will be parsed manually from argv/argc /** * Argtable, struct or array that must end with arg_end(). * Used by the register function and for disambiguation. */ void* argtable; }; /** * Active console context pointer, valid only when handling a console command (otherwise NULL). * * Used by the console printf & other IO methods. */ extern struct console_ctx * console_active_ctx; /** * Function handling a callback from the console loop. */ typedef void(*console_callback_t)(console_ctx_t *ctx); /** * Console context struct */ struct console_ctx { #if CONSOLE_USE_FILE_IO_STREAMS // Streams FILE* in; //!< stdin fd, can be -1 if not available (running commands in non-interactive mode) FILE* out; //!< stdout fd #if CONSOLE_USE_TERMIOS // original termios is stored here before entering raw mode struct termios orig_termios; #endif #else void *ioctx; #endif //CONSOLE_USE_FILE_IO_STREAMS #if CONSOLE_FILE_SUPPORT char *history_file; #endif //CONSOLE_FILE_SUPPORT bool __internal_heap_allocated; bool exit_allowed; char prompt[CONSOLE_PROMPT_MAX_LEN]; //!< Prompt, can be modified by a command or `before_readline_fn` char line_buffer[CONSOLE_LINE_BUF_LEN]; /** * Callback fired in the command evaluation loop, each time before the prompt is shown and new line read. * This command can print to the output streams, change prompt, shutdown console, etc. */ console_callback_t loop_handler; /** * Callback fired before the console task shuts down */ console_callback_t shutdown_handler; /** * Shutdown requested. Console will exit as soon as possible. */ bool exit_requested; /** * Interactive mode. Enables additional outputs for user convenience. */ bool interactive; /** * Enable ANSI colors */ bool use_colors; /* These fields are valid only during command execution */ const char **argv; //!< The current argv size_t argc; //!< The current argc const cmd_signature_t *cmd; //!< Pointer to the currently executed command signature /** Used for argument validation */ uint32_t __internal_magic; }; #define CONSOLE_CTX_MAGIC 0x6f587468 /** * Command handler type. * * @param ctx - console context, including input/output files * @param reg - signature struct; if not NULL, init the static argtable, fill this struct, and return OK (0). * @return status code, 0 = OK (use cons_err_t constants if possible) */ typedef int (*console_command_t)(console_ctx_t *ctx, struct cmd_signature *reg); /** * Intialize the console * * @param config - config pointer, NULL to use defaults * @return status code */ console_err_t console_init(const console_config_t *config); /** * @brief Register console command * * If the command function is already registered, this creates an alias. * A multi-word command automatically create a command group. The group can * be described using a description string by calling `console_group_add()` * - at convenience before or after the commands are registered. * * @param name - command name (may contain spaces for "multi-part commands") * @param handler pointer to the command handler. * @return status code */ console_err_t console_cmd_register(console_command_t handler, const char *name); /** * @brief Register a command group. * * Command groups are created automatically when used. * This method can create a group with description, or attach a custom description * to an existing group. * * @param name - group name (first word of multi-part commands) * @param descr - description to attach, can be NULL * @return staus code */ console_err_t console_group_add(const char *name, const char *descr); /** * Add alias to an existing command by name. * * @param original - original command name * @param alias - command's alias * @return status code */ console_err_t console_cmd_add_alias(const char *original, const char *alias); /** * Add alias by handler function * * @param handler - command handler * @param alias - new name * @return status code */ console_err_t console_cmd_add_alias_fn(console_command_t handler, const char *alias); /** * Internal error print function. Has WEAK linkage, can be overridden. * * This function is used to report detected bugs and should not be called * in well-written "production code". * * @param msg - error message */ void console_internal_error_print(const char *msg); /** * This function is guarded by a mutex and will wait for the execution lock as * configured in console_config. * * @brief Run command line * @param[in] outf * @param[in] inf * @param cmdline command line (command name followed by a number of arguments) * @param[out] pRetval return code from the command (set if command was run) * @param[out] pCommandSig - is set to a pointer to the matched command signature, or NULL on error * @return status code */ console_err_t console_handle_cmd( console_ctx_t *ctx, const char *cmdline, int *pRetval, const struct cmd_signature **pCommandSig ); /** * Count all registered commands * * @return */ size_t console_count_commands(void); /** * Create a console IO context and init it to defaults. * * takes stdin and stdout file descriptors, or IO context (based on config flags) * * In the FD variant, pass NULL as STDIN if not available. * * @param ctx - context, if using static alloc, NULL to allocate internally. * @return the context, NULL if alloc or init fails */ console_ctx_t *console_ctx_init( console_ctx_t *ctx, #if CONSOLE_USE_FILE_IO_STREAMS FILE* inf, FILE* outf #else void * ioctx #endif ); /** * Destroy a console IO context. * * Make sure to release any user fields (ioctx, for example) beforehand. * * @attention ONLY CALL THIS IF THE CONTEXT WAS DYNAMICALLY ALLOCATED! * * @param[in,out] ctx - pointer to context, will be set to NULL. */ void console_ctx_destroy(console_ctx_t *ctx); /** * Console task * * @param[in] param - must be a valid console context (see `console_ctx_init()`) */ void console_task(void *param); /** * Variant of 'console_task' for pthreads (returns NULL) */ void* console_task_posix(void *param); #include "console_io.h" #endif //LIBCONSOLE_H