mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
[FL-3965] Separate cli_shell into toolbox (#4175)
* cli_shell: separate into toolbox * fix: cmd flags * fix formatting * cli: increase default stack depth * cli_shell: fix loader lock logic * cli: fix command flags * fix f18 * speaker_debug: fix * cli_registry: fix docs * ufbt: rename cli target back * cli: rename app and record * cli: fix and simplify help command * cli_master_shell: fix ext commands * fix formatting * cli: rename master to main * fix formatting --------- Co-authored-by: hedger <hedger@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <notification/notification.h>
|
#include <notification/notification.h>
|
||||||
#include <music_worker/music_worker.h>
|
#include <music_worker/music_worker.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <toolbox/args.h>
|
#include <toolbox/args.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
#include <toolbox/cli/cli_registry.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
|
|
||||||
#define TAG "SpeakerDebug"
|
#define TAG "SpeakerDebug"
|
||||||
|
|
||||||
@@ -20,14 +21,14 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
MusicWorker* music_worker;
|
MusicWorker* music_worker;
|
||||||
FuriMessageQueue* message_queue;
|
FuriMessageQueue* message_queue;
|
||||||
Cli* cli;
|
CliRegistry* cli_registry;
|
||||||
} SpeakerDebugApp;
|
} SpeakerDebugApp;
|
||||||
|
|
||||||
static SpeakerDebugApp* speaker_app_alloc(void) {
|
static SpeakerDebugApp* speaker_app_alloc(void) {
|
||||||
SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp));
|
SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp));
|
||||||
app->music_worker = music_worker_alloc();
|
app->music_worker = music_worker_alloc();
|
||||||
app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage));
|
app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage));
|
||||||
app->cli = furi_record_open(RECORD_CLI);
|
app->cli_registry = furi_record_open(RECORD_CLI);
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +97,8 @@ static void speaker_app_run(SpeakerDebugApp* app, const char* arg) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);
|
cli_registry_add_command(
|
||||||
|
app->cli_registry, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app);
|
||||||
|
|
||||||
SpeakerDebugAppMessage message;
|
SpeakerDebugAppMessage message;
|
||||||
FuriStatus status;
|
FuriStatus status;
|
||||||
@@ -111,7 +113,7 @@ static void speaker_app_run(SpeakerDebugApp* app, const char* arg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cli_delete_command(app->cli, CLI_COMMAND);
|
cli_registry_delete_command(app->cli_registry, CLI_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t speaker_debug_app(void* arg) {
|
int32_t speaker_debug_app(void* arg) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "tests/test_api.h"
|
#include "tests/test_api.h"
|
||||||
|
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
#include <toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <rpc/rpc.h>
|
#include <rpc/rpc.h>
|
||||||
#include <rpc/rpc_i.h>
|
#include <rpc/rpc_i.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
#include <storage/filesystem_api_defines.h>
|
#include <storage/filesystem_api_defines.h>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <toolbox/cli/cli_registry.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
|
|
||||||
#include "test_runner.h"
|
#include "test_runner.h"
|
||||||
|
|
||||||
@@ -14,8 +16,9 @@ void unit_tests_cli(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
|
|
||||||
void unit_tests_on_system_start(void) {
|
void unit_tests_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
cli_registry_add_command(
|
||||||
|
registry, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "usb_uart_bridge.h"
|
#include "usb_uart_bridge.h"
|
||||||
#include "usb_cdc.h"
|
#include "usb_cdc.h"
|
||||||
#include <cli/cli_vcp.h>
|
#include <cli/cli_vcp.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <toolbox/api_lock.h>
|
#include <toolbox/api_lock.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <furi_hal_usb_cdc.h>
|
#include <furi_hal_usb_cdc.h>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#include <cli/cli_commands.h>
|
#include <cli/cli_main_commands.h>
|
||||||
#include <toolbox/args.h>
|
#include <toolbox/args.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
@@ -240,4 +240,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_string_free(cmd);
|
furi_string_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(ikey, execute, CliCommandFlagDefault, 1024);
|
CLI_COMMAND_INTERFACE(ikey, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include <cli/cli_commands.h>
|
#include <cli/cli_main_commands.h>
|
||||||
#include <infrared.h>
|
#include <infrared.h>
|
||||||
#include <infrared_worker.h>
|
#include <infrared_worker.h>
|
||||||
#include <furi_hal_infrared.h>
|
#include <furi_hal_infrared.h>
|
||||||
@@ -554,4 +554,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_string_free(command);
|
furi_string_free(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(ir, execute, CliCommandFlagDefault, 2048);
|
CLI_COMMAND_INTERFACE(ir, execute, CliCommandFlagDefault, 2048, CLI_APPID);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <cli/cli_commands.h>
|
#include <cli/cli_main_commands.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <lib/lfrfid/lfrfid_worker.h>
|
#include <lib/lfrfid/lfrfid_worker.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
@@ -566,4 +566,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_string_free(cmd);
|
furi_string_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(rfid, execute, CliCommandFlagDefault, 2048);
|
CLI_COMMAND_INTERFACE(rfid, execute, CliCommandFlagDefault, 2048, CLI_APPID);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <gui/view_dispatcher.h>
|
#include <gui/view_dispatcher.h>
|
||||||
#include <gui/scene_manager.h>
|
#include <gui/scene_manager.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <notification/notification_messages.h>
|
#include <notification/notification_messages.h>
|
||||||
|
|
||||||
#include <gui/modules/submenu.h>
|
#include <gui/modules/submenu.h>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <cli/cli.h>
|
#include <cli/cli_main_commands.h>
|
||||||
#include <cli/cli_commands.h>
|
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <lib/toolbox/hex.h>
|
#include <lib/toolbox/hex.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
@@ -65,4 +64,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_string_free(cmd);
|
furi_string_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(nfc, execute, CliCommandFlagDefault, 1024);
|
CLI_COMMAND_INTERFACE(nfc, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#include <cli/cli_commands.h>
|
#include <cli/cli_main_commands.h>
|
||||||
#include <power/power_service/power.h>
|
#include <power/power_service/power.h>
|
||||||
#include <cli/cli_commands.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
#include <toolbox/args.h>
|
#include <toolbox/args.h>
|
||||||
|
|
||||||
#include <one_wire/one_wire_host.h>
|
#include <one_wire/one_wire_host.h>
|
||||||
@@ -64,4 +64,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_string_free(cmd);
|
furi_string_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(onewire, execute, CliCommandFlagDefault, 1024);
|
CLI_COMMAND_INTERFACE(onewire, execute, CliCommandFlagDefault, 1024, CLI_APPID);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../subghz_i.h"
|
#include "../subghz_i.h"
|
||||||
#include <lib/subghz/devices/devices.h>
|
#include <lib/subghz/devices/devices.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
typedef struct SubGhzChatWorker SubGhzChatWorker;
|
typedef struct SubGhzChatWorker SubGhzChatWorker;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
|
||||||
#include <cli/cli_commands.h>
|
#include <cli/cli_main_commands.h>
|
||||||
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
|
|
||||||
#include <lib/subghz/subghz_keystore.h>
|
#include <lib/subghz/subghz_keystore.h>
|
||||||
#include <lib/subghz/receiver.h>
|
#include <lib/subghz/receiver.h>
|
||||||
@@ -1184,4 +1185,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_string_free(cmd);
|
furi_string_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(subghz, execute, CliCommandFlagDefault, 2048);
|
CLI_COMMAND_INTERFACE(subghz, execute, CliCommandFlagDefault, 2048, CLI_APPID);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cli/cli.h>
|
|
||||||
|
|
||||||
void subghz_on_system_start(void);
|
void subghz_on_system_start(void);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
|
#include <toolbox/cli/cli_registry.h>
|
||||||
|
|
||||||
#include <ble/ble.h>
|
#include <ble/ble.h>
|
||||||
#include "bt_settings.h"
|
#include "bt_settings.h"
|
||||||
@@ -230,8 +231,8 @@ static void bt_cli(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
|
|
||||||
void bt_on_system_start(void) {
|
void bt_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, RECORD_BT, CliCommandFlagDefault, bt_cli, NULL);
|
cli_registry_add_command(registry, "bt", CliCommandFlagDefault, bt_cli, NULL);
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#else
|
#else
|
||||||
UNUSED(bt_cli);
|
UNUSED(bt_cli);
|
||||||
|
|||||||
@@ -4,17 +4,9 @@ App(
|
|||||||
entry_point="cli_on_system_start",
|
entry_point="cli_on_system_start",
|
||||||
cdefines=["SRV_CLI"],
|
cdefines=["SRV_CLI"],
|
||||||
sources=[
|
sources=[
|
||||||
"cli.c",
|
|
||||||
"shell/cli_shell.c",
|
|
||||||
"shell/cli_shell_completions.c",
|
|
||||||
"shell/cli_shell_line.c",
|
|
||||||
"cli_commands.c",
|
|
||||||
"cli_command_gpio.c",
|
"cli_command_gpio.c",
|
||||||
"cli_ansi.c",
|
"cli_main_commands.c",
|
||||||
],
|
"cli_main_shell.c",
|
||||||
sdk_headers=[
|
|
||||||
"cli.h",
|
|
||||||
"cli_ansi.h",
|
|
||||||
],
|
],
|
||||||
# This STARTUP has to be processed before those that depend on the "cli" record.
|
# This STARTUP has to be processed before those that depend on the "cli" record.
|
||||||
# "cli" used to be a SERVICE, but it's been converted into a STARTUP in order to
|
# "cli" used to be a SERVICE, but it's been converted into a STARTUP in order to
|
||||||
@@ -49,3 +41,11 @@ App(
|
|||||||
requires=["cli"],
|
requires=["cli"],
|
||||||
sources=["commands/neofetch.c"],
|
sources=["commands/neofetch.c"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
App(
|
||||||
|
appid="cli_subshell_demo",
|
||||||
|
apptype=FlipperAppType.PLUGIN,
|
||||||
|
entry_point="cli_subshell_demo_ep",
|
||||||
|
requires=["cli"],
|
||||||
|
sources=["commands/subshell_demo.c"],
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,184 +0,0 @@
|
|||||||
#include "cli.h"
|
|
||||||
#include "cli_i.h"
|
|
||||||
#include "cli_commands.h"
|
|
||||||
#include "cli_ansi.h"
|
|
||||||
#include <toolbox/pipe.h>
|
|
||||||
|
|
||||||
#define TAG "cli"
|
|
||||||
|
|
||||||
struct Cli {
|
|
||||||
CliCommandTree_t commands;
|
|
||||||
FuriMutex* mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
Cli* cli_alloc(void) {
|
|
||||||
Cli* cli = malloc(sizeof(Cli));
|
|
||||||
CliCommandTree_init(cli->commands);
|
|
||||||
cli->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
|
||||||
return cli;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_add_command(
|
|
||||||
Cli* cli,
|
|
||||||
const char* name,
|
|
||||||
CliCommandFlag flags,
|
|
||||||
CliExecuteCallback callback,
|
|
||||||
void* context) {
|
|
||||||
cli_add_command_ex(cli, name, flags, callback, context, CLI_BUILTIN_COMMAND_STACK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_add_command_ex(
|
|
||||||
Cli* cli,
|
|
||||||
const char* name,
|
|
||||||
CliCommandFlag flags,
|
|
||||||
CliExecuteCallback callback,
|
|
||||||
void* context,
|
|
||||||
size_t stack_size) {
|
|
||||||
furi_check(cli);
|
|
||||||
furi_check(name);
|
|
||||||
furi_check(callback);
|
|
||||||
|
|
||||||
// the shell always attaches the pipe to the stdio, thus both flags can't be used at once
|
|
||||||
if(flags & CliCommandFlagUseShellThread) furi_check(!(flags & CliCommandFlagDontAttachStdio));
|
|
||||||
|
|
||||||
FuriString* name_str;
|
|
||||||
name_str = furi_string_alloc_set(name);
|
|
||||||
// command cannot contain spaces
|
|
||||||
furi_check(furi_string_search_char(name_str, ' ') == FURI_STRING_FAILURE);
|
|
||||||
|
|
||||||
CliCommand command = {
|
|
||||||
.context = context,
|
|
||||||
.execute_callback = callback,
|
|
||||||
.flags = flags,
|
|
||||||
.stack_depth = stack_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
CliCommandTree_set_at(cli->commands, name_str, command);
|
|
||||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
|
||||||
|
|
||||||
furi_string_free(name_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_delete_command(Cli* cli, const char* name) {
|
|
||||||
furi_check(cli);
|
|
||||||
FuriString* name_str;
|
|
||||||
name_str = furi_string_alloc_set(name);
|
|
||||||
furi_string_trim(name_str);
|
|
||||||
|
|
||||||
size_t name_replace;
|
|
||||||
do {
|
|
||||||
name_replace = furi_string_replace(name_str, " ", "_");
|
|
||||||
} while(name_replace != FURI_STRING_FAILURE);
|
|
||||||
|
|
||||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
CliCommandTree_erase(cli->commands, name_str);
|
|
||||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
|
||||||
|
|
||||||
furi_string_free(name_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cli_get_command(Cli* cli, FuriString* command, CliCommand* result) {
|
|
||||||
furi_assert(cli);
|
|
||||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
CliCommand* data = CliCommandTree_get(cli->commands, command);
|
|
||||||
if(data) *result = *data;
|
|
||||||
|
|
||||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
|
||||||
|
|
||||||
return !!data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_remove_external_commands(Cli* cli) {
|
|
||||||
furi_check(cli);
|
|
||||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
|
|
||||||
// FIXME FL-3977: memory leak somewhere within this function
|
|
||||||
|
|
||||||
CliCommandTree_t internal_cmds;
|
|
||||||
CliCommandTree_init(internal_cmds);
|
|
||||||
for
|
|
||||||
M_EACH(item, cli->commands, CliCommandTree_t) {
|
|
||||||
if(!(item->value_ptr->flags & CliCommandFlagExternal))
|
|
||||||
CliCommandTree_set_at(internal_cmds, *item->key_ptr, *item->value_ptr);
|
|
||||||
}
|
|
||||||
CliCommandTree_move(cli->commands, internal_cmds);
|
|
||||||
|
|
||||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_enumerate_external_commands(Cli* cli) {
|
|
||||||
furi_check(cli);
|
|
||||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
FURI_LOG_D(TAG, "Enumerating external commands");
|
|
||||||
|
|
||||||
cli_remove_external_commands(cli);
|
|
||||||
|
|
||||||
// iterate over files in plugin directory
|
|
||||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
||||||
File* plugin_dir = storage_file_alloc(storage);
|
|
||||||
|
|
||||||
if(storage_dir_open(plugin_dir, CLI_COMMANDS_PATH)) {
|
|
||||||
char plugin_filename[64];
|
|
||||||
FuriString* plugin_name = furi_string_alloc();
|
|
||||||
|
|
||||||
while(storage_dir_read(plugin_dir, NULL, plugin_filename, sizeof(plugin_filename))) {
|
|
||||||
FURI_LOG_T(TAG, "Plugin: %s", plugin_filename);
|
|
||||||
furi_string_set_str(plugin_name, plugin_filename);
|
|
||||||
furi_string_replace_all_str(plugin_name, ".fal", "");
|
|
||||||
furi_string_replace_at(plugin_name, 0, 4, ""); // remove "cli_" in the beginning
|
|
||||||
CliCommand command = {
|
|
||||||
.context = NULL,
|
|
||||||
.execute_callback = NULL,
|
|
||||||
.flags = CliCommandFlagExternal,
|
|
||||||
};
|
|
||||||
CliCommandTree_set_at(cli->commands, plugin_name, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
furi_string_free(plugin_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
storage_file_free(plugin_dir);
|
|
||||||
furi_record_close(RECORD_STORAGE);
|
|
||||||
|
|
||||||
FURI_LOG_D(TAG, "Finished enumerating external commands");
|
|
||||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_lock_commands(Cli* cli) {
|
|
||||||
furi_check(cli);
|
|
||||||
furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_unlock_commands(Cli* cli) {
|
|
||||||
furi_check(cli);
|
|
||||||
furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
CliCommandTree_t* cli_get_commands(Cli* cli) {
|
|
||||||
furi_check(cli);
|
|
||||||
furi_check(furi_mutex_get_owner(cli->mutex) == furi_thread_get_current_id());
|
|
||||||
return &cli->commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side) {
|
|
||||||
if(pipe_state(side) == PipeStateBroken) return true;
|
|
||||||
if(!pipe_bytes_available(side)) return false;
|
|
||||||
char c = getchar();
|
|
||||||
return c == CliKeyETX;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
|
||||||
furi_check(cmd);
|
|
||||||
furi_check(arg);
|
|
||||||
furi_check(usage);
|
|
||||||
|
|
||||||
printf("%s: illegal option -- %s\r\nusage: %s %s", cmd, arg, cmd, usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_on_system_start(void) {
|
|
||||||
Cli* cli = cli_alloc();
|
|
||||||
cli_commands_init(cli);
|
|
||||||
cli_enumerate_external_commands(cli);
|
|
||||||
furi_record_create(RECORD_CLI, cli);
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file cli.h
|
|
||||||
* API for registering commands with the CLI
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <furi.h>
|
|
||||||
#include <m-array.h>
|
|
||||||
#include "cli_ansi.h"
|
|
||||||
#include <toolbox/pipe.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define RECORD_CLI "cli"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CliCommandFlagDefault = 0, /**< Default */
|
|
||||||
CliCommandFlagParallelSafe = (1 << 0), /**< Safe to run in parallel with other apps */
|
|
||||||
CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */
|
|
||||||
CliCommandFlagDontAttachStdio = (1 << 2), /**< Do no attach I/O pipe to thread stdio */
|
|
||||||
CliCommandFlagUseShellThread =
|
|
||||||
(1
|
|
||||||
<< 3), /**< Don't start a separate thread to run the command in. Incompatible with DontAttachStdio */
|
|
||||||
|
|
||||||
// internal flags (do not set them yourselves!)
|
|
||||||
|
|
||||||
CliCommandFlagExternal = (1 << 4), /**< The command comes from a .fal file */
|
|
||||||
} CliCommandFlag;
|
|
||||||
|
|
||||||
/** Cli type anonymous structure */
|
|
||||||
typedef struct Cli Cli;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief CLI execution callback pointer. Implement this interface and use
|
|
||||||
* `add_cli_command`.
|
|
||||||
*
|
|
||||||
* This callback will be called from a separate thread spawned just for your
|
|
||||||
* command. The pipe will be installed as the thread's stdio, so you can use
|
|
||||||
* `printf`, `getchar` and other standard functions to communicate with the
|
|
||||||
* user.
|
|
||||||
*
|
|
||||||
* @param [in] pipe Pipe that can be used to send and receive data. If
|
|
||||||
* `CliCommandFlagDontAttachStdio` was not set, you can
|
|
||||||
* also use standard C functions (printf, getc, etc.) to
|
|
||||||
* access this pipe.
|
|
||||||
* @param [in] args String with what was passed after the command
|
|
||||||
* @param [in] context Whatever you provided to `cli_add_command`
|
|
||||||
*/
|
|
||||||
typedef void (*CliExecuteCallback)(PipeSide* pipe, FuriString* args, void* context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Registers a command with the CLI. Provides less options than the `_ex`
|
|
||||||
* counterpart.
|
|
||||||
*
|
|
||||||
* @param [in] cli Pointer to CLI instance
|
|
||||||
* @param [in] name Command name
|
|
||||||
* @param [in] flags CliCommandFlag
|
|
||||||
* @param [in] callback Callback function
|
|
||||||
* @param [in] context Custom context
|
|
||||||
*/
|
|
||||||
void cli_add_command(
|
|
||||||
Cli* cli,
|
|
||||||
const char* name,
|
|
||||||
CliCommandFlag flags,
|
|
||||||
CliExecuteCallback callback,
|
|
||||||
void* context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Registers a command with the CLI. Provides more options than the
|
|
||||||
* non-`_ex` counterpart.
|
|
||||||
*
|
|
||||||
* @param [in] cli Pointer to CLI instance
|
|
||||||
* @param [in] name Command name
|
|
||||||
* @param [in] flags CliCommandFlag
|
|
||||||
* @param [in] callback Callback function
|
|
||||||
* @param [in] context Custom context
|
|
||||||
* @param [in] stack_size Thread stack size
|
|
||||||
*/
|
|
||||||
void cli_add_command_ex(
|
|
||||||
Cli* cli,
|
|
||||||
const char* name,
|
|
||||||
CliCommandFlag flags,
|
|
||||||
CliExecuteCallback callback,
|
|
||||||
void* context,
|
|
||||||
size_t stack_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Deletes a cli command
|
|
||||||
*
|
|
||||||
* @param [in] cli pointer to cli instance
|
|
||||||
* @param [in] name command name
|
|
||||||
*/
|
|
||||||
void cli_delete_command(Cli* cli, const char* name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Unregisters all external commands
|
|
||||||
*
|
|
||||||
* @param [in] cli pointer to the cli instance
|
|
||||||
*/
|
|
||||||
void cli_remove_external_commands(Cli* cli);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reloads the list of externally available commands
|
|
||||||
*
|
|
||||||
* @param [in] cli pointer to cli instance
|
|
||||||
*/
|
|
||||||
void cli_enumerate_external_commands(Cli* cli);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Detects if Ctrl+C has been pressed or session has been terminated
|
|
||||||
*
|
|
||||||
* @param [in] side Pointer to pipe side given to the command thread
|
|
||||||
* @warning This function also assumes that the pipe is installed as the
|
|
||||||
* thread's stdio
|
|
||||||
* @warning This function will consume 1 byte from the pipe
|
|
||||||
*/
|
|
||||||
bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side);
|
|
||||||
|
|
||||||
/** Print unified cmd usage tip
|
|
||||||
*
|
|
||||||
* @param cmd cmd name
|
|
||||||
* @param usage usage tip
|
|
||||||
* @param arg arg passed by user
|
|
||||||
*/
|
|
||||||
void cli_print_usage(const char* cmd, const char* usage, const char* arg);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
|
||||||
void cli_command_gpio_print_usage(void) {
|
void cli_command_gpio_print_usage(void) {
|
||||||
printf("Usage:\r\n");
|
printf("Usage:\r\n");
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cli_i.h"
|
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
void cli_command_gpio(PipeSide* pipe, FuriString* args, void* context);
|
void cli_command_gpio(PipeSide* pipe, FuriString* args, void* context);
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cli.h"
|
|
||||||
#include <flipper_application/flipper_application.h>
|
|
||||||
|
|
||||||
void cli_commands_init(Cli* cli);
|
|
||||||
|
|
||||||
#define PLUGIN_APP_ID "cli"
|
|
||||||
#define PLUGIN_API_VERSION 1
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char* name;
|
|
||||||
CliExecuteCallback execute_callback;
|
|
||||||
CliCommandFlag flags;
|
|
||||||
size_t stack_depth;
|
|
||||||
} CliCommandDescriptor;
|
|
||||||
|
|
||||||
#define CLI_COMMAND_INTERFACE(name, execute_callback, flags, stack_depth) \
|
|
||||||
static const CliCommandDescriptor cli_##name##_desc = { \
|
|
||||||
#name, \
|
|
||||||
&execute_callback, \
|
|
||||||
flags, \
|
|
||||||
stack_depth, \
|
|
||||||
}; \
|
|
||||||
\
|
|
||||||
static const FlipperAppPluginDescriptor plugin_descriptor = { \
|
|
||||||
.appid = PLUGIN_APP_ID, \
|
|
||||||
.ep_api_version = PLUGIN_API_VERSION, \
|
|
||||||
.entry_point = &cli_##name##_desc, \
|
|
||||||
}; \
|
|
||||||
\
|
|
||||||
const FlipperAppPluginDescriptor* cli_##name##_ep(void) { \
|
|
||||||
return &plugin_descriptor; \
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "cli_commands.h"
|
#include "cli_main_commands.h"
|
||||||
#include "cli_command_gpio.h"
|
#include "cli_command_gpio.h"
|
||||||
#include "cli_ansi.h"
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
#include "cli.h"
|
|
||||||
|
|
||||||
#include <core/thread.h>
|
#include <core/thread.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
@@ -56,49 +55,6 @@ void cli_command_info(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_command_help(PipeSide* pipe, FuriString* args, void* context) {
|
|
||||||
UNUSED(pipe);
|
|
||||||
UNUSED(args);
|
|
||||||
UNUSED(context);
|
|
||||||
printf("Available commands:" ANSI_FG_GREEN);
|
|
||||||
|
|
||||||
// count non-hidden commands
|
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
|
||||||
cli_lock_commands(cli);
|
|
||||||
CliCommandTree_t* commands = cli_get_commands(cli);
|
|
||||||
size_t commands_count = CliCommandTree_size(*commands);
|
|
||||||
|
|
||||||
// create iterators starting at different positions
|
|
||||||
const size_t columns = 3;
|
|
||||||
const size_t commands_per_column = (commands_count / columns) + (commands_count % columns);
|
|
||||||
CliCommandTree_it_t iterators[columns];
|
|
||||||
for(size_t c = 0; c < columns; c++) {
|
|
||||||
CliCommandTree_it(iterators[c], *commands);
|
|
||||||
for(size_t i = 0; i < c * commands_per_column; i++)
|
|
||||||
CliCommandTree_next(iterators[c]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// print commands
|
|
||||||
for(size_t r = 0; r < commands_per_column; r++) {
|
|
||||||
printf("\r\n");
|
|
||||||
|
|
||||||
for(size_t c = 0; c < columns; c++) {
|
|
||||||
if(!CliCommandTree_end_p(iterators[c])) {
|
|
||||||
const CliCommandTree_itref_t* item = CliCommandTree_cref(iterators[c]);
|
|
||||||
printf("%-30s", furi_string_get_cstr(*item->key_ptr));
|
|
||||||
CliCommandTree_next(iterators[c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(ANSI_RESET
|
|
||||||
"\r\nIf you just added a new command and can't see it above, run `reload_ext_cmds`");
|
|
||||||
printf(ANSI_RESET "\r\nFind out more: https://docs.flipper.net/development/cli");
|
|
||||||
|
|
||||||
cli_unlock_commands(cli);
|
|
||||||
furi_record_close(RECORD_CLI);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_command_uptime(PipeSide* pipe, FuriString* args, void* context) {
|
void cli_command_uptime(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
UNUSED(pipe);
|
UNUSED(pipe);
|
||||||
UNUSED(args);
|
UNUSED(args);
|
||||||
@@ -514,16 +470,6 @@ void cli_command_i2c(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
furi_hal_i2c_release(&furi_hal_i2c_handle_external);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_command_reload_external(PipeSide* pipe, FuriString* args, void* context) {
|
|
||||||
UNUSED(pipe);
|
|
||||||
UNUSED(args);
|
|
||||||
UNUSED(context);
|
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
|
||||||
cli_enumerate_external_commands(cli);
|
|
||||||
furi_record_close(RECORD_CLI);
|
|
||||||
printf("OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Echoes any bytes it receives except ASCII ETX (0x03, Ctrl+C)
|
* Echoes any bytes it receives except ASCII ETX (0x03, Ctrl+C)
|
||||||
*/
|
*/
|
||||||
@@ -545,27 +491,32 @@ void cli_command_echo(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_commands_init(Cli* cli) {
|
void cli_main_commands_init(CliRegistry* registry) {
|
||||||
cli_add_command(cli, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
cli_registry_add_command(
|
||||||
cli_add_command(cli, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
|
registry, "!", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||||
cli_add_command(cli, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
cli_registry_add_command(registry, "info", CliCommandFlagParallelSafe, cli_command_info, NULL);
|
||||||
cli_add_command(
|
cli_registry_add_command(
|
||||||
cli, "reload_ext_cmds", CliCommandFlagDefault, cli_command_reload_external, NULL);
|
registry, "device_info", CliCommandFlagParallelSafe, cli_command_info, (void*)true);
|
||||||
|
|
||||||
cli_add_command(cli, "?", CliCommandFlagParallelSafe, cli_command_help, NULL);
|
cli_registry_add_command(
|
||||||
cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL);
|
registry, "uptime", CliCommandFlagParallelSafe, cli_command_uptime, NULL);
|
||||||
|
cli_registry_add_command(registry, "date", CliCommandFlagParallelSafe, cli_command_date, NULL);
|
||||||
|
cli_registry_add_command(registry, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
||||||
|
cli_registry_add_command(registry, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);
|
||||||
|
cli_registry_add_command(registry, "top", CliCommandFlagParallelSafe, cli_command_top, NULL);
|
||||||
|
cli_registry_add_command(registry, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
|
||||||
|
cli_registry_add_command(
|
||||||
|
registry, "free_blocks", CliCommandFlagDefault, cli_command_free_blocks, NULL);
|
||||||
|
cli_registry_add_command(registry, "echo", CliCommandFlagParallelSafe, cli_command_echo, NULL);
|
||||||
|
|
||||||
cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, NULL);
|
cli_registry_add_command(registry, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL);
|
||||||
cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL);
|
cli_registry_add_command(registry, "led", CliCommandFlagDefault, cli_command_led, NULL);
|
||||||
cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL);
|
cli_registry_add_command(registry, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL);
|
||||||
cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL);
|
cli_registry_add_command(registry, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL);
|
||||||
cli_add_command(cli, "top", CliCommandFlagParallelSafe, cli_command_top, NULL);
|
}
|
||||||
cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL);
|
|
||||||
cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL);
|
void cli_on_system_start(void) {
|
||||||
cli_add_command(cli, "echo", CliCommandFlagParallelSafe, cli_command_echo, NULL);
|
CliRegistry* registry = cli_registry_alloc();
|
||||||
|
cli_main_commands_init(registry);
|
||||||
cli_add_command(cli, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL);
|
furi_record_create(RECORD_CLI, registry);
|
||||||
cli_add_command(cli, "led", CliCommandFlagDefault, cli_command_led, NULL);
|
|
||||||
cli_add_command(cli, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL);
|
|
||||||
cli_add_command(cli, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL);
|
|
||||||
}
|
}
|
||||||
9
applications/services/cli/cli_main_commands.h
Normal file
9
applications/services/cli/cli_main_commands.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <toolbox/cli/cli_registry.h>
|
||||||
|
|
||||||
|
#define RECORD_CLI "cli"
|
||||||
|
#define CLI_APPID "cli"
|
||||||
|
|
||||||
|
void cli_main_commands_init(CliRegistry* registry);
|
||||||
46
applications/services/cli/cli_main_shell.c
Normal file
46
applications/services/cli/cli_main_shell.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "cli_main_shell.h"
|
||||||
|
#include "cli_main_commands.h"
|
||||||
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
|
#include <toolbox/cli/shell/cli_shell.h>
|
||||||
|
#include <furi_hal_version.h>
|
||||||
|
|
||||||
|
void cli_main_motd(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
printf(ANSI_FLIPPER_BRAND_ORANGE
|
||||||
|
"\r\n"
|
||||||
|
" _.-------.._ -,\r\n"
|
||||||
|
" .-\"```\"--..,,_/ /`-, -, \\ \r\n"
|
||||||
|
" .:\" /:/ /'\\ \\ ,_..., `. | |\r\n"
|
||||||
|
" / ,----/:/ /`\\ _\\~`_-\"` _;\r\n"
|
||||||
|
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n"
|
||||||
|
" | | | 0 | | .-' ,/` /\r\n"
|
||||||
|
" | ,..\\ \\ ,.-\"` ,/` /\r\n"
|
||||||
|
" ; : `/`\"\"\\` ,/--==,/-----,\r\n"
|
||||||
|
" | `-...| -.___-Z:_______J...---;\r\n"
|
||||||
|
" : ` _-'\r\n"
|
||||||
|
" _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n"
|
||||||
|
"| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n"
|
||||||
|
"| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n"
|
||||||
|
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
|
||||||
|
"\r\n" ANSI_FG_BR_WHITE "Welcome to Flipper Zero Command Line Interface!\r\n"
|
||||||
|
"Read the manual: https://docs.flipper.net/development/cli\r\n"
|
||||||
|
"Run `help` or `?` to list available commands\r\n"
|
||||||
|
"\r\n" ANSI_RESET);
|
||||||
|
|
||||||
|
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
||||||
|
if(firmware_version) {
|
||||||
|
printf(
|
||||||
|
"Firmware version: %s %s (%s%s built on %s)\r\n",
|
||||||
|
version_get_gitbranch(firmware_version),
|
||||||
|
version_get_version(firmware_version),
|
||||||
|
version_get_githash(firmware_version),
|
||||||
|
version_get_dirty_flag(firmware_version) ? "-dirty" : "",
|
||||||
|
version_get_builddate(firmware_version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CliCommandExternalConfig cli_main_ext_config = {
|
||||||
|
.search_directory = "/ext/apps_data/cli/plugins",
|
||||||
|
.fal_prefix = "cli_",
|
||||||
|
.appid = CLI_APPID,
|
||||||
|
};
|
||||||
7
applications/services/cli/cli_main_shell.h
Normal file
7
applications/services/cli/cli_main_shell.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
|
||||||
|
void cli_main_motd(void* context);
|
||||||
|
|
||||||
|
extern const CliCommandExternalConfig cli_main_ext_config;
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
#include "cli_vcp.h"
|
#include "cli_vcp.h"
|
||||||
#include "shell/cli_shell.h"
|
|
||||||
#include <furi_hal_usb_cdc.h>
|
#include <furi_hal_usb_cdc.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
#include <toolbox/cli/shell/cli_shell.h>
|
||||||
|
#include "cli_main_shell.h"
|
||||||
|
#include "cli_main_commands.h"
|
||||||
|
|
||||||
#define TAG "CliVcp"
|
#define TAG "CliVcp"
|
||||||
|
|
||||||
@@ -47,10 +49,12 @@ struct CliVcp {
|
|||||||
FuriHalUsbInterface* previous_interface;
|
FuriHalUsbInterface* previous_interface;
|
||||||
|
|
||||||
PipeSide* own_pipe;
|
PipeSide* own_pipe;
|
||||||
|
PipeSide* shell_pipe;
|
||||||
bool is_currently_transmitting;
|
bool is_currently_transmitting;
|
||||||
size_t previous_tx_length;
|
size_t previous_tx_length;
|
||||||
|
|
||||||
FuriThread* shell;
|
CliRegistry* main_registry;
|
||||||
|
CliShell* shell;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============
|
// ============
|
||||||
@@ -210,13 +214,15 @@ static void cli_vcp_internal_event_happened(void* context) {
|
|||||||
// wait for previous shell to stop
|
// wait for previous shell to stop
|
||||||
furi_check(!cli_vcp->own_pipe);
|
furi_check(!cli_vcp->own_pipe);
|
||||||
if(cli_vcp->shell) {
|
if(cli_vcp->shell) {
|
||||||
furi_thread_join(cli_vcp->shell);
|
cli_shell_join(cli_vcp->shell);
|
||||||
furi_thread_free(cli_vcp->shell);
|
cli_shell_free(cli_vcp->shell);
|
||||||
|
pipe_free(cli_vcp->shell_pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start shell thread
|
// start shell thread
|
||||||
PipeSideBundle bundle = pipe_alloc(VCP_BUF_SIZE, 1);
|
PipeSideBundle bundle = pipe_alloc(VCP_BUF_SIZE, 1);
|
||||||
cli_vcp->own_pipe = bundle.alices_side;
|
cli_vcp->own_pipe = bundle.alices_side;
|
||||||
|
cli_vcp->shell_pipe = bundle.bobs_side;
|
||||||
pipe_attach_to_event_loop(cli_vcp->own_pipe, cli_vcp->event_loop);
|
pipe_attach_to_event_loop(cli_vcp->own_pipe, cli_vcp->event_loop);
|
||||||
pipe_set_callback_context(cli_vcp->own_pipe, cli_vcp);
|
pipe_set_callback_context(cli_vcp->own_pipe, cli_vcp);
|
||||||
pipe_set_data_arrived_callback(
|
pipe_set_data_arrived_callback(
|
||||||
@@ -224,7 +230,9 @@ static void cli_vcp_internal_event_happened(void* context) {
|
|||||||
pipe_set_space_freed_callback(
|
pipe_set_space_freed_callback(
|
||||||
cli_vcp->own_pipe, cli_vcp_shell_ready, FuriEventLoopEventFlagEdge);
|
cli_vcp->own_pipe, cli_vcp_shell_ready, FuriEventLoopEventFlagEdge);
|
||||||
furi_delay_ms(33); // we are too fast, minicom isn't ready yet
|
furi_delay_ms(33); // we are too fast, minicom isn't ready yet
|
||||||
cli_vcp->shell = cli_shell_start(bundle.bobs_side);
|
cli_vcp->shell = cli_shell_alloc(
|
||||||
|
cli_main_motd, NULL, cli_vcp->shell_pipe, cli_vcp->main_registry, &cli_main_ext_config);
|
||||||
|
cli_shell_start(cli_vcp->shell);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event & CliVcpInternalEventRx) {
|
if(event & CliVcpInternalEventRx) {
|
||||||
@@ -260,6 +268,8 @@ static CliVcp* cli_vcp_alloc(void) {
|
|||||||
furi_event_loop_subscribe_thread_flags(
|
furi_event_loop_subscribe_thread_flags(
|
||||||
cli_vcp->event_loop, cli_vcp_internal_event_happened, cli_vcp);
|
cli_vcp->event_loop, cli_vcp_internal_event_happened, cli_vcp);
|
||||||
|
|
||||||
|
cli_vcp->main_registry = furi_record_open(RECORD_CLI);
|
||||||
|
|
||||||
return cli_vcp;
|
return cli_vcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "../cli_commands.h"
|
#include "../cli_main_commands.h"
|
||||||
|
|
||||||
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
UNUSED(pipe);
|
UNUSED(pipe);
|
||||||
@@ -7,4 +7,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
puts("Hello, World!");
|
puts("Hello, World!");
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(hello_world, execute, CliCommandFlagDefault, 768);
|
CLI_COMMAND_INTERFACE(hello_world, execute, CliCommandFlagParallelSafe, 768, CLI_APPID);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "../cli_commands.h"
|
#include "../cli_main_commands.h"
|
||||||
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
#include <toolbox/version.h>
|
#include <toolbox/version.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <furi_hal_info.h>
|
#include <furi_hal_info.h>
|
||||||
@@ -156,4 +157,4 @@ static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
#undef NEOFETCH_COLOR
|
#undef NEOFETCH_COLOR
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI_COMMAND_INTERFACE(neofetch, execute, CliCommandFlagDefault, 2048);
|
CLI_COMMAND_INTERFACE(neofetch, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID);
|
||||||
|
|||||||
43
applications/services/cli/commands/subshell_demo.c
Normal file
43
applications/services/cli/commands/subshell_demo.c
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "../cli_main_commands.h"
|
||||||
|
#include <toolbox/cli/cli_registry.h>
|
||||||
|
#include <toolbox/cli/shell/cli_shell.h>
|
||||||
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
|
|
||||||
|
#define RAINBOW_SUBCOMMAND \
|
||||||
|
ANSI_FG_RED "s" ANSI_FG_YELLOW "u" ANSI_FG_BLUE "b" ANSI_FG_GREEN "c" ANSI_FG_MAGENTA \
|
||||||
|
"o" ANSI_FG_RED "m" ANSI_FG_YELLOW "m" ANSI_FG_BLUE "a" ANSI_FG_GREEN \
|
||||||
|
"n" ANSI_FG_MAGENTA "d"
|
||||||
|
|
||||||
|
static void subcommand(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
|
UNUSED(pipe);
|
||||||
|
UNUSED(args);
|
||||||
|
UNUSED(context);
|
||||||
|
printf("This is a ✨" RAINBOW_SUBCOMMAND ANSI_RESET "✨!");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void motd(void* context) {
|
||||||
|
UNUSED(context);
|
||||||
|
printf("\r\n");
|
||||||
|
printf("+------------------------------------+\r\n");
|
||||||
|
printf("| Hello world! |\r\n");
|
||||||
|
printf("| This is the " ANSI_FG_GREEN "MOTD" ANSI_RESET " for our " ANSI_FG_BLUE
|
||||||
|
"subshell" ANSI_RESET "! |\r\n");
|
||||||
|
printf("+------------------------------------+\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
|
UNUSED(args);
|
||||||
|
UNUSED(context);
|
||||||
|
CliRegistry* registry = cli_registry_alloc();
|
||||||
|
cli_registry_add_command(registry, "subcommand", CliCommandFlagParallelSafe, subcommand, NULL);
|
||||||
|
|
||||||
|
CliShell* shell = cli_shell_alloc(motd, NULL, pipe, registry, NULL);
|
||||||
|
cli_shell_set_prompt(shell, "subshell");
|
||||||
|
cli_shell_start(shell);
|
||||||
|
cli_shell_join(shell);
|
||||||
|
|
||||||
|
cli_shell_free(shell);
|
||||||
|
cli_registry_free(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLI_COMMAND_INTERFACE(subshell_demo, execute, CliCommandFlagParallelSafe, 2048, CLI_APPID);
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <furi.h>
|
|
||||||
#include <toolbox/pipe.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CLI_SHELL_STACK_SIZE (4 * 1024U)
|
|
||||||
|
|
||||||
FuriThread* cli_shell_start(PipeSide* pipe);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
#include <cli/cli.h>
|
#include <cli/cli_main_commands.h>
|
||||||
|
#include <toolbox/cli/cli_registry.h>
|
||||||
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
|
|
||||||
void crypto_cli_print_usage(void) {
|
void crypto_cli_print_usage(void) {
|
||||||
printf("Usage:\r\n");
|
printf("Usage:\r\n");
|
||||||
@@ -319,8 +321,8 @@ static void crypto_cli(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
|
|
||||||
void crypto_on_system_start(void) {
|
void crypto_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
|
cli_registry_add_command(registry, "crypto", CliCommandFlagDefault, crypto_cli, NULL);
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#else
|
#else
|
||||||
UNUSED(crypto_cli);
|
UNUSED(crypto_cli);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "desktop_i.h"
|
#include "desktop_i.h"
|
||||||
|
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <cli/cli_vcp.h>
|
#include <cli/cli_vcp.h>
|
||||||
|
|
||||||
#include <gui/gui_i.h>
|
#include <gui/gui_i.h>
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <furi_hal_gpio.h>
|
#include <furi_hal_gpio.h>
|
||||||
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
|
#define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2)
|
||||||
@@ -93,8 +94,10 @@ int32_t input_srv(void* p) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub);
|
cli_registry_add_command(
|
||||||
|
registry, "input", CliCommandFlagParallelSafe, input_cli, event_pubsub);
|
||||||
|
furi_record_close(RECORD_CLI);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
InputPinState pin_states[input_pins_count];
|
InputPinState pin_states[input_pins_count];
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
#include <toolbox/args.h>
|
#include <toolbox/args.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <applications.h>
|
#include <applications.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <lib/toolbox/strint.h>
|
#include <lib/toolbox/strint.h>
|
||||||
@@ -141,8 +142,9 @@ static void loader_cli(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
|
|
||||||
void loader_on_system_start(void) {
|
void loader_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);
|
cli_registry_add_command(
|
||||||
|
registry, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL);
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#else
|
#else
|
||||||
UNUSED(loader_cli);
|
UNUSED(loader_cli);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include "power_cli.h"
|
#include "power_cli.h"
|
||||||
|
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <power/power_service/power.h>
|
#include <power/power_service/power.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
@@ -114,10 +115,8 @@ void power_cli(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
|
|
||||||
void power_on_system_start(void) {
|
void power_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
|
cli_registry_add_command(registry, "power", CliCommandFlagParallelSafe, power_cli, NULL);
|
||||||
cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL);
|
|
||||||
|
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#else
|
#else
|
||||||
UNUSED(power_cli);
|
UNUSED(power_cli);
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
|
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <m-dict.h>
|
#include <m-dict.h>
|
||||||
@@ -435,9 +436,14 @@ void rpc_on_system_start(void* p) {
|
|||||||
|
|
||||||
rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||||
|
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(
|
cli_registry_add_command(
|
||||||
cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc);
|
registry,
|
||||||
|
"start_rpc_session",
|
||||||
|
CliCommandFlagParallelSafe,
|
||||||
|
rpc_cli_command_start_session,
|
||||||
|
rpc);
|
||||||
|
furi_record_close(RECORD_CLI);
|
||||||
|
|
||||||
furi_record_create(RECORD_RPC, rpc);
|
furi_record_create(RECORD_RPC, rpc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <rpc/rpc.h>
|
#include <rpc/rpc.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include <flipper.pb.h>
|
#include <flipper.pb.h>
|
||||||
#include <cli/cli.h>
|
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <toolbox/cli/cli_ansi.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <lib/toolbox/args.h>
|
#include <lib/toolbox/args.h>
|
||||||
#include <lib/toolbox/dir_walk.h>
|
#include <lib/toolbox/dir_walk.h>
|
||||||
#include <lib/toolbox/md5_calc.h>
|
#include <lib/toolbox/md5_calc.h>
|
||||||
@@ -695,16 +697,15 @@ static void storage_cli_factory_reset(PipeSide* pipe, FuriString* args, void* co
|
|||||||
|
|
||||||
void storage_on_system_start(void) {
|
void storage_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command_ex(
|
cli_registry_add_command(
|
||||||
cli,
|
registry,
|
||||||
"storage",
|
"storage",
|
||||||
CliCommandFlagParallelSafe | CliCommandFlagUseShellThread,
|
CliCommandFlagParallelSafe | CliCommandFlagUseShellThread,
|
||||||
storage_cli,
|
storage_cli,
|
||||||
NULL,
|
NULL);
|
||||||
512);
|
cli_registry_add_command(
|
||||||
cli_add_command(
|
registry, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
|
||||||
cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL);
|
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#else
|
#else
|
||||||
UNUSED(storage_cli_factory_reset);
|
UNUSED(storage_cli_factory_reset);
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
#include "js_app_i.h"
|
#include "js_app_i.h"
|
||||||
#include <toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
#include <assets_icons.h>
|
#include <assets_icons.h>
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <toolbox/pipe.h>
|
#include <toolbox/pipe.h>
|
||||||
|
|
||||||
#define TAG "JS app"
|
#define TAG "JS app"
|
||||||
@@ -209,8 +210,8 @@ void js_cli_execute(PipeSide* pipe, FuriString* args, void* context) {
|
|||||||
|
|
||||||
void js_app_on_system_start(void) {
|
void js_app_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, "js", CliCommandFlagDefault, js_cli_execute, NULL);
|
cli_registry_add_command(registry, "js", CliCommandFlagDefault, js_cli_execute, NULL);
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <furi_hal.h>
|
#include <furi_hal.h>
|
||||||
#include <cli/cli.h>
|
#include <toolbox/cli/cli_command.h>
|
||||||
|
#include <cli/cli_main_commands.h>
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <loader/loader.h>
|
#include <loader/loader.h>
|
||||||
#include <toolbox/path.h>
|
#include <toolbox/path.h>
|
||||||
@@ -106,8 +107,8 @@ static void updater_start_app(void* context, uint32_t arg) {
|
|||||||
|
|
||||||
void updater_on_system_start(void) {
|
void updater_on_system_start(void) {
|
||||||
#ifdef SRV_CLI
|
#ifdef SRV_CLI
|
||||||
Cli* cli = (Cli*)furi_record_open(RECORD_CLI);
|
CliRegistry* registry = furi_record_open(RECORD_CLI);
|
||||||
cli_add_command(cli, "update", CliCommandFlagDefault, updater_cli_ep, NULL);
|
cli_registry_add_command(registry, "update", CliCommandFlagDefault, updater_cli_ep, NULL);
|
||||||
furi_record_close(RECORD_CLI);
|
furi_record_close(RECORD_CLI);
|
||||||
#else
|
#else
|
||||||
UNUSED(updater_cli_ep);
|
UNUSED(updater_cli_ep);
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ env.Append(
|
|||||||
],
|
],
|
||||||
SDK_HEADERS=[
|
SDK_HEADERS=[
|
||||||
File("api_lock.h"),
|
File("api_lock.h"),
|
||||||
|
File("cli/cli_ansi.h"),
|
||||||
|
File("cli/cli_command.h"),
|
||||||
|
File("cli/cli_registry.h"),
|
||||||
|
File("cli/shell/cli_shell.h"),
|
||||||
File("compress.h"),
|
File("compress.h"),
|
||||||
File("manchester_decoder.h"),
|
File("manchester_decoder.h"),
|
||||||
File("manchester_encoder.h"),
|
File("manchester_encoder.h"),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cli.h"
|
#include <furi.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
17
lib/toolbox/cli/cli_command.c
Normal file
17
lib/toolbox/cli/cli_command.c
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "cli_command.h"
|
||||||
|
#include "cli_ansi.h"
|
||||||
|
|
||||||
|
bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side) {
|
||||||
|
if(pipe_state(side) == PipeStateBroken) return true;
|
||||||
|
if(!pipe_bytes_available(side)) return false;
|
||||||
|
char c = getchar();
|
||||||
|
return c == CliKeyETX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_print_usage(const char* cmd, const char* usage, const char* arg) {
|
||||||
|
furi_check(cmd);
|
||||||
|
furi_check(arg);
|
||||||
|
furi_check(usage);
|
||||||
|
|
||||||
|
printf("%s: illegal option -- %s\r\nusage: %s %s", cmd, arg, cmd, usage);
|
||||||
|
}
|
||||||
103
lib/toolbox/cli/cli_command.h
Normal file
103
lib/toolbox/cli/cli_command.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* @file cli_command.h
|
||||||
|
* Command metadata and helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <toolbox/pipe.h>
|
||||||
|
#include <lib/flipper_application/flipper_application.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CLI_PLUGIN_API_VERSION 1
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CliCommandFlagDefault = 0, /**< Default */
|
||||||
|
CliCommandFlagParallelSafe = (1 << 0), /**< Safe to run in parallel with other apps */
|
||||||
|
CliCommandFlagInsomniaSafe = (1 << 1), /**< Safe to run with insomnia mode on */
|
||||||
|
CliCommandFlagDontAttachStdio = (1 << 2), /**< Do no attach I/O pipe to thread stdio */
|
||||||
|
CliCommandFlagUseShellThread =
|
||||||
|
(1
|
||||||
|
<< 3), /**< Don't start a separate thread to run the command in. Incompatible with DontAttachStdio */
|
||||||
|
|
||||||
|
// internal flags (do not set them yourselves!)
|
||||||
|
|
||||||
|
CliCommandFlagExternal = (1 << 4), /**< The command comes from a .fal file */
|
||||||
|
} CliCommandFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief CLI command execution callback pointer
|
||||||
|
*
|
||||||
|
* This callback will be called from a separate thread spawned just for your
|
||||||
|
* command. The pipe will be installed as the thread's stdio, so you can use
|
||||||
|
* `printf`, `getchar` and other standard functions to communicate with the
|
||||||
|
* user.
|
||||||
|
*
|
||||||
|
* @param [in] pipe Pipe that can be used to send and receive data. If
|
||||||
|
* `CliCommandFlagDontAttachStdio` was not set, you can
|
||||||
|
* also use standard C functions (printf, getc, etc.) to
|
||||||
|
* access this pipe.
|
||||||
|
* @param [in] args String with what was passed after the command
|
||||||
|
* @param [in] context Whatever you provided to `cli_add_command`
|
||||||
|
*/
|
||||||
|
typedef void (*CliCommandExecuteCallback)(PipeSide* pipe, FuriString* args, void* context);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char* name;
|
||||||
|
CliCommandExecuteCallback execute_callback;
|
||||||
|
CliCommandFlag flags;
|
||||||
|
size_t stack_depth;
|
||||||
|
} CliCommandDescriptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration for locating external commands
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
const char* search_directory; //<! The directory to look in
|
||||||
|
const char* fal_prefix; //<! File name prefix that commands should have
|
||||||
|
const char* appid; //<! Expected plugin-reported appid
|
||||||
|
} CliCommandExternalConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Detects if Ctrl+C has been pressed or session has been terminated
|
||||||
|
*
|
||||||
|
* @param [in] side Pointer to pipe side given to the command thread
|
||||||
|
* @warning This function also assumes that the pipe is installed as the
|
||||||
|
* thread's stdio
|
||||||
|
* @warning This function will consume 0 or 1 bytes from the pipe
|
||||||
|
*/
|
||||||
|
bool cli_is_pipe_broken_or_is_etx_next_char(PipeSide* side);
|
||||||
|
|
||||||
|
/** Print unified cmd usage tip
|
||||||
|
*
|
||||||
|
* @param cmd cmd name
|
||||||
|
* @param usage usage tip
|
||||||
|
* @param arg arg passed by user
|
||||||
|
*/
|
||||||
|
void cli_print_usage(const char* cmd, const char* usage, const char* arg);
|
||||||
|
|
||||||
|
#define CLI_COMMAND_INTERFACE(name, execute_callback, flags, stack_depth, app_id) \
|
||||||
|
static const CliCommandDescriptor cli_##name##_desc = { \
|
||||||
|
#name, \
|
||||||
|
&execute_callback, \
|
||||||
|
flags, \
|
||||||
|
stack_depth, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static const FlipperAppPluginDescriptor plugin_descriptor = { \
|
||||||
|
.appid = app_id, \
|
||||||
|
.ep_api_version = CLI_PLUGIN_API_VERSION, \
|
||||||
|
.entry_point = &cli_##name##_desc, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
const FlipperAppPluginDescriptor* cli_##name##_ep(void) { \
|
||||||
|
return &plugin_descriptor; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
178
lib/toolbox/cli/cli_registry.c
Normal file
178
lib/toolbox/cli/cli_registry.c
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#include "cli_registry.h"
|
||||||
|
#include "cli_registry_i.h"
|
||||||
|
#include <toolbox/pipe.h>
|
||||||
|
#include <storage/storage.h>
|
||||||
|
|
||||||
|
#define TAG "cli"
|
||||||
|
|
||||||
|
struct CliRegistry {
|
||||||
|
CliCommandTree_t commands;
|
||||||
|
FuriMutex* mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
CliRegistry* cli_registry_alloc(void) {
|
||||||
|
CliRegistry* registry = malloc(sizeof(CliRegistry));
|
||||||
|
CliCommandTree_init(registry->commands);
|
||||||
|
registry->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_free(CliRegistry* registry) {
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
furi_mutex_free(registry->mutex);
|
||||||
|
CliCommandTree_clear(registry->commands);
|
||||||
|
free(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_add_command(
|
||||||
|
CliRegistry* registry,
|
||||||
|
const char* name,
|
||||||
|
CliCommandFlag flags,
|
||||||
|
CliCommandExecuteCallback callback,
|
||||||
|
void* context) {
|
||||||
|
cli_registry_add_command_ex(
|
||||||
|
registry, name, flags, callback, context, CLI_BUILTIN_COMMAND_STACK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_add_command_ex(
|
||||||
|
CliRegistry* registry,
|
||||||
|
const char* name,
|
||||||
|
CliCommandFlag flags,
|
||||||
|
CliCommandExecuteCallback callback,
|
||||||
|
void* context,
|
||||||
|
size_t stack_size) {
|
||||||
|
furi_check(registry);
|
||||||
|
furi_check(name);
|
||||||
|
furi_check(callback);
|
||||||
|
|
||||||
|
// the shell always attaches the pipe to the stdio, thus both flags can't be used at once
|
||||||
|
if(flags & CliCommandFlagUseShellThread) furi_check(!(flags & CliCommandFlagDontAttachStdio));
|
||||||
|
|
||||||
|
FuriString* name_str;
|
||||||
|
name_str = furi_string_alloc_set(name);
|
||||||
|
// command cannot contain spaces
|
||||||
|
furi_check(furi_string_search_char(name_str, ' ') == FURI_STRING_FAILURE);
|
||||||
|
|
||||||
|
CliRegistryCommand command = {
|
||||||
|
.context = context,
|
||||||
|
.execute_callback = callback,
|
||||||
|
.flags = flags,
|
||||||
|
.stack_depth = stack_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
CliCommandTree_set_at(registry->commands, name_str, command);
|
||||||
|
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||||
|
|
||||||
|
furi_string_free(name_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_delete_command(CliRegistry* registry, const char* name) {
|
||||||
|
furi_check(registry);
|
||||||
|
FuriString* name_str;
|
||||||
|
name_str = furi_string_alloc_set(name);
|
||||||
|
furi_string_trim(name_str);
|
||||||
|
|
||||||
|
size_t name_replace;
|
||||||
|
do {
|
||||||
|
name_replace = furi_string_replace(name_str, " ", "_");
|
||||||
|
} while(name_replace != FURI_STRING_FAILURE);
|
||||||
|
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
CliCommandTree_erase(registry->commands, name_str);
|
||||||
|
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||||
|
|
||||||
|
furi_string_free(name_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cli_registry_get_command(
|
||||||
|
CliRegistry* registry,
|
||||||
|
FuriString* command,
|
||||||
|
CliRegistryCommand* result) {
|
||||||
|
furi_assert(registry);
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
CliRegistryCommand* data = CliCommandTree_get(registry->commands, command);
|
||||||
|
if(data) *result = *data;
|
||||||
|
|
||||||
|
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||||
|
|
||||||
|
return !!data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_remove_external_commands(CliRegistry* registry) {
|
||||||
|
furi_check(registry);
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
|
||||||
|
// FIXME FL-3977: memory leak somewhere within this function
|
||||||
|
|
||||||
|
CliCommandTree_t internal_cmds;
|
||||||
|
CliCommandTree_init(internal_cmds);
|
||||||
|
for
|
||||||
|
M_EACH(item, registry->commands, CliCommandTree_t) {
|
||||||
|
if(!(item->value_ptr->flags & CliCommandFlagExternal))
|
||||||
|
CliCommandTree_set_at(internal_cmds, *item->key_ptr, *item->value_ptr);
|
||||||
|
}
|
||||||
|
CliCommandTree_move(registry->commands, internal_cmds);
|
||||||
|
|
||||||
|
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_reload_external_commands(
|
||||||
|
CliRegistry* registry,
|
||||||
|
const CliCommandExternalConfig* config) {
|
||||||
|
furi_check(registry);
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
FURI_LOG_D(TAG, "Reloading ext commands");
|
||||||
|
|
||||||
|
cli_registry_remove_external_commands(registry);
|
||||||
|
|
||||||
|
// iterate over files in plugin directory
|
||||||
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||||
|
File* plugin_dir = storage_file_alloc(storage);
|
||||||
|
|
||||||
|
if(storage_dir_open(plugin_dir, config->search_directory)) {
|
||||||
|
char plugin_filename[64];
|
||||||
|
FuriString* plugin_name = furi_string_alloc();
|
||||||
|
|
||||||
|
while(storage_dir_read(plugin_dir, NULL, plugin_filename, sizeof(plugin_filename))) {
|
||||||
|
FURI_LOG_T(TAG, "Plugin: %s", plugin_filename);
|
||||||
|
furi_string_set_str(plugin_name, plugin_filename);
|
||||||
|
|
||||||
|
furi_check(furi_string_end_with_str(plugin_name, ".fal"));
|
||||||
|
furi_string_replace_all_str(plugin_name, ".fal", "");
|
||||||
|
furi_check(furi_string_start_with_str(plugin_name, config->fal_prefix));
|
||||||
|
furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), "");
|
||||||
|
|
||||||
|
CliRegistryCommand command = {
|
||||||
|
.context = NULL,
|
||||||
|
.execute_callback = NULL,
|
||||||
|
.flags = CliCommandFlagExternal,
|
||||||
|
};
|
||||||
|
CliCommandTree_set_at(registry->commands, plugin_name, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
furi_string_free(plugin_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_dir_close(plugin_dir);
|
||||||
|
storage_file_free(plugin_dir);
|
||||||
|
furi_record_close(RECORD_STORAGE);
|
||||||
|
|
||||||
|
FURI_LOG_D(TAG, "Done reloading ext commands");
|
||||||
|
furi_check(furi_mutex_release(registry->mutex) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_lock(CliRegistry* registry) {
|
||||||
|
furi_assert(registry);
|
||||||
|
furi_check(furi_mutex_acquire(registry->mutex, FuriWaitForever) == FuriStatusOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_registry_unlock(CliRegistry* registry) {
|
||||||
|
furi_assert(registry);
|
||||||
|
furi_mutex_release(registry->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
CliCommandTree_t* cli_registry_get_commands(CliRegistry* registry) {
|
||||||
|
furi_assert(registry);
|
||||||
|
return ®istry->commands;
|
||||||
|
}
|
||||||
92
lib/toolbox/cli/cli_registry.h
Normal file
92
lib/toolbox/cli/cli_registry.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* @file cli_registry.h
|
||||||
|
* API for registering commands with a CLI shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <m-array.h>
|
||||||
|
#include <toolbox/pipe.h>
|
||||||
|
#include "cli_command.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct CliRegistry CliRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocates a `CliRegistry`.
|
||||||
|
*/
|
||||||
|
CliRegistry* cli_registry_alloc(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees a `CliRegistry`.
|
||||||
|
*/
|
||||||
|
void cli_registry_free(CliRegistry* registry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a command with the registry. Provides less options than the
|
||||||
|
* `_ex` counterpart.
|
||||||
|
*
|
||||||
|
* @param [in] registry Pointer to registry instance
|
||||||
|
* @param [in] name Command name
|
||||||
|
* @param [in] flags see CliCommandFlag
|
||||||
|
* @param [in] callback Callback function
|
||||||
|
* @param [in] context Custom context
|
||||||
|
*/
|
||||||
|
void cli_registry_add_command(
|
||||||
|
CliRegistry* registry,
|
||||||
|
const char* name,
|
||||||
|
CliCommandFlag flags,
|
||||||
|
CliCommandExecuteCallback callback,
|
||||||
|
void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a command with the registry. Provides more options than the
|
||||||
|
* non-`_ex` counterpart.
|
||||||
|
*
|
||||||
|
* @param [in] registry Pointer to registry instance
|
||||||
|
* @param [in] name Command name
|
||||||
|
* @param [in] flags see CliCommandFlag
|
||||||
|
* @param [in] callback Callback function
|
||||||
|
* @param [in] context Custom context
|
||||||
|
* @param [in] stack_size Thread stack size
|
||||||
|
*/
|
||||||
|
void cli_registry_add_command_ex(
|
||||||
|
CliRegistry* registry,
|
||||||
|
const char* name,
|
||||||
|
CliCommandFlag flags,
|
||||||
|
CliCommandExecuteCallback callback,
|
||||||
|
void* context,
|
||||||
|
size_t stack_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deletes a cli command
|
||||||
|
*
|
||||||
|
* @param [in] registry Pointer to registry instance
|
||||||
|
* @param [in] name Command name
|
||||||
|
*/
|
||||||
|
void cli_registry_delete_command(CliRegistry* registry, const char* name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregisters all external commands
|
||||||
|
*
|
||||||
|
* @param [in] registry Pointer to registry instance
|
||||||
|
*/
|
||||||
|
void cli_registry_remove_external_commands(CliRegistry* registry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reloads the list of externally available commands
|
||||||
|
*
|
||||||
|
* @param [in] registry Pointer to registry instance
|
||||||
|
* @param [in] config See `CliCommandExternalConfig`
|
||||||
|
*/
|
||||||
|
void cli_registry_reload_external_commands(
|
||||||
|
CliRegistry* registry,
|
||||||
|
const CliCommandExternalConfig* config);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @file cli_i.h
|
* @file cli_registry_i.h
|
||||||
* Internal API for getting commands registered with the CLI
|
* Internal API for getting commands registered with the CLI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -7,21 +7,20 @@
|
|||||||
|
|
||||||
#include <furi.h>
|
#include <furi.h>
|
||||||
#include <m-bptree.h>
|
#include <m-bptree.h>
|
||||||
#include "cli.h"
|
#include "cli_registry.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CLI_BUILTIN_COMMAND_STACK_SIZE (3 * 1024U)
|
#define CLI_BUILTIN_COMMAND_STACK_SIZE (4 * 1024U)
|
||||||
#define CLI_COMMANDS_PATH "/ext/apps_data/cli/plugins"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* context; //<! Context passed to callbacks
|
void* context; //<! Context passed to callbacks
|
||||||
CliExecuteCallback execute_callback; //<! Callback for command execution
|
CliCommandExecuteCallback execute_callback; //<! Callback for command execution
|
||||||
CliCommandFlag flags;
|
CliCommandFlag flags;
|
||||||
size_t stack_depth;
|
size_t stack_depth;
|
||||||
} CliCommand;
|
} CliRegistryCommand;
|
||||||
|
|
||||||
#define CLI_COMMANDS_TREE_RANK 4
|
#define CLI_COMMANDS_TREE_RANK 4
|
||||||
|
|
||||||
@@ -32,21 +31,24 @@ BPTREE_DEF2(
|
|||||||
CLI_COMMANDS_TREE_RANK,
|
CLI_COMMANDS_TREE_RANK,
|
||||||
FuriString*,
|
FuriString*,
|
||||||
FURI_STRING_OPLIST,
|
FURI_STRING_OPLIST,
|
||||||
CliCommand,
|
CliRegistryCommand,
|
||||||
M_POD_OPLIST);
|
M_POD_OPLIST);
|
||||||
|
|
||||||
#define M_OPL_CliCommandTree_t() BPTREE_OPLIST2(CliCommandTree, FURI_STRING_OPLIST, M_POD_OPLIST)
|
#define M_OPL_CliCommandTree_t() BPTREE_OPLIST2(CliCommandTree, FURI_STRING_OPLIST, M_POD_OPLIST)
|
||||||
|
|
||||||
bool cli_get_command(Cli* cli, FuriString* command, CliCommand* result);
|
bool cli_registry_get_command(
|
||||||
|
CliRegistry* registry,
|
||||||
|
FuriString* command,
|
||||||
|
CliRegistryCommand* result);
|
||||||
|
|
||||||
void cli_lock_commands(Cli* cli);
|
void cli_registry_lock(CliRegistry* registry);
|
||||||
|
|
||||||
void cli_unlock_commands(Cli* cli);
|
void cli_registry_unlock(CliRegistry* registry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @warning Surround calls to this function with `cli_[un]lock_commands`
|
* @warning Surround calls to this function with `cli_registry_[un]lock`
|
||||||
*/
|
*/
|
||||||
CliCommandTree_t* cli_get_commands(Cli* cli);
|
CliCommandTree_t* cli_registry_get_commands(CliRegistry* registry);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "cli_shell.h"
|
#include "cli_shell.h"
|
||||||
#include "cli_shell_i.h"
|
#include "cli_shell_i.h"
|
||||||
#include "../cli_ansi.h"
|
#include "../cli_ansi.h"
|
||||||
#include "../cli_i.h"
|
#include "../cli_registry_i.h"
|
||||||
#include "../cli_commands.h"
|
#include "../cli_command.h"
|
||||||
#include "cli_shell_line.h"
|
#include "cli_shell_line.h"
|
||||||
#include "cli_shell_completions.h"
|
#include "cli_shell_completions.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -36,10 +36,17 @@ typedef enum {
|
|||||||
} CliShellStorageEvent;
|
} CliShellStorageEvent;
|
||||||
|
|
||||||
struct CliShell {
|
struct CliShell {
|
||||||
Cli* cli;
|
// Set and freed by external thread
|
||||||
FuriEventLoop* event_loop;
|
CliShellMotd motd;
|
||||||
|
void* callback_context;
|
||||||
PipeSide* pipe;
|
PipeSide* pipe;
|
||||||
|
CliRegistry* registry;
|
||||||
|
const CliCommandExternalConfig* ext_config;
|
||||||
|
FuriThread* thread;
|
||||||
|
const char* prompt;
|
||||||
|
|
||||||
|
// Set and freed by shell thread
|
||||||
|
FuriEventLoop* event_loop;
|
||||||
CliAnsiParser* ansi_parser;
|
CliAnsiParser* ansi_parser;
|
||||||
FuriEventLoopTimer* ansi_parsing_timer;
|
FuriEventLoopTimer* ansi_parsing_timer;
|
||||||
|
|
||||||
@@ -51,7 +58,7 @@ struct CliShell {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
CliCommand* command;
|
CliRegistryCommand* command;
|
||||||
PipeSide* pipe;
|
PipeSide* pipe;
|
||||||
FuriString* args;
|
FuriString* args;
|
||||||
} CliCommandThreadData;
|
} CliCommandThreadData;
|
||||||
@@ -73,9 +80,62 @@ static void cli_shell_detach_pipe(CliShell* cli_shell) {
|
|||||||
furi_thread_set_stdout_callback(NULL, NULL);
|
furi_thread_set_stdout_callback(NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========
|
// =================
|
||||||
// Execution
|
// Built-in commands
|
||||||
// =========
|
// =================
|
||||||
|
|
||||||
|
void cli_command_reload_external(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
|
UNUSED(pipe);
|
||||||
|
UNUSED(args);
|
||||||
|
CliShell* shell = context;
|
||||||
|
furi_check(shell->ext_config);
|
||||||
|
cli_registry_reload_external_commands(shell->registry, shell->ext_config);
|
||||||
|
printf("OK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_command_help(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
|
UNUSED(pipe);
|
||||||
|
UNUSED(args);
|
||||||
|
CliShell* shell = context;
|
||||||
|
CliRegistry* registry = shell->registry;
|
||||||
|
|
||||||
|
const size_t columns = 3;
|
||||||
|
|
||||||
|
printf("Available commands:\r\n" ANSI_FG_GREEN);
|
||||||
|
cli_registry_lock(registry);
|
||||||
|
CliCommandTree_t* commands = cli_registry_get_commands(registry);
|
||||||
|
size_t commands_count = CliCommandTree_size(*commands);
|
||||||
|
|
||||||
|
CliCommandTree_it_t iterator;
|
||||||
|
CliCommandTree_it(iterator, *commands);
|
||||||
|
for(size_t i = 0; i < commands_count; i++) {
|
||||||
|
const CliCommandTree_itref_t* item = CliCommandTree_cref(iterator);
|
||||||
|
printf("%-30s", furi_string_get_cstr(*item->key_ptr));
|
||||||
|
CliCommandTree_next(iterator);
|
||||||
|
|
||||||
|
if(i % columns == columns - 1) printf("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shell->ext_config)
|
||||||
|
printf(
|
||||||
|
ANSI_RESET
|
||||||
|
"\r\nIf you added a new external command and can't see it above, run `reload_ext_cmds`");
|
||||||
|
printf(ANSI_RESET "\r\nFind out more: https://docs.flipper.net/development/cli");
|
||||||
|
|
||||||
|
cli_registry_unlock(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_command_exit(PipeSide* pipe, FuriString* args, void* context) {
|
||||||
|
UNUSED(pipe);
|
||||||
|
UNUSED(args);
|
||||||
|
CliShell* shell = context;
|
||||||
|
cli_shell_line_set_about_to_exit(shell->components[CliShellComponentLine]);
|
||||||
|
furi_event_loop_stop(shell->event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// Internal functions
|
||||||
|
// ==================
|
||||||
|
|
||||||
static int32_t cli_command_thread(void* context) {
|
static int32_t cli_command_thread(void* context) {
|
||||||
CliCommandThreadData* thread_data = context;
|
CliCommandThreadData* thread_data = context;
|
||||||
@@ -99,12 +159,13 @@ void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) {
|
|||||||
furi_string_right(args, space + 1);
|
furi_string_right(args, space + 1);
|
||||||
|
|
||||||
PluginManager* plugin_manager = NULL;
|
PluginManager* plugin_manager = NULL;
|
||||||
Loader* loader = NULL;
|
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||||
CliCommand command_data;
|
bool loader_locked = false;
|
||||||
|
CliRegistryCommand command_data;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// find handler
|
// find handler
|
||||||
if(!cli_get_command(cli_shell->cli, command_name, &command_data)) {
|
if(!cli_registry_get_command(cli_shell->registry, command_name, &command_data)) {
|
||||||
printf(
|
printf(
|
||||||
ANSI_FG_RED "could not find command `%s`, try `help`" ANSI_RESET,
|
ANSI_FG_RED "could not find command `%s`, try `help`" ANSI_RESET,
|
||||||
furi_string_get_cstr(command_name));
|
furi_string_get_cstr(command_name));
|
||||||
@@ -113,10 +174,14 @@ void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) {
|
|||||||
|
|
||||||
// load external command
|
// load external command
|
||||||
if(command_data.flags & CliCommandFlagExternal) {
|
if(command_data.flags & CliCommandFlagExternal) {
|
||||||
plugin_manager =
|
const CliCommandExternalConfig* ext_config = cli_shell->ext_config;
|
||||||
plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface);
|
plugin_manager = plugin_manager_alloc(
|
||||||
|
ext_config->appid, CLI_PLUGIN_API_VERSION, firmware_api_interface);
|
||||||
FuriString* path = furi_string_alloc_printf(
|
FuriString* path = furi_string_alloc_printf(
|
||||||
"%s/cli_%s.fal", CLI_COMMANDS_PATH, furi_string_get_cstr(command_name));
|
"%s/%s%s.fal",
|
||||||
|
ext_config->search_directory,
|
||||||
|
ext_config->fal_prefix,
|
||||||
|
furi_string_get_cstr(command_name));
|
||||||
uint32_t plugin_cnt_last = plugin_manager_get_count(plugin_manager);
|
uint32_t plugin_cnt_last = plugin_manager_get_count(plugin_manager);
|
||||||
PluginManagerError error =
|
PluginManagerError error =
|
||||||
plugin_manager_load_single(plugin_manager, furi_string_get_cstr(path));
|
plugin_manager_load_single(plugin_manager, furi_string_get_cstr(path));
|
||||||
@@ -141,9 +206,8 @@ void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) {
|
|||||||
|
|
||||||
// lock loader
|
// lock loader
|
||||||
if(!(command_data.flags & CliCommandFlagParallelSafe)) {
|
if(!(command_data.flags & CliCommandFlagParallelSafe)) {
|
||||||
loader = furi_record_open(RECORD_LOADER);
|
loader_locked = loader_lock(loader);
|
||||||
bool success = loader_lock(loader);
|
if(!loader_locked) {
|
||||||
if(!success) {
|
|
||||||
printf(ANSI_FG_RED
|
printf(ANSI_FG_RED
|
||||||
"this command cannot be run while an application is open" ANSI_RESET);
|
"this command cannot be run while an application is open" ANSI_RESET);
|
||||||
break;
|
break;
|
||||||
@@ -177,13 +241,17 @@ void cli_shell_execute_command(CliShell* cli_shell, FuriString* command) {
|
|||||||
furi_string_free(args);
|
furi_string_free(args);
|
||||||
|
|
||||||
// unlock loader
|
// unlock loader
|
||||||
if(loader) loader_unlock(loader);
|
if(loader_locked) loader_unlock(loader);
|
||||||
furi_record_close(RECORD_LOADER);
|
furi_record_close(RECORD_LOADER);
|
||||||
|
|
||||||
// unload external command
|
// unload external command
|
||||||
if(plugin_manager) plugin_manager_free(plugin_manager);
|
if(plugin_manager) plugin_manager_free(plugin_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* cli_shell_get_prompt(CliShell* cli_shell) {
|
||||||
|
return cli_shell->prompt;
|
||||||
|
}
|
||||||
|
|
||||||
// ==============
|
// ==============
|
||||||
// Event handlers
|
// Event handlers
|
||||||
// ==============
|
// ==============
|
||||||
@@ -210,9 +278,9 @@ static void cli_shell_storage_internal_event(FuriEventLoopObject* object, void*
|
|||||||
furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);
|
furi_check(furi_message_queue_get(queue, &event, 0) == FuriStatusOk);
|
||||||
|
|
||||||
if(event == CliShellStorageEventMount) {
|
if(event == CliShellStorageEventMount) {
|
||||||
cli_enumerate_external_commands(cli_shell->cli);
|
cli_registry_reload_external_commands(cli_shell->registry, cli_shell->ext_config);
|
||||||
} else if(event == CliShellStorageEventUnmount) {
|
} else if(event == CliShellStorageEventUnmount) {
|
||||||
cli_remove_external_commands(cli_shell->cli);
|
cli_registry_remove_external_commands(cli_shell->registry);
|
||||||
} else {
|
} else {
|
||||||
furi_crash();
|
furi_crash();
|
||||||
}
|
}
|
||||||
@@ -265,113 +333,97 @@ static void cli_shell_timer_expired(void* context) {
|
|||||||
cli_shell, cli_ansi_parser_feed_timeout(cli_shell->ansi_parser));
|
cli_shell, cli_ansi_parser_feed_timeout(cli_shell->ansi_parser));
|
||||||
}
|
}
|
||||||
|
|
||||||
// =======
|
// ===========
|
||||||
// Helpers
|
// Thread code
|
||||||
// =======
|
// ===========
|
||||||
|
|
||||||
static CliShell* cli_shell_alloc(PipeSide* pipe) {
|
static void cli_shell_init(CliShell* shell) {
|
||||||
CliShell* cli_shell = malloc(sizeof(CliShell));
|
cli_registry_add_command(
|
||||||
|
shell->registry,
|
||||||
|
"help",
|
||||||
|
CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,
|
||||||
|
cli_command_help,
|
||||||
|
shell);
|
||||||
|
cli_registry_add_command(
|
||||||
|
shell->registry,
|
||||||
|
"?",
|
||||||
|
CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,
|
||||||
|
cli_command_help,
|
||||||
|
shell);
|
||||||
|
cli_registry_add_command(
|
||||||
|
shell->registry,
|
||||||
|
"exit",
|
||||||
|
CliCommandFlagUseShellThread | CliCommandFlagParallelSafe,
|
||||||
|
cli_command_exit,
|
||||||
|
shell);
|
||||||
|
|
||||||
cli_shell->cli = furi_record_open(RECORD_CLI);
|
if(shell->ext_config) {
|
||||||
cli_shell->ansi_parser = cli_ansi_parser_alloc();
|
cli_registry_add_command(
|
||||||
cli_shell->pipe = pipe;
|
shell->registry,
|
||||||
|
"reload_ext_cmds",
|
||||||
|
CliCommandFlagUseShellThread,
|
||||||
|
cli_command_reload_external,
|
||||||
|
shell);
|
||||||
|
cli_registry_reload_external_commands(shell->registry, shell->ext_config);
|
||||||
|
}
|
||||||
|
|
||||||
cli_shell->components[CliShellComponentLine] = cli_shell_line_alloc(cli_shell);
|
shell->components[CliShellComponentLine] = cli_shell_line_alloc(shell);
|
||||||
cli_shell->components[CliShellComponentCompletions] = cli_shell_completions_alloc(
|
shell->components[CliShellComponentCompletions] = cli_shell_completions_alloc(
|
||||||
cli_shell->cli, cli_shell, cli_shell->components[CliShellComponentLine]);
|
shell->registry, shell, shell->components[CliShellComponentLine]);
|
||||||
|
|
||||||
cli_shell->event_loop = furi_event_loop_alloc();
|
shell->ansi_parser = cli_ansi_parser_alloc();
|
||||||
cli_shell->ansi_parsing_timer = furi_event_loop_timer_alloc(
|
|
||||||
cli_shell->event_loop, cli_shell_timer_expired, FuriEventLoopTimerTypeOnce, cli_shell);
|
|
||||||
|
|
||||||
cli_shell_install_pipe(cli_shell);
|
shell->event_loop = furi_event_loop_alloc();
|
||||||
|
shell->ansi_parsing_timer = furi_event_loop_timer_alloc(
|
||||||
|
shell->event_loop, cli_shell_timer_expired, FuriEventLoopTimerTypeOnce, shell);
|
||||||
|
|
||||||
cli_shell->storage_event_queue = furi_message_queue_alloc(1, sizeof(CliShellStorageEvent));
|
shell->storage_event_queue = furi_message_queue_alloc(1, sizeof(CliShellStorageEvent));
|
||||||
furi_event_loop_subscribe_message_queue(
|
furi_event_loop_subscribe_message_queue(
|
||||||
cli_shell->event_loop,
|
shell->event_loop,
|
||||||
cli_shell->storage_event_queue,
|
shell->storage_event_queue,
|
||||||
FuriEventLoopEventIn,
|
FuriEventLoopEventIn,
|
||||||
cli_shell_storage_internal_event,
|
cli_shell_storage_internal_event,
|
||||||
cli_shell);
|
shell);
|
||||||
cli_shell->storage = furi_record_open(RECORD_STORAGE);
|
shell->storage = furi_record_open(RECORD_STORAGE);
|
||||||
cli_shell->storage_subscription = furi_pubsub_subscribe(
|
shell->storage_subscription =
|
||||||
storage_get_pubsub(cli_shell->storage), cli_shell_storage_event, cli_shell);
|
furi_pubsub_subscribe(storage_get_pubsub(shell->storage), cli_shell_storage_event, shell);
|
||||||
|
|
||||||
return cli_shell;
|
cli_shell_install_pipe(shell);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cli_shell_free(CliShell* cli_shell) {
|
static void cli_shell_deinit(CliShell* shell) {
|
||||||
furi_pubsub_unsubscribe(
|
furi_pubsub_unsubscribe(storage_get_pubsub(shell->storage), shell->storage_subscription);
|
||||||
storage_get_pubsub(cli_shell->storage), cli_shell->storage_subscription);
|
|
||||||
furi_record_close(RECORD_STORAGE);
|
furi_record_close(RECORD_STORAGE);
|
||||||
furi_event_loop_unsubscribe(cli_shell->event_loop, cli_shell->storage_event_queue);
|
furi_event_loop_unsubscribe(shell->event_loop, shell->storage_event_queue);
|
||||||
furi_message_queue_free(cli_shell->storage_event_queue);
|
furi_message_queue_free(shell->storage_event_queue);
|
||||||
|
|
||||||
cli_shell_completions_free(cli_shell->components[CliShellComponentCompletions]);
|
cli_shell_completions_free(shell->components[CliShellComponentCompletions]);
|
||||||
cli_shell_line_free(cli_shell->components[CliShellComponentLine]);
|
cli_shell_line_free(shell->components[CliShellComponentLine]);
|
||||||
|
|
||||||
cli_shell_detach_pipe(cli_shell);
|
cli_shell_detach_pipe(shell);
|
||||||
furi_event_loop_timer_free(cli_shell->ansi_parsing_timer);
|
furi_event_loop_timer_free(shell->ansi_parsing_timer);
|
||||||
furi_event_loop_free(cli_shell->event_loop);
|
furi_event_loop_free(shell->event_loop);
|
||||||
pipe_free(cli_shell->pipe);
|
cli_ansi_parser_free(shell->ansi_parser);
|
||||||
cli_ansi_parser_free(cli_shell->ansi_parser);
|
|
||||||
furi_record_close(RECORD_CLI);
|
|
||||||
free(cli_shell);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cli_shell_motd(void) {
|
|
||||||
printf(ANSI_FLIPPER_BRAND_ORANGE
|
|
||||||
"\r\n"
|
|
||||||
" _.-------.._ -,\r\n"
|
|
||||||
" .-\"```\"--..,,_/ /`-, -, \\ \r\n"
|
|
||||||
" .:\" /:/ /'\\ \\ ,_..., `. | |\r\n"
|
|
||||||
" / ,----/:/ /`\\ _\\~`_-\"` _;\r\n"
|
|
||||||
" ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n"
|
|
||||||
" | | | 0 | | .-' ,/` /\r\n"
|
|
||||||
" | ,..\\ \\ ,.-\"` ,/` /\r\n"
|
|
||||||
" ; : `/`\"\"\\` ,/--==,/-----,\r\n"
|
|
||||||
" | `-...| -.___-Z:_______J...---;\r\n"
|
|
||||||
" : ` _-'\r\n"
|
|
||||||
" _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n"
|
|
||||||
"| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n"
|
|
||||||
"| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n"
|
|
||||||
"|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n"
|
|
||||||
"\r\n" ANSI_FG_BR_WHITE "Welcome to Flipper Zero Command Line Interface!\r\n"
|
|
||||||
"Read the manual: https://docs.flipper.net/development/cli\r\n"
|
|
||||||
"Run `help` or `?` to list available commands\r\n"
|
|
||||||
"\r\n" ANSI_RESET);
|
|
||||||
|
|
||||||
const Version* firmware_version = furi_hal_version_get_firmware_version();
|
|
||||||
if(firmware_version) {
|
|
||||||
printf(
|
|
||||||
"Firmware version: %s %s (%s%s built on %s)\r\n",
|
|
||||||
version_get_gitbranch(firmware_version),
|
|
||||||
version_get_version(firmware_version),
|
|
||||||
version_get_githash(firmware_version),
|
|
||||||
version_get_dirty_flag(firmware_version) ? "-dirty" : "",
|
|
||||||
version_get_builddate(firmware_version));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t cli_shell_thread(void* context) {
|
static int32_t cli_shell_thread(void* context) {
|
||||||
PipeSide* pipe = context;
|
CliShell* shell = context;
|
||||||
|
|
||||||
// Sometimes, the other side closes the pipe even before our thread is started. Although the
|
// Sometimes, the other side closes the pipe even before our thread is started. Although the
|
||||||
// rest of the code will eventually find this out if this check is removed, there's no point in
|
// rest of the code will eventually find this out if this check is removed, there's no point in
|
||||||
// wasting time.
|
// wasting time.
|
||||||
if(pipe_state(pipe) == PipeStateBroken) return 0;
|
if(pipe_state(shell->pipe) == PipeStateBroken) return 0;
|
||||||
|
|
||||||
CliShell* cli_shell = cli_shell_alloc(pipe);
|
|
||||||
|
|
||||||
|
cli_shell_init(shell);
|
||||||
FURI_LOG_D(TAG, "Started");
|
FURI_LOG_D(TAG, "Started");
|
||||||
cli_shell_motd();
|
|
||||||
cli_shell_line_prompt(cli_shell->components[CliShellComponentLine]);
|
|
||||||
|
|
||||||
furi_event_loop_run(cli_shell->event_loop);
|
shell->motd(shell->callback_context);
|
||||||
|
cli_shell_line_prompt(shell->components[CliShellComponentLine]);
|
||||||
|
|
||||||
|
furi_event_loop_run(shell->event_loop);
|
||||||
|
|
||||||
FURI_LOG_D(TAG, "Stopped");
|
FURI_LOG_D(TAG, "Stopped");
|
||||||
|
cli_shell_deinit(shell);
|
||||||
cli_shell_free(cli_shell);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,9 +431,48 @@ static int32_t cli_shell_thread(void* context) {
|
|||||||
// Public API
|
// Public API
|
||||||
// ==========
|
// ==========
|
||||||
|
|
||||||
FuriThread* cli_shell_start(PipeSide* pipe) {
|
CliShell* cli_shell_alloc(
|
||||||
FuriThread* thread =
|
CliShellMotd motd,
|
||||||
furi_thread_alloc_ex("CliShell", CLI_SHELL_STACK_SIZE, cli_shell_thread, pipe);
|
void* context,
|
||||||
furi_thread_start(thread);
|
PipeSide* pipe,
|
||||||
return thread;
|
CliRegistry* registry,
|
||||||
|
const CliCommandExternalConfig* ext_config) {
|
||||||
|
furi_check(motd);
|
||||||
|
furi_check(pipe);
|
||||||
|
furi_check(registry);
|
||||||
|
|
||||||
|
CliShell* shell = malloc(sizeof(CliShell));
|
||||||
|
*shell = (CliShell){
|
||||||
|
.motd = motd,
|
||||||
|
.callback_context = context,
|
||||||
|
.pipe = pipe,
|
||||||
|
.registry = registry,
|
||||||
|
.ext_config = ext_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
shell->thread =
|
||||||
|
furi_thread_alloc_ex("CliShell", CLI_SHELL_STACK_SIZE, cli_shell_thread, shell);
|
||||||
|
|
||||||
|
return shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_shell_free(CliShell* shell) {
|
||||||
|
furi_check(shell);
|
||||||
|
furi_thread_free(shell->thread);
|
||||||
|
free(shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_shell_start(CliShell* shell) {
|
||||||
|
furi_check(shell);
|
||||||
|
furi_thread_start(shell->thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_shell_join(CliShell* shell) {
|
||||||
|
furi_check(shell);
|
||||||
|
furi_thread_join(shell->thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cli_shell_set_prompt(CliShell* shell, const char* prompt) {
|
||||||
|
furi_check(shell);
|
||||||
|
shell->prompt = prompt;
|
||||||
}
|
}
|
||||||
75
lib/toolbox/cli/shell/cli_shell.h
Normal file
75
lib/toolbox/cli/shell/cli_shell.h
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <furi.h>
|
||||||
|
#include <toolbox/pipe.h>
|
||||||
|
#include "../cli_registry.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CLI_SHELL_STACK_SIZE (4 * 1024U)
|
||||||
|
|
||||||
|
typedef struct CliShell CliShell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from the shell thread to print the Message of the Day when the shell
|
||||||
|
* is started.
|
||||||
|
*/
|
||||||
|
typedef void (*CliShellMotd)(void* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocates a shell
|
||||||
|
*
|
||||||
|
* @param [in] motd Message of the Day callback
|
||||||
|
* @param [in] context Callback context
|
||||||
|
* @param [in] pipe Pipe side to be used by the shell
|
||||||
|
* @param [in] registry Command registry
|
||||||
|
* @param [in] ext_config External command configuration. See
|
||||||
|
* `CliCommandExternalConfig`. May be NULL if support for
|
||||||
|
* external commands is not required.
|
||||||
|
*
|
||||||
|
* @return Shell instance
|
||||||
|
*/
|
||||||
|
CliShell* cli_shell_alloc(
|
||||||
|
CliShellMotd motd,
|
||||||
|
void* context,
|
||||||
|
PipeSide* pipe,
|
||||||
|
CliRegistry* registry,
|
||||||
|
const CliCommandExternalConfig* ext_config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Frees a shell
|
||||||
|
*
|
||||||
|
* @param [in] shell Shell instance
|
||||||
|
*/
|
||||||
|
void cli_shell_free(CliShell* shell);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Starts a shell
|
||||||
|
*
|
||||||
|
* The shell runs in a separate thread. This call is non-blocking.
|
||||||
|
*
|
||||||
|
* @param [in] shell Shell instance
|
||||||
|
*/
|
||||||
|
void cli_shell_start(CliShell* shell);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Joins the shell thread
|
||||||
|
*
|
||||||
|
* @warning This call is blocking.
|
||||||
|
*
|
||||||
|
* @param [in] shell Shell instance
|
||||||
|
*/
|
||||||
|
void cli_shell_join(CliShell* shell);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets optional text before prompt (`>:`)
|
||||||
|
*
|
||||||
|
* @param [in] shell Shell instance
|
||||||
|
*/
|
||||||
|
void cli_shell_set_prompt(CliShell* shell, const char* prompt);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -4,7 +4,7 @@ ARRAY_DEF(CommandCompletions, FuriString*, FURI_STRING_OPLIST); // -V524
|
|||||||
#define M_OPL_CommandCompletions_t() ARRAY_OPLIST(CommandCompletions)
|
#define M_OPL_CommandCompletions_t() ARRAY_OPLIST(CommandCompletions)
|
||||||
|
|
||||||
struct CliShellCompletions {
|
struct CliShellCompletions {
|
||||||
Cli* cli;
|
CliRegistry* registry;
|
||||||
CliShell* shell;
|
CliShell* shell;
|
||||||
CliShellLine* line;
|
CliShellLine* line;
|
||||||
CommandCompletions_t variants;
|
CommandCompletions_t variants;
|
||||||
@@ -45,10 +45,11 @@ typedef struct {
|
|||||||
// Public API
|
// Public API
|
||||||
// ==========
|
// ==========
|
||||||
|
|
||||||
CliShellCompletions* cli_shell_completions_alloc(Cli* cli, CliShell* shell, CliShellLine* line) {
|
CliShellCompletions*
|
||||||
|
cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line) {
|
||||||
CliShellCompletions* completions = malloc(sizeof(CliShellCompletions));
|
CliShellCompletions* completions = malloc(sizeof(CliShellCompletions));
|
||||||
|
|
||||||
completions->cli = cli;
|
completions->registry = registry;
|
||||||
completions->shell = shell;
|
completions->shell = shell;
|
||||||
completions->line = line;
|
completions->line = line;
|
||||||
CommandCompletions_init(completions->variants);
|
CommandCompletions_init(completions->variants);
|
||||||
@@ -108,8 +109,9 @@ void cli_shell_completions_fill_variants(CliShellCompletions* completions) {
|
|||||||
furi_string_left(input, segment.length);
|
furi_string_left(input, segment.length);
|
||||||
|
|
||||||
if(segment.type == CliShellCompletionSegmentTypeCommand) {
|
if(segment.type == CliShellCompletionSegmentTypeCommand) {
|
||||||
cli_lock_commands(completions->cli);
|
CliRegistry* registry = completions->registry;
|
||||||
CliCommandTree_t* commands = cli_get_commands(completions->cli);
|
cli_registry_lock(registry);
|
||||||
|
CliCommandTree_t* commands = cli_registry_get_commands(registry);
|
||||||
for
|
for
|
||||||
M_EACH(registered_command, *commands, CliCommandTree_t) {
|
M_EACH(registered_command, *commands, CliCommandTree_t) {
|
||||||
FuriString* command_name = *registered_command->key_ptr;
|
FuriString* command_name = *registered_command->key_ptr;
|
||||||
@@ -117,7 +119,7 @@ void cli_shell_completions_fill_variants(CliShellCompletions* completions) {
|
|||||||
CommandCompletions_push_back(completions->variants, command_name);
|
CommandCompletions_push_back(completions->variants, command_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cli_unlock_commands(completions->cli);
|
cli_registry_unlock(registry);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// support removed, might reimplement in the future
|
// support removed, might reimplement in the future
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
#include <m-array.h>
|
#include <m-array.h>
|
||||||
#include "cli_shell_i.h"
|
#include "cli_shell_i.h"
|
||||||
#include "cli_shell_line.h"
|
#include "cli_shell_line.h"
|
||||||
#include "../cli.h"
|
#include "../cli_registry.h"
|
||||||
#include "../cli_i.h"
|
#include "../cli_registry_i.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -13,7 +13,8 @@ extern "C" {
|
|||||||
|
|
||||||
typedef struct CliShellCompletions CliShellCompletions;
|
typedef struct CliShellCompletions CliShellCompletions;
|
||||||
|
|
||||||
CliShellCompletions* cli_shell_completions_alloc(Cli* cli, CliShell* shell, CliShellLine* line);
|
CliShellCompletions*
|
||||||
|
cli_shell_completions_alloc(CliRegistry* registry, CliShell* shell, CliShellLine* line);
|
||||||
|
|
||||||
void cli_shell_completions_free(CliShellCompletions* completions);
|
void cli_shell_completions_free(CliShellCompletions* completions);
|
||||||
|
|
||||||
@@ -27,6 +27,8 @@ typedef struct {
|
|||||||
|
|
||||||
void cli_shell_execute_command(CliShell* cli_shell, FuriString* command);
|
void cli_shell_execute_command(CliShell* cli_shell, FuriString* command);
|
||||||
|
|
||||||
|
const char* cli_shell_get_prompt(CliShell* cli_shell);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -8,6 +8,7 @@ struct CliShellLine {
|
|||||||
FuriString* history[HISTORY_DEPTH];
|
FuriString* history[HISTORY_DEPTH];
|
||||||
size_t history_entries;
|
size_t history_entries;
|
||||||
CliShell* shell;
|
CliShell* shell;
|
||||||
|
bool about_to_exit;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ==========
|
// ==========
|
||||||
@@ -39,14 +40,16 @@ FuriString* cli_shell_line_get_editing(CliShellLine* line) {
|
|||||||
return line->history[0];
|
return line->history[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t cli_shell_line_prompt_length(CliShellLine* line) {
|
|
||||||
UNUSED(line);
|
|
||||||
return strlen(">: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
void cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length) {
|
void cli_shell_line_format_prompt(CliShellLine* line, char* buf, size_t length) {
|
||||||
UNUSED(line);
|
UNUSED(line);
|
||||||
snprintf(buf, length - 1, ">: ");
|
const char* prompt = cli_shell_get_prompt(line->shell);
|
||||||
|
snprintf(buf, length - 1, "%s>: ", prompt ? prompt : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cli_shell_line_prompt_length(CliShellLine* line) {
|
||||||
|
char buffer[128];
|
||||||
|
cli_shell_line_format_prompt(line, buffer, sizeof(buffer));
|
||||||
|
return strlen(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_shell_line_prompt(CliShellLine* line) {
|
void cli_shell_line_prompt(CliShellLine* line) {
|
||||||
@@ -65,6 +68,10 @@ void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cli_shell_line_set_about_to_exit(CliShellLine* line) {
|
||||||
|
line->about_to_exit = true;
|
||||||
|
}
|
||||||
|
|
||||||
size_t cli_shell_line_get_line_position(CliShellLine* line) {
|
size_t cli_shell_line_get_line_position(CliShellLine* line) {
|
||||||
return line->line_position;
|
return line->line_position;
|
||||||
}
|
}
|
||||||
@@ -188,8 +195,7 @@ static bool cli_shell_line_input_cr(CliKeyCombo combo, void* context) {
|
|||||||
printf("\r\n");
|
printf("\r\n");
|
||||||
cli_shell_execute_command(line->shell, command_copy);
|
cli_shell_execute_command(line->shell, command_copy);
|
||||||
furi_string_free(command_copy);
|
furi_string_free(command_copy);
|
||||||
|
if(!line->about_to_exit) cli_shell_line_prompt(line);
|
||||||
cli_shell_line_prompt(line);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,10 +208,13 @@ static bool cli_shell_line_input_up_down(CliKeyCombo combo, void* context) {
|
|||||||
|
|
||||||
// print prompt with selected command
|
// print prompt with selected command
|
||||||
if(new_pos != line->history_position) {
|
if(new_pos != line->history_position) {
|
||||||
|
char prompt[64];
|
||||||
|
cli_shell_line_format_prompt(line, prompt, sizeof(prompt));
|
||||||
line->history_position = new_pos;
|
line->history_position = new_pos;
|
||||||
FuriString* command = cli_shell_line_get_selected(line);
|
FuriString* command = cli_shell_line_get_selected(line);
|
||||||
printf(
|
printf(
|
||||||
ANSI_CURSOR_HOR_POS("1") ">: %s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),
|
ANSI_CURSOR_HOR_POS("1") "%s%s" ANSI_ERASE_LINE(ANSI_ERASE_FROM_CURSOR_TO_END),
|
||||||
|
prompt,
|
||||||
furi_string_get_cstr(command));
|
furi_string_get_cstr(command));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
line->line_position = furi_string_size(command);
|
line->line_position = furi_string_size(command);
|
||||||
@@ -33,6 +33,8 @@ void cli_shell_line_set_line_position(CliShellLine* line, size_t position);
|
|||||||
*/
|
*/
|
||||||
void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line);
|
void cli_shell_line_ensure_not_overwriting_history(CliShellLine* line);
|
||||||
|
|
||||||
|
void cli_shell_line_set_about_to_exit(CliShellLine* line);
|
||||||
|
|
||||||
extern CliShellKeyComboSet cli_shell_line_key_combo_set;
|
extern CliShellKeyComboSet cli_shell_line_key_combo_set;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,84.1,,
|
Version,+,85.0,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
|
||||||
Header,+,applications/services/cli/cli_ansi.h,,
|
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
Header,+,applications/services/dialogs/dialogs.h,,
|
Header,+,applications/services/dialogs/dialogs.h,,
|
||||||
Header,+,applications/services/dolphin/dolphin.h,,
|
Header,+,applications/services/dolphin/dolphin.h,,
|
||||||
@@ -153,6 +151,10 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,,
|
|||||||
Header,+,lib/toolbox/api_lock.h,,
|
Header,+,lib/toolbox/api_lock.h,,
|
||||||
Header,+,lib/toolbox/args.h,,
|
Header,+,lib/toolbox/args.h,,
|
||||||
Header,+,lib/toolbox/bit_buffer.h,,
|
Header,+,lib/toolbox/bit_buffer.h,,
|
||||||
|
Header,+,lib/toolbox/cli/cli_ansi.h,,
|
||||||
|
Header,+,lib/toolbox/cli/cli_command.h,,
|
||||||
|
Header,+,lib/toolbox/cli/cli_registry.h,,
|
||||||
|
Header,+,lib/toolbox/cli/shell/cli_shell.h,,
|
||||||
Header,+,lib/toolbox/compress.h,,
|
Header,+,lib/toolbox/compress.h,,
|
||||||
Header,+,lib/toolbox/crc32_calc.h,,
|
Header,+,lib/toolbox/crc32_calc.h,,
|
||||||
Header,+,lib/toolbox/dir_walk.h,,
|
Header,+,lib/toolbox/dir_walk.h,,
|
||||||
@@ -779,17 +781,24 @@ Function,-,ceill,long double,long double
|
|||||||
Function,-,cfree,void,void*
|
Function,-,cfree,void,void*
|
||||||
Function,-,clearerr,void,FILE*
|
Function,-,clearerr,void,FILE*
|
||||||
Function,-,clearerr_unlocked,void,FILE*
|
Function,-,clearerr_unlocked,void,FILE*
|
||||||
Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliExecuteCallback, void*"
|
|
||||||
Function,+,cli_add_command_ex,void,"Cli*, const char*, CliCommandFlag, CliExecuteCallback, void*, size_t"
|
|
||||||
Function,+,cli_ansi_parser_alloc,CliAnsiParser*,
|
Function,+,cli_ansi_parser_alloc,CliAnsiParser*,
|
||||||
Function,+,cli_ansi_parser_feed,CliAnsiParserResult,"CliAnsiParser*, char"
|
Function,+,cli_ansi_parser_feed,CliAnsiParserResult,"CliAnsiParser*, char"
|
||||||
Function,+,cli_ansi_parser_feed_timeout,CliAnsiParserResult,CliAnsiParser*
|
Function,+,cli_ansi_parser_feed_timeout,CliAnsiParserResult,CliAnsiParser*
|
||||||
Function,+,cli_ansi_parser_free,void,CliAnsiParser*
|
Function,+,cli_ansi_parser_free,void,CliAnsiParser*
|
||||||
Function,+,cli_delete_command,void,"Cli*, const char*"
|
|
||||||
Function,+,cli_enumerate_external_commands,void,Cli*
|
|
||||||
Function,+,cli_is_pipe_broken_or_is_etx_next_char,_Bool,PipeSide*
|
Function,+,cli_is_pipe_broken_or_is_etx_next_char,_Bool,PipeSide*
|
||||||
Function,+,cli_print_usage,void,"const char*, const char*, const char*"
|
Function,+,cli_print_usage,void,"const char*, const char*, const char*"
|
||||||
Function,+,cli_remove_external_commands,void,Cli*
|
Function,+,cli_registry_add_command,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*"
|
||||||
|
Function,+,cli_registry_add_command_ex,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*, size_t"
|
||||||
|
Function,+,cli_registry_alloc,CliRegistry*,
|
||||||
|
Function,+,cli_registry_delete_command,void,"CliRegistry*, const char*"
|
||||||
|
Function,+,cli_registry_free,void,CliRegistry*
|
||||||
|
Function,+,cli_registry_reload_external_commands,void,"CliRegistry*, const CliCommandExternalConfig*"
|
||||||
|
Function,+,cli_registry_remove_external_commands,void,CliRegistry*
|
||||||
|
Function,+,cli_shell_alloc,CliShell*,"CliShellMotd, void*, PipeSide*, CliRegistry*, const CliCommandExternalConfig*"
|
||||||
|
Function,+,cli_shell_free,void,CliShell*
|
||||||
|
Function,+,cli_shell_join,void,CliShell*
|
||||||
|
Function,+,cli_shell_set_prompt,void,"CliShell*, const char*"
|
||||||
|
Function,+,cli_shell_start,void,CliShell*
|
||||||
Function,+,cli_vcp_disable,void,CliVcp*
|
Function,+,cli_vcp_disable,void,CliVcp*
|
||||||
Function,+,cli_vcp_enable,void,CliVcp*
|
Function,+,cli_vcp_enable,void,CliVcp*
|
||||||
Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*"
|
Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*"
|
||||||
|
|||||||
|
@@ -1,10 +1,8 @@
|
|||||||
entry,status,name,type,params
|
entry,status,name,type,params
|
||||||
Version,+,84.1,,
|
Version,+,85.0,,
|
||||||
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||||
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
|
||||||
Header,+,applications/services/cli/cli.h,,
|
|
||||||
Header,+,applications/services/cli/cli_ansi.h,,
|
|
||||||
Header,+,applications/services/cli/cli_vcp.h,,
|
Header,+,applications/services/cli/cli_vcp.h,,
|
||||||
Header,+,applications/services/dialogs/dialogs.h,,
|
Header,+,applications/services/dialogs/dialogs.h,,
|
||||||
Header,+,applications/services/dolphin/dolphin.h,,
|
Header,+,applications/services/dolphin/dolphin.h,,
|
||||||
@@ -225,6 +223,10 @@ Header,+,lib/subghz/transmitter.h,,
|
|||||||
Header,+,lib/toolbox/api_lock.h,,
|
Header,+,lib/toolbox/api_lock.h,,
|
||||||
Header,+,lib/toolbox/args.h,,
|
Header,+,lib/toolbox/args.h,,
|
||||||
Header,+,lib/toolbox/bit_buffer.h,,
|
Header,+,lib/toolbox/bit_buffer.h,,
|
||||||
|
Header,+,lib/toolbox/cli/cli_ansi.h,,
|
||||||
|
Header,+,lib/toolbox/cli/cli_command.h,,
|
||||||
|
Header,+,lib/toolbox/cli/cli_registry.h,,
|
||||||
|
Header,+,lib/toolbox/cli/shell/cli_shell.h,,
|
||||||
Header,+,lib/toolbox/compress.h,,
|
Header,+,lib/toolbox/compress.h,,
|
||||||
Header,+,lib/toolbox/crc32_calc.h,,
|
Header,+,lib/toolbox/crc32_calc.h,,
|
||||||
Header,+,lib/toolbox/dir_walk.h,,
|
Header,+,lib/toolbox/dir_walk.h,,
|
||||||
@@ -856,17 +858,24 @@ Function,-,ceill,long double,long double
|
|||||||
Function,-,cfree,void,void*
|
Function,-,cfree,void,void*
|
||||||
Function,-,clearerr,void,FILE*
|
Function,-,clearerr,void,FILE*
|
||||||
Function,-,clearerr_unlocked,void,FILE*
|
Function,-,clearerr_unlocked,void,FILE*
|
||||||
Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliExecuteCallback, void*"
|
|
||||||
Function,+,cli_add_command_ex,void,"Cli*, const char*, CliCommandFlag, CliExecuteCallback, void*, size_t"
|
|
||||||
Function,+,cli_ansi_parser_alloc,CliAnsiParser*,
|
Function,+,cli_ansi_parser_alloc,CliAnsiParser*,
|
||||||
Function,+,cli_ansi_parser_feed,CliAnsiParserResult,"CliAnsiParser*, char"
|
Function,+,cli_ansi_parser_feed,CliAnsiParserResult,"CliAnsiParser*, char"
|
||||||
Function,+,cli_ansi_parser_feed_timeout,CliAnsiParserResult,CliAnsiParser*
|
Function,+,cli_ansi_parser_feed_timeout,CliAnsiParserResult,CliAnsiParser*
|
||||||
Function,+,cli_ansi_parser_free,void,CliAnsiParser*
|
Function,+,cli_ansi_parser_free,void,CliAnsiParser*
|
||||||
Function,+,cli_delete_command,void,"Cli*, const char*"
|
|
||||||
Function,+,cli_enumerate_external_commands,void,Cli*
|
|
||||||
Function,+,cli_is_pipe_broken_or_is_etx_next_char,_Bool,PipeSide*
|
Function,+,cli_is_pipe_broken_or_is_etx_next_char,_Bool,PipeSide*
|
||||||
Function,+,cli_print_usage,void,"const char*, const char*, const char*"
|
Function,+,cli_print_usage,void,"const char*, const char*, const char*"
|
||||||
Function,+,cli_remove_external_commands,void,Cli*
|
Function,+,cli_registry_add_command,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*"
|
||||||
|
Function,+,cli_registry_add_command_ex,void,"CliRegistry*, const char*, CliCommandFlag, CliCommandExecuteCallback, void*, size_t"
|
||||||
|
Function,+,cli_registry_alloc,CliRegistry*,
|
||||||
|
Function,+,cli_registry_delete_command,void,"CliRegistry*, const char*"
|
||||||
|
Function,+,cli_registry_free,void,CliRegistry*
|
||||||
|
Function,+,cli_registry_reload_external_commands,void,"CliRegistry*, const CliCommandExternalConfig*"
|
||||||
|
Function,+,cli_registry_remove_external_commands,void,CliRegistry*
|
||||||
|
Function,+,cli_shell_alloc,CliShell*,"CliShellMotd, void*, PipeSide*, CliRegistry*, const CliCommandExternalConfig*"
|
||||||
|
Function,+,cli_shell_free,void,CliShell*
|
||||||
|
Function,+,cli_shell_join,void,CliShell*
|
||||||
|
Function,+,cli_shell_set_prompt,void,"CliShell*, const char*"
|
||||||
|
Function,+,cli_shell_start,void,CliShell*
|
||||||
Function,+,cli_vcp_disable,void,CliVcp*
|
Function,+,cli_vcp_disable,void,CliVcp*
|
||||||
Function,+,cli_vcp_enable,void,CliVcp*
|
Function,+,cli_vcp_enable,void,CliVcp*
|
||||||
Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*"
|
Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user