206 lines
5.7 KiB
C
206 lines
5.7 KiB
C
#include "command.h"
|
||
#include "utils.h"
|
||
#include "esp_log.h"
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
|
||
static const char *TAG = "COMMAND";
|
||
|
||
static const command_t *registry[MAX_COMMANDS];
|
||
static size_t registry_count = 0;
|
||
|
||
/* Max longueur lue/copied par arg (sécurité si non \0) */
|
||
#ifndef COMMAND_MAX_ARG_LEN
|
||
#define COMMAND_MAX_ARG_LEN 128
|
||
#endif
|
||
|
||
/* Max args temporaires qu’on accepte ici (doit couvrir tes commandes) */
|
||
#ifndef COMMAND_MAX_ARGS
|
||
#define COMMAND_MAX_ARGS 16
|
||
#endif
|
||
|
||
/* =========================================================
|
||
* Register command
|
||
* ========================================================= */
|
||
void command_register(const command_t *cmd)
|
||
{
|
||
if (!cmd || !cmd->name || !cmd->handler) {
|
||
ESP_LOGE(TAG, "Invalid command registration");
|
||
return;
|
||
}
|
||
|
||
if (registry_count >= MAX_COMMANDS) {
|
||
ESP_LOGE(TAG, "Command registry full");
|
||
return;
|
||
}
|
||
|
||
registry[registry_count++] = cmd;
|
||
#ifdef CONFIG_ESPILON_LOG_CMD_REG_VERBOSE
|
||
ESPILON_LOGI_PURPLE(TAG, "Registered command: %s", cmd->name);
|
||
#endif
|
||
}
|
||
|
||
/* =========================================================
|
||
* Summary
|
||
* ========================================================= */
|
||
void command_log_registry_summary(void)
|
||
{
|
||
if (registry_count == 0) {
|
||
ESPILON_LOGI_PURPLE(TAG, "Registered commands: none");
|
||
return;
|
||
}
|
||
|
||
char buf[512];
|
||
int off = snprintf(
|
||
buf,
|
||
sizeof(buf),
|
||
"Registered commands (%d): ",
|
||
(int)registry_count
|
||
);
|
||
|
||
for (size_t i = 0; i < registry_count; i++) {
|
||
const char *name = registry[i] && registry[i]->name
|
||
? registry[i]->name : "?";
|
||
const char *sep = (i == 0) ? "" : ", ";
|
||
int n = snprintf(buf + off, sizeof(buf) - (size_t)off,
|
||
"%s%s", sep, name);
|
||
if (n < 0 || n >= (int)(sizeof(buf) - (size_t)off)) {
|
||
if (off < (int)sizeof(buf) - 4) {
|
||
strcpy(buf + (sizeof(buf) - 4), "...");
|
||
}
|
||
break;
|
||
}
|
||
off += n;
|
||
}
|
||
|
||
ESPILON_LOGI_PURPLE(TAG, "%s", buf);
|
||
}
|
||
|
||
/* =========================================================
|
||
* Helpers: deep-copy argv into one arena + argv[] pointers
|
||
* ========================================================= */
|
||
static bool deepcopy_argv(char *const *argv_in,
|
||
int argc,
|
||
char ***argv_out,
|
||
char **arena_out,
|
||
const char *req_id)
|
||
{
|
||
*argv_out = NULL;
|
||
*arena_out = NULL;
|
||
|
||
if (argc < 0) {
|
||
msg_error("cmd", "Invalid argc", req_id);
|
||
return false;
|
||
}
|
||
|
||
if (argc == 0) {
|
||
char **argv0 = (char **)calloc(1, sizeof(char *));
|
||
if (!argv0) {
|
||
msg_error("cmd", "OOM copying argv", req_id);
|
||
return false;
|
||
}
|
||
*argv_out = argv0;
|
||
*arena_out = NULL;
|
||
return true;
|
||
}
|
||
|
||
size_t total = 0;
|
||
for (int i = 0; i < argc; i++) {
|
||
const char *s = (argv_in && argv_in[i]) ? argv_in[i] : "";
|
||
size_t n = strnlen(s, COMMAND_MAX_ARG_LEN);
|
||
total += (n + 1);
|
||
}
|
||
|
||
char *arena = (char *)malloc(total ? total : 1);
|
||
char **argv_copy = (char **)malloc((size_t)argc * sizeof(char *));
|
||
if (!arena || !argv_copy) {
|
||
free(arena);
|
||
free(argv_copy);
|
||
msg_error("cmd", "OOM copying argv", req_id);
|
||
return false;
|
||
}
|
||
|
||
size_t off = 0;
|
||
for (int i = 0; i < argc; i++) {
|
||
const char *s = (argv_in && argv_in[i]) ? argv_in[i] : "";
|
||
size_t n = strnlen(s, COMMAND_MAX_ARG_LEN);
|
||
|
||
argv_copy[i] = &arena[off];
|
||
memcpy(&arena[off], s, n);
|
||
arena[off + n] = '\0';
|
||
off += (n + 1);
|
||
}
|
||
|
||
*argv_out = argv_copy;
|
||
*arena_out = arena;
|
||
return true;
|
||
}
|
||
|
||
/* =========================================================
|
||
* Dispatch nanopb command
|
||
* ========================================================= */
|
||
void command_process_pb(const c2_Command *cmd)
|
||
{
|
||
if (!cmd) return;
|
||
|
||
/* nanopb: tableaux fixes => jamais NULL */
|
||
const char *name = cmd->command_name;
|
||
const char *reqid = cmd->request_id;
|
||
const char *reqid_or_null = (reqid[0] ? reqid : NULL);
|
||
|
||
int argc = cmd->argv_count;
|
||
|
||
for (size_t i = 0; i < registry_count; i++) {
|
||
const command_t *c = registry[i];
|
||
|
||
if (strcmp(c->name, name) != 0)
|
||
continue;
|
||
|
||
if (argc < c->min_args || argc > c->max_args) {
|
||
msg_error("cmd", "Invalid argument count", reqid_or_null);
|
||
return;
|
||
}
|
||
|
||
ESP_LOGI(TAG, "Execute: %s (argc=%d)", name, argc);
|
||
|
||
if (c->async) {
|
||
/* Ton async copie déjà argv/request_id dans une queue => OK */
|
||
command_async_enqueue(c, cmd);
|
||
return;
|
||
}
|
||
|
||
/* ================================
|
||
* SYNC PATH (FIX):
|
||
* Ne PAS caster cmd->argv en char**
|
||
* On construit argv_ptrs[] depuis cmd->argv[i]
|
||
* ================================ */
|
||
if (argc > COMMAND_MAX_ARGS) {
|
||
msg_error("cmd", "Too many args", reqid_or_null);
|
||
return;
|
||
}
|
||
|
||
char *argv_ptrs[COMMAND_MAX_ARGS] = {0};
|
||
for (int a = 0; a < argc; a++) {
|
||
/* Fonctionne que cmd->argv soit char*[N] ou char[N][M] */
|
||
argv_ptrs[a] = (char *)cmd->argv[a];
|
||
}
|
||
|
||
/* Deep-copy pour rendre sync aussi safe que async */
|
||
char **argv_copy = NULL;
|
||
char *arena = NULL;
|
||
|
||
if (!deepcopy_argv(argv_ptrs, argc, &argv_copy, &arena, reqid_or_null))
|
||
return;
|
||
|
||
c->handler(argc, argv_copy, reqid_or_null, c->ctx);
|
||
|
||
free(argv_copy);
|
||
free(arena);
|
||
return;
|
||
}
|
||
|
||
msg_error("cmd", "Unknown command", reqid_or_null);
|
||
}
|