Files
Momentum-Firmware/lib/toolbox/cli/cli_registry.c

177 lines
5.7 KiB
C

#include "cli_registry.h"
#include "cli_registry_i.h"
#include <toolbox/pipe.h>
#include <storage/storage.h>
#define TAG "CliRegistry"
struct CliRegistry {
CliCommandDict_t commands;
FuriMutex* mutex;
};
CliRegistry* cli_registry_alloc(void) {
CliRegistry* registry = malloc(sizeof(CliRegistry));
CliCommandDict_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);
CliCommandDict_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);
CliCommandDict_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);
CliCommandDict_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 = CliCommandDict_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);
CliCommandDict_t internal_cmds;
CliCommandDict_init(internal_cmds);
for
M_EACH(item, registry->commands, CliCommandDict_t) {
if(!(item->value.flags & CliCommandFlagExternal))
CliCommandDict_set_at(internal_cmds, item->key, item->value);
}
CliCommandDict_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);
if(!furi_string_end_with_str(plugin_name, ".fal")) continue;
furi_string_replace_all_str(plugin_name, ".fal", "");
if(!furi_string_start_with_str(plugin_name, config->fal_prefix)) continue;
furi_string_replace_at(plugin_name, 0, strlen(config->fal_prefix), "");
CliRegistryCommand command = {
.context = NULL,
.execute_callback = NULL,
.flags = CliCommandFlagExternal,
};
CliCommandDict_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);
}
CliCommandDict_t* cli_registry_get_commands(CliRegistry* registry) {
furi_assert(registry);
return &registry->commands;
}