Support in newshell for <cmd>?*[j] and j*[j] <command>

This commit is contained in:
wargio 2021-02-06 16:24:04 +01:00 committed by Giovanni
parent f5664319be
commit 8cfa9e31e3
10 changed files with 437 additions and 87 deletions

View file

@ -89,10 +89,10 @@ err:
return NULL;
}
static bool do_autocmplt_cmdidentifier(RzCmd *cmd, const char *name, void *user) {
static bool do_autocmplt_cmdidentifier(RzCmd *cmd, const RzCmdDesc *desc, void *user) {
struct autocmplt_cmdidentifier_t *u = (struct autocmplt_cmdidentifier_t *)user;
if (!strncmp(name, u->s, u->len)) {
rz_line_ns_completion_result_add(u->res, name);
if (!strncmp(desc->name, u->s, u->len)) {
rz_line_ns_completion_result_add(u->res, desc->name);
}
return true;
}
@ -103,7 +103,7 @@ static void autocmplt_cmdidentifier(RzCore *core, RzLineNSCompletionResult *res,
.s = s,
.len = len,
};
rz_cmd_foreach_cmdname(core->rcmd, do_autocmplt_cmdidentifier, &u);
rz_cmd_foreach_cmdname(core->rcmd, NULL, do_autocmplt_cmdidentifier, &u);
}
static void autocmplt_cmd_arg_file(RzLineNSCompletionResult *res, const char *s, size_t len) {

View file

@ -4618,7 +4618,8 @@ DEFINE_HANDLE_TS_FCN_AND_SYMBOL(arged_command) {
if (!help_pra) {
goto err;
}
char *help_msg = rz_cmd_get_help(state->core->rcmd, help_pra, true);
bool use_color = state->core->print->flags & RZ_PRINT_FLAGS_COLOR;
char *help_msg = rz_cmd_get_help(state->core->rcmd, help_pra, use_color);
if (!help_msg) {
goto help_pra_err;
}
@ -4760,21 +4761,83 @@ DEFINE_HANDLE_TS_FCN_AND_SYMBOL(redirect_command) {
return res;
}
typedef struct _search_help {
bool color;
RzStrBuf *sb;
PJ *pj;
} RzHelpSearch;
static bool help_search_cmd_desc_entry(RzCmd *cmd, const RzCmdDesc *cd, void *user) {
rz_return_val_if_fail(cd, false);
RzHelpSearch *hs = (RzHelpSearch *)user;
if (hs->pj) {
rz_cmd_get_help_json(cmd, cd, hs->pj);
} else {
rz_cmd_get_help_strbuf(cmd, cd, hs->color, hs->sb);
}
return true;
}
RZ_IPI RzCmdStatus rz_cmd_help_search_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
RzCmdStatus status = RZ_CMD_STATUS_OK;
RzCmdDesc *begin = NULL;
if (argc == 2) {
begin = rz_cmd_get_desc(core->rcmd, argv[1]);
if (!begin) {
status = RZ_CMD_STATUS_NONEXISTINGCMD;
goto exit_status;
}
}
RzHelpSearch hs = {
.color = core->print->flags & RZ_PRINT_FLAGS_COLOR,
.pj = NULL,
.sb = NULL,
};
if (mode & RZ_OUTPUT_MODE_JSON) {
hs.pj = pj_new();
if (!hs.pj) {
status = RZ_CMD_STATUS_ERROR;
goto exit_status;
}
pj_o(hs.pj);
} else {
hs.sb = rz_strbuf_new(NULL);
if (!hs.sb) {
status = RZ_CMD_STATUS_ERROR;
goto exit_status;
}
}
rz_cmd_foreach_cmdname(core->rcmd, begin, help_search_cmd_desc_entry, &hs);
if (mode & RZ_OUTPUT_MODE_JSON) {
pj_end(hs.pj);
rz_cons_printf("%s\n", pj_string(hs.pj));
pj_free(hs.pj);
} else {
char *help = rz_strbuf_drain(hs.sb);
rz_cons_printf("%s", help);
free(help);
}
exit_status:
return status;
}
DEFINE_HANDLE_TS_FCN_AND_SYMBOL(help_command) {
size_t node_str_len = strlen(node_string);
if (node_str_len >= 2 && !strcmp(node_string + node_str_len - 2, "?*")) {
// TODO: get recursive help from RzCmdDesc
int detail = 0;
if (node_str_len > 3 && node_string[node_str_len - 3] == '?') {
detail++;
if (node_str_len > 4 && node_string[node_str_len - 4] == '?') {
detail++;
node_string[node_str_len - 2] = 0;
const char *argv[2] = { NULL, node_string };
return rz_cmd_help_search_handler(state->core, 2, argv, RZ_OUTPUT_MODE_STANDARD);
} else if (node_str_len >= 3 && !strcmp(node_string + node_str_len - 3, "?*j")) {
node_string[node_str_len - 3] = 0;
const char *argv[2] = { NULL, node_string };
return rz_cmd_help_search_handler(state->core, 2, argv, RZ_OUTPUT_MODE_JSON);
}
}
node_string[node_str_len - 2 - detail] = '\0';
recursive_help(state->core, detail, node_string);
return RZ_CMD_STATUS_OK;
} else {
TSNode command = ts_node_child_by_field_name(node, "command", strlen("command"));
char *command_str = ts_node_sub_string(command, state->input);
TSNode args = ts_node_child_by_field_name(node, "args", strlen("args"));
@ -4797,18 +4860,17 @@ DEFINE_HANDLE_TS_FCN_AND_SYMBOL(help_command) {
// let's try first with the new auto-generated help, if
// something fails fallback to old behaviour
char *help_msg = rz_cmd_get_help(state->core->rcmd, pr_args, state->core->print->flags & RZ_PRINT_FLAGS_COLOR);
bool use_color = state->core->print->flags & RZ_PRINT_FLAGS_COLOR;
char *help_msg = rz_cmd_get_help(state->core->rcmd, pr_args, use_color);
if (help_msg) {
rz_cons_printf("%s", help_msg);
free(help_msg);
res = RZ_CMD_STATUS_OK;
}
err_else:
err_else:
rz_cmd_parsed_args_free(pr_args);
free(command_str);
return res;
}
return RZ_CMD_STATUS_OK;
}
DEFINE_HANDLE_TS_FCN_AND_SYMBOL(tmp_seek_command) {

View file

@ -642,7 +642,7 @@ static size_t strbuf_append_calc(RzStrBuf *sb, const char *s) {
return strlen(s);
}
static void fill_modes_children_chars(RzStrBuf *sb, RzCmdDesc *cd) {
static void fill_modes_children_chars(RzStrBuf *sb, const RzCmdDesc *cd) {
// RZ_CMD_DESC_TYPE_ARGV_MODES does not have actual children for
// the output modes, so we consider it separately
size_t i;
@ -653,7 +653,7 @@ static void fill_modes_children_chars(RzStrBuf *sb, RzCmdDesc *cd) {
}
}
static size_t fill_children_chars(RzStrBuf *sb, RzCmdDesc *cd) {
static size_t fill_children_chars(RzStrBuf *sb, const RzCmdDesc *cd) {
if (cd->help->options) {
return strbuf_append_calc(sb, cd->help->options);
}
@ -663,7 +663,7 @@ static size_t fill_children_chars(RzStrBuf *sb, RzCmdDesc *cd) {
void **it;
bool has_other_commands = false;
RzCmdDesc *exec_cd = rz_cmd_desc_get_exec(cd);
const RzCmdDesc *exec_cd = rz_cmd_desc_get_exec((RzCmdDesc *)cd);
if (exec_cd) {
switch (exec_cd->type) {
case RZ_CMD_DESC_TYPE_ARGV_MODES:
@ -706,7 +706,7 @@ static size_t fill_children_chars(RzStrBuf *sb, RzCmdDesc *cd) {
return res;
}
static bool show_children_shortcut(RzCmdDesc *cd) {
static bool show_children_shortcut(const RzCmdDesc *cd) {
return cd->n_children || cd->help->options || cd->type == RZ_CMD_DESC_TYPE_OLDINPUT || cd->type == RZ_CMD_DESC_TYPE_ARGV_MODES;
}
@ -739,7 +739,7 @@ static void fill_wrapped_comment(RzCmd *cmd, RzStrBuf *sb, const char *comment,
}
}
static size_t fill_args(RzStrBuf *sb, RzCmdDesc *cd) {
static size_t fill_args(RzStrBuf *sb, const RzCmdDesc *cd) {
const RzCmdDescArg *arg;
size_t n_optionals = 0;
size_t len = 0;
@ -836,11 +836,11 @@ static void fill_usage_strbuf(RzCmd *cmd, RzStrBuf *sb, RzCmdDesc *cd, bool use_
rz_strbuf_append(sb, "\n");
}
static size_t calc_padding_len(RzCmdDesc *cd, const char *name) {
static size_t calc_padding_len(const RzCmdDesc *cd, const char *name, bool show_children) {
size_t name_len = strlen(name);
size_t args_len = 0;
size_t children_length = 0;
if (show_children_shortcut(cd)) {
if (show_children && show_children_shortcut(cd)) {
RzStrBuf sb;
rz_strbuf_init(&sb);
fill_children_chars(&sb, cd);
@ -859,14 +859,14 @@ static size_t calc_padding_len(RzCmdDesc *cd, const char *name) {
return name_len + args_len + children_length;
}
static void update_minmax_len(RzCmdDesc *cd, size_t *max_len, size_t *min_len) {
size_t val = calc_padding_len(cd, cd->name);
static void update_minmax_len(RzCmdDesc *cd, size_t *max_len, size_t *min_len, bool show_children) {
size_t val = calc_padding_len(cd, cd->name, show_children);
*max_len = val > *max_len ? val : *max_len;
*min_len = val < *min_len ? val : *min_len;
}
static void do_print_child_help(RzCmd *cmd, RzStrBuf *sb, RzCmdDesc *cd, const char *name, const char *summary, bool show_children, size_t max_len, bool use_color) {
size_t str_len = calc_padding_len(cd, name);
static void do_print_child_help(RzCmd *cmd, RzStrBuf *sb, const RzCmdDesc *cd, const char *name, const char *summary, bool show_children, size_t max_len, bool use_color) {
size_t str_len = calc_padding_len(cd, name, show_children);
int padding = str_len < max_len ? max_len - str_len : 0;
const char *pal_args_color = "",
*pal_opt_color = "",
@ -918,7 +918,7 @@ static char *group_get_help(RzCmd *cmd, RzCmdDesc *cd, bool use_color) {
rz_cmd_desc_children_foreach(cd, it_cd) {
RzCmdDesc *child = *(RzCmdDesc **)it_cd;
update_minmax_len(child, &max_len, &min_len);
update_minmax_len(child, &max_len, &min_len, true);
}
if (max_len - min_len > MAX_RIGHT_ALIGHNMENT) {
max_len = min_len + MAX_RIGHT_ALIGHNMENT;
@ -936,7 +936,7 @@ static char *argv_modes_get_help(RzCmd *cmd, RzCmdDesc *cd, bool use_color) {
fill_usage_strbuf(cmd, sb, cd, use_color);
size_t max_len = 0, min_len = SIZE_MAX;
update_minmax_len(cd, &max_len, &min_len);
update_minmax_len(cd, &max_len, &min_len, true);
max_len++; // consider the suffix letter
if (max_len - min_len > MAX_RIGHT_ALIGHNMENT) {
max_len = min_len + MAX_RIGHT_ALIGHNMENT;
@ -1022,17 +1022,18 @@ static char *argv_get_help(RzCmd *cmd, RzCmdDesc *cd, size_t detail, bool use_co
switch (detail) {
case 1:
return rz_strbuf_drain(sb);
break;
case 2:
if (cd->help->description) {
rz_strbuf_appendf(sb, "\n%s\n", cd->help->description);
}
fill_details(cmd, cd, sb, use_color);
return rz_strbuf_drain(sb);
break;
default:
rz_strbuf_free(sb);
return NULL;
}
return rz_strbuf_drain(sb);
}
static char *fake_get_help(RzCmd *cmd, RzCmdDesc *cd, bool use_color) {
@ -1089,6 +1090,142 @@ static char *get_help(RzCmd *cmd, RzCmdDesc *cd, RzCmdParsedArgs *args, bool use
return NULL;
}
static void fill_args_json(const RzCmdDesc *cd, PJ *j) {
const RzCmdDescArg *arg;
bool has_array = false;
pj_ka(j, "args");
const char *argtype = NULL;
for (arg = cd->help->args; arg && arg->name; arg++) {
if (has_array) {
rz_warn_if_reached();
break;
}
pj_o(j);
#define CASE_TYPE(x, y) \
case (x): \
argtype = (y); \
break
switch (arg->type) {
CASE_TYPE(RZ_CMD_ARG_TYPE_FAKE, "fake");
CASE_TYPE(RZ_CMD_ARG_TYPE_NUM, "number");
CASE_TYPE(RZ_CMD_ARG_TYPE_RZNUM, "expression");
CASE_TYPE(RZ_CMD_ARG_TYPE_STRING, "string");
CASE_TYPE(RZ_CMD_ARG_TYPE_ENV, "environment_variable");
CASE_TYPE(RZ_CMD_ARG_TYPE_ZIGN, "zignature");
CASE_TYPE(RZ_CMD_ARG_TYPE_ZIGN_SPACE, "zignature_space");
CASE_TYPE(RZ_CMD_ARG_TYPE_CHOICES, "choice");
CASE_TYPE(RZ_CMD_ARG_TYPE_FCN, "function");
CASE_TYPE(RZ_CMD_ARG_TYPE_FILE, "filename");
CASE_TYPE(RZ_CMD_ARG_TYPE_OPTION, "option");
CASE_TYPE(RZ_CMD_ARG_TYPE_CMD, "command");
CASE_TYPE(RZ_CMD_ARG_TYPE_MACRO, "macro");
CASE_TYPE(RZ_CMD_ARG_TYPE_EVAL_KEY, "evaluable");
CASE_TYPE(RZ_CMD_ARG_TYPE_EVAL_FULL, "evaluable_full");
#undef CASE_TYPE
default:
argtype = "unknown";
break;
}
pj_ks(j, "type", argtype);
pj_ks(j, "name", arg->name);
if (arg->type == RZ_CMD_ARG_TYPE_FAKE) {
pj_end(j);
continue;
}
if (arg->no_space) {
pj_kb(j, "nospace", true);
}
if (!arg->optional) {
pj_kb(j, "required", true);
}
if (arg->flags & RZ_CMD_ARG_FLAG_LAST) {
pj_kb(j, "is_last", true);
}
if (arg->flags & RZ_CMD_ARG_FLAG_ARRAY) {
pj_kb(j, "is_array", true);
}
if (arg->flags & RZ_CMD_ARG_FLAG_OPTION) {
pj_kb(j, "is_option", true);
}
if (arg->default_value) {
pj_ks(j, "default", arg->default_value);
}
if (arg->type == RZ_CMD_ARG_TYPE_CHOICES) {
pj_ka(j, "choices");
const char **choice = arg->choices;
for (; *choice; choice++) {
pj_s(j, *choice);
}
pj_end(j);
}
pj_end(j);
}
pj_end(j);
}
/**
* \brief Generates a JSON output of the given help message description
*
* \param cmd reference to RzCmd
* \param cd reference to RzCmdDesc
* \param j reference to PJ
*
* \return returns false if an invalid argument was given, otherwise true.
*/
RZ_API bool rz_cmd_get_help_json(RzCmd *cmd, const RzCmdDesc *cd, PJ *j) {
rz_return_val_if_fail(cmd && cd && j, false);
pj_ko(j, cd->name);
pj_ks(j, "cmd", cd->name);
const char *type;
switch (cd->type) {
#define CASE_CDTYPE(x, y) \
case (x): \
type = (y); \
break
CASE_CDTYPE(RZ_CMD_DESC_TYPE_OLDINPUT, "oldinput");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_ARGV, "argv");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_GROUP, "group");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_INNER, "inner");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_FAKE, "fake");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_ARGV_MODES, "argv_modes");
#undef CASE_CDTYPE
default:
type = "unknown";
break;
}
pj_ks(j, "type", type);
if (cd->help->args_str) {
pj_ks(j, "args_str", cd->help->args_str);
} else {
RzStrBuf *sb = rz_strbuf_new(NULL);
fill_args(sb, cd);
char *args = rz_strbuf_drain(sb);
pj_ks(j, "args_str", args);
free(args);
}
fill_args_json(cd, j);
pj_ks(j, "description", cd->help->description ? cd->help->description : "");
pj_ks(j, "summary", cd->help->summary ? cd->help->summary : "");
pj_end(j);
return true;
}
/**
* \brief Generates a text output of the given help message description (summary format)
*
* \param cmd reference to RzCmd
* \param cd reference to RzCmdDesc
* \param use_color output strings with color codes.
* \param sb reference to RzStrBuf
*
* \return returns false if an invalid argument was given, otherwise true.
*/
RZ_API bool rz_cmd_get_help_strbuf(RzCmd *cmd, const RzCmdDesc *cd, bool use_color, RzStrBuf *sb) {
rz_return_val_if_fail(cmd && cd && sb, false);
do_print_child_help(cmd, sb, cd, cd->name, cd->help->summary, false, MAX_RIGHT_ALIGHNMENT, use_color);
return true;
}
RZ_API char *rz_cmd_get_help(RzCmd *cmd, RzCmdParsedArgs *args, bool use_color) {
char *cmdid = strdup(rz_cmd_parsed_args_cmd(args));
char *cmdid_p = cmdid + strlen(cmdid) - 1;
@ -1822,7 +1959,7 @@ RZ_API RzCmdDesc *rz_cmd_desc_parent(RzCmdDesc *cd) {
return cd->parent;
}
RZ_API bool rz_cmd_desc_has_handler(RzCmdDesc *cd) {
RZ_API bool rz_cmd_desc_has_handler(const RzCmdDesc *cd) {
rz_return_val_if_fail(cd, false);
switch (cd->type) {
case RZ_CMD_DESC_TYPE_ARGV:
@ -1879,6 +2016,17 @@ RZ_API const RzCmdDescArg *rz_cmd_desc_get_arg(RzCmd *cmd, const RzCmdDesc *cd,
return NULL;
}
static RzCmdDescHelp *mode_cmd_desc_help(RzCmdDescHelp *dst, const RzCmdDescHelp *src, const char *suffix) {
dst->summary = rz_str_newf("%s%s", src->summary, suffix);
dst->description = src->description;
dst->args_str = src->args_str;
dst->usage = src->usage;
dst->options = src->options;
dst->details = src->details;
dst->args = src->args;
return dst;
}
static void cmd_foreach_cmdname(RzCmd *cmd, RzCmdDesc *cd, RzCmdForeachNameCb cb, void *user) {
if (!cd) {
return;
@ -1890,15 +2038,25 @@ static void cmd_foreach_cmdname(RzCmd *cmd, RzCmdDesc *cd, RzCmdForeachNameCb cb
switch (cd->type) {
case RZ_CMD_DESC_TYPE_ARGV:
if (rz_cmd_desc_has_handler(cd)) {
cb(cmd, cd->name, user);
cb(cmd, cd, user);
}
break;
case RZ_CMD_DESC_TYPE_ARGV_MODES:
for (i = 0; i < RZ_ARRAY_SIZE(argv_modes); i++) {
if (cd->d.argv_modes_data.modes & argv_modes[i].mode) {
char *name = rz_str_newf("%s%s", cd->name, argv_modes[i].suffix);
cb(cmd, name, user);
free(name);
RzCmdDescHelp mode_help;
const RzCmdDescHelp *copy = cd->help;
cd->help = mode_cmd_desc_help(&mode_help, copy, argv_modes[i].summary_suffix);
char *name = cd->name;
cd->name = rz_str_newf("%s%s", name, argv_modes[i].suffix);
cb(cmd, cd, user);
free(cd->name);
free((char *)mode_help.summary);
cd->name = name;
cd->help = copy;
}
}
break;
@ -1906,7 +2064,7 @@ static void cmd_foreach_cmdname(RzCmd *cmd, RzCmdDesc *cd, RzCmdForeachNameCb cb
break;
case RZ_CMD_DESC_TYPE_OLDINPUT:
if (rz_cmd_desc_has_handler(cd)) {
cb(cmd, cd->name, user);
cb(cmd, cd, user);
}
// fallthrough
case RZ_CMD_DESC_TYPE_INNER:
@ -1926,11 +2084,12 @@ static void cmd_foreach_cmdname(RzCmd *cmd, RzCmdDesc *cd, RzCmdForeachNameCb cb
* commands (e.g. ?, h?, etc.) are ignored.
*
* \param cmd Reference to RzCmd
* \param begin Reference to RzCmdDesc from where to begin the for loop; if NULL the root will be used.
* \param cb Callback function that is called for each command name.
* \param user Additional user data that is passed to the callback \p cb.
*/
RZ_API void rz_cmd_foreach_cmdname(RzCmd *cmd, RzCmdForeachNameCb cb, void *user) {
RzCmdDesc *cd = rz_cmd_get_root(cmd);
RZ_API void rz_cmd_foreach_cmdname(RzCmd *cmd, RzCmdDesc *begin, RzCmdForeachNameCb cb, void *user) {
RzCmdDesc *cd = begin ? begin : rz_cmd_get_root(cmd);
cmd_foreach_cmdname(cmd, cd, cb, user);
}

View file

@ -45,6 +45,7 @@ static const RzCmdDescArg remote_open_args[2];
static const RzCmdDescArg remote_rap_args[3];
static const RzCmdDescArg remote_tcp_args[3];
static const RzCmdDescArg remote_rap_bg_args[2];
static const RzCmdDescArg cmd_help_search_args[2];
static const RzCmdDescArg push_escaped_args[2];
static const RzCmdDescArg eval_getset_args[2];
static const RzCmdDescArg eval_list_args[2];
@ -598,6 +599,20 @@ static const RzCmdDescHelp remote_rap_bg_help = {
.args = remote_rap_bg_args,
};
static const RzCmdDescArg cmd_help_search_args[] = {
{
.name = "search_cmd",
.type = RZ_CMD_ARG_TYPE_STRING,
.optional = true,
},
{ 0 },
};
static const RzCmdDescHelp cmd_help_search_help = {
.summary = "Search help",
.args = cmd_help_search_args,
};
static const RzCmdDescHelp cmd_help_help = {
.summary = "Help or evaluate math expression",
.options = "[??]",
@ -2362,6 +2377,9 @@ RZ_IPI void newshell_cmddescs_init(RzCore *core) {
RzCmdDesc *remote_rap_bg_cd = rz_cmd_desc_argv_new(core->rcmd, equal__cd, "=&:", rz_remote_rap_bg_handler, &remote_rap_bg_help);
rz_warn_if_fail(remote_rap_bg_cd);
RzCmdDesc *cmd_help_search_cd = rz_cmd_desc_argv_modes_new(core->rcmd, root_cd, "?*", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_cmd_help_search_handler, &cmd_help_search_help);
rz_warn_if_fail(cmd_help_search_cd);
RzCmdDesc *cmd_help_cd = rz_cmd_desc_oldinput_new(core->rcmd, root_cd, "?", rz_cmd_help, &cmd_help_help);
rz_warn_if_fail(cmd_help_cd);

View file

@ -47,6 +47,7 @@ RZ_IPI int rz_equal_h_handler_old(void *data, const char *input);
RZ_IPI int rz_equal_H_handler_old(void *data, const char *input);
RZ_IPI RzCmdStatus rz_remote_tcp_handler(RzCore *core, int argc, const char **argv);
RZ_IPI RzCmdStatus rz_remote_rap_bg_handler(RzCore *core, int argc, const char **argv);
RZ_IPI RzCmdStatus rz_cmd_help_search_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode);
RZ_IPI int rz_cmd_help(void *data, const char *input);
RZ_IPI RzCmdStatus rz_push_escaped_handler(RzCore *core, int argc, const char **argv);
RZ_IPI int rz_cmd_ox(void *data, const char *input);

View file

@ -347,6 +347,16 @@
args:
- name: port
type: RZ_CMD_ARG_TYPE_NUM
- name: "?*"
cname: cmd_help_search
summary: Search help
args:
- name: search_cmd
type: RZ_CMD_ARG_TYPE_STRING
optional: true
modes:
- RZ_OUTPUT_MODE_STANDARD
- RZ_OUTPUT_MODE_JSON
- name: "?"
cname: cmd_help
options: "[??]"

View file

@ -431,7 +431,7 @@ typedef struct rz_core_plugin_t {
RzCmdCb fini;
} RzCorePlugin;
typedef bool (*RzCmdForeachNameCb)(RzCmd *cmd, const char *name, void *user);
typedef bool (*RzCmdForeachNameCb)(RzCmd *cmd, const RzCmdDesc *desc, void *user);
#ifdef RZ_API
RZ_API int rz_core_plugin_init(RzCmd *cmd);
@ -449,6 +449,8 @@ RZ_API RzCmdStatus rz_cmd_call_parsed_args(RzCmd *cmd, RzCmdParsedArgs *args);
RZ_API RzCmdDesc *rz_cmd_get_root(RzCmd *cmd);
RZ_API RzCmdDesc *rz_cmd_get_desc(RzCmd *cmd, const char *cmd_identifier);
RZ_API char *rz_cmd_get_help(RzCmd *cmd, RzCmdParsedArgs *args, bool use_color);
RZ_API bool rz_cmd_get_help_json(RzCmd *cmd, const RzCmdDesc *cd, PJ *j);
RZ_API bool rz_cmd_get_help_strbuf(RzCmd *cmd, const RzCmdDesc *cd, bool use_color, RzStrBuf *sb);
static inline RzCmdStatus rz_cmd_int2status(int v) {
if (v == -2) {
@ -485,9 +487,9 @@ RZ_API RzCmdDesc *rz_cmd_desc_oldinput_new(RzCmd *cmd, RzCmdDesc *parent, const
RZ_API RzCmdDesc *rz_cmd_desc_fake_new(RzCmd *cmd, RzCmdDesc *parent, const char *name, const RzCmdDescHelp *help);
RZ_API RzCmdDesc *rz_cmd_desc_parent(RzCmdDesc *cd);
RZ_API RzCmdDesc *rz_cmd_desc_get_exec(RzCmdDesc *cd);
RZ_API bool rz_cmd_desc_has_handler(RzCmdDesc *cd);
RZ_API bool rz_cmd_desc_has_handler(const RzCmdDesc *cd);
RZ_API bool rz_cmd_desc_remove(RzCmd *cmd, RzCmdDesc *cd);
RZ_API void rz_cmd_foreach_cmdname(RzCmd *cmd, RzCmdForeachNameCb cb, void *user);
RZ_API void rz_cmd_foreach_cmdname(RzCmd *cmd, RzCmdDesc *begin, RzCmdForeachNameCb cb, void *user);
RZ_API const RzCmdDescArg *rz_cmd_desc_get_arg(RzCmd *cmd, const RzCmdDesc *cd, size_t i);
#define rz_cmd_desc_children_foreach(root, it_cd) rz_pvector_foreach (&root->children, it_cd)

View file

@ -104,8 +104,12 @@ static bool is_concat_eq_sep(const int32_t ch) {
return is_concat(ch) && ch != '=';
}
static bool is_recursive_help(int id_len, const int32_t before_last_ch, const int32_t last_ch) {
return id_len >= 2 && before_last_ch == '?' && last_ch == '*';
static bool is_recursive_help(const int32_t before_last_ch, const int32_t last_ch) {
return before_last_ch == '?' && last_ch == '*';
}
static bool is_recursive_help_json(const int32_t trd_last_ch, const int32_t snd_last_ch, const int32_t last_ch) {
return trd_last_ch == '?' && snd_last_ch == '*' && last_ch == 'j';
}
static bool scan_number(TSLexer *lexer, const bool *valid_symbols) {
@ -178,7 +182,9 @@ bool tree_sitter_rzcmd_external_scanner_scan(void *payload, TSLexer *lexer, cons
return false;
}
// ?? is not considered an help command, just a regular one
if ((res[i_res - 1] == '?' && strcmp (res, "??")) || (i_res >= 2 && is_recursive_help (i_res, res[i_res - 2], res[i_res - 1]))) {
if ((res[i_res - 1] == '?' && strcmp (res, "??") != 0) ||
(i_res > 2 && is_recursive_help (res[i_res - 2], res[i_res - 1])) ||
(i_res > 3 && is_recursive_help_json (res[i_res - 3], res[i_res - 2], res[i_res - 1]))) {
if (i_res == 1) {
return false;
}

View file

@ -243,3 +243,53 @@ EXPECT=<<EOF
0x116e
EOF
RUN
NAME=recursive help
FILE=-
CMDS=<<EOF
?* zi
EOF
EXPECT=<<EOF
| zi [<commens>] # Show zignatures matching information
| zij [<commens>] # Show zignatures matching information (JSON mode)
| zi* [<commens>] # Show zignatures matching information (rizin mode)
| ziq [<commens>] # Show zignatures matching information (quiet mode)
| zii <from> <to> # Show zignatures matching information in range
EOF
RUN
NAME=recursive help (json)
FILE=-
CMDS=<<EOF
?*j zi
EOF
EXPECT=<<EOF
{"zi":{"cmd":"zi","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information"},"zij":{"cmd":"zij","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information (JSON mode)"},"zi*":{"cmd":"zi*","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information (rizin mode)"},"ziq":{"cmd":"ziq","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information (quiet mode)"},"zii":{"cmd":"zii","type":"argv","args_str":" <from> <to>","args":[{"type":"number","name":"from","required":true},{"type":"number","name":"to","required":true}],"description":"","summary":"Show zignatures matching information in range"}}
EOF
RUN
NAME=old recursive help
FILE=-
CMDS=<<EOF
zi?*
EOF
EXPECT=<<EOF
| zi [<commens>] # Show zignatures matching information
| zij [<commens>] # Show zignatures matching information (JSON mode)
| zi* [<commens>] # Show zignatures matching information (rizin mode)
| ziq [<commens>] # Show zignatures matching information (quiet mode)
| zii <from> <to> # Show zignatures matching information in range
EOF
RUN
NAME=old recursive help (json)
FILE=-
CMDS=<<EOF
zi?*j
EOF
EXPECT=<<EOF
{"zi":{"cmd":"zi","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information"},"zij":{"cmd":"zij","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information (JSON mode)"},"zi*":{"cmd":"zi*","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information (rizin mode)"},"ziq":{"cmd":"ziq","type":"argv_modes","args_str":" [<commens>]","args":[{"type":"string","name":"commens"}],"description":"","summary":"Show zignatures matching information (quiet mode)"},"zii":{"cmd":"zii","type":"argv","args_str":" <from> <to>","args":[{"type":"number","name":"from","required":true},{"type":"number","name":"to","required":true}],"description":"","summary":"Show zignatures matching information in range"}}
EOF
RUN

View file

@ -599,8 +599,8 @@ bool test_cmd_group_argv_modes(void) {
mu_end;
}
static bool foreach_cmdname_cb(RzCmd *cmd, const char *name, void *user) {
rz_list_append((RzList *)user, strdup(name));
static bool foreach_cmdname_cb(RzCmd *cmd, const RzCmdDesc *desc, void *user) {
rz_list_append((RzList *)user, strdup(desc->name));
return true;
}
@ -619,7 +619,7 @@ bool test_foreach_cmdname(void) {
rz_cmd_desc_argv_new(cmd, v_inner_cd, "v2", zd_handler, &fake_help);
RzList *res = rz_list_newf(free);
rz_cmd_foreach_cmdname(cmd, foreach_cmdname_cb, res);
rz_cmd_foreach_cmdname(cmd, NULL, foreach_cmdname_cb, res);
const char *exp_regular[] = { "z", "zj", "zq", "zd", "zsq", "pi", "v", "v1", "v2" };
mu_assert_eq(rz_list_length(res), RZ_ARRAY_SIZE(exp_regular), "count regular commands that can be executed");
@ -645,6 +645,47 @@ bool test_foreach_cmdname(void) {
mu_end;
}
bool test_foreach_cmdname_begin(void) {
RzCmd *cmd = rz_cmd_new(false);
RzCmdDesc *root = rz_cmd_get_root(cmd);
RzCmdDesc *z_cd = rz_cmd_desc_group_modes_new(cmd, root, "z", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_QUIET, z_modes_handler, &fake_help, &fake_help);
rz_cmd_desc_argv_new(cmd, z_cd, "zd", zd_handler, &fake_help);
rz_cmd_desc_argv_modes_new(cmd, z_cd, "zs", RZ_OUTPUT_MODE_QUIET, z_modes_handler, &fake_help);
rz_cmd_desc_fake_new(cmd, root, "x", &fake_help);
RzCmdDesc *p_cd = rz_cmd_desc_group_new(cmd, root, "p", NULL, NULL, &fake_help);
rz_cmd_desc_argv_new(cmd, p_cd, "pi", zd_handler, &fake_help);
RzCmdDesc *v_cd = rz_cmd_desc_oldinput_new(cmd, root, "v", a_oldinput_cb, &fake_help);
RzCmdDesc *v_inner_cd = rz_cmd_desc_inner_new(cmd, v_cd, "v", &fake_help);
rz_cmd_desc_argv_new(cmd, v_inner_cd, "v1", zd_handler, &fake_help);
rz_cmd_desc_argv_new(cmd, v_inner_cd, "v2", zd_handler, &fake_help);
RzList *res = rz_list_newf(free);
rz_cmd_foreach_cmdname(cmd, v_cd, foreach_cmdname_cb, res);
const char *exp_regular[] = { "v", "v1", "v2" };
mu_assert_eq(rz_list_length(res), RZ_ARRAY_SIZE(exp_regular), "count regular commands that can be executed");
RzList *exp_regular_l = rz_list_new_from_array((const void **)exp_regular, RZ_ARRAY_SIZE(exp_regular));
rz_list_sort(exp_regular_l, (RzListComparator)strcmp);
rz_list_sort(res, (RzListComparator)strcmp);
RzListIter *it;
char *s;
size_t i = 0;
rz_list_foreach (exp_regular_l, it, s) {
RzStrBuf sb;
rz_strbuf_initf(&sb, "check command `%s`", s);
mu_assert_streq(rz_list_get_n(res, i++), s, rz_strbuf_get(&sb));
rz_strbuf_fini(&sb);
}
rz_list_free(res);
rz_list_free(exp_regular_l);
rz_cmd_free(cmd);
mu_end;
}
bool test_arg_escaping(void) {
mu_assert_streq_free(rz_cmd_escape_arg("hello", RZ_CMD_ESCAPE_ONE_ARG), "hello", "regular string remains the same");
mu_assert_streq_free(rz_cmd_escape_arg("hello world", RZ_CMD_ESCAPE_ONE_ARG), "hello\\ world", "spaces are escaped");
@ -829,6 +870,7 @@ int all_tests() {
mu_run_test(test_cmd_argv_modes);
mu_run_test(test_cmd_group_argv_modes);
mu_run_test(test_foreach_cmdname);
mu_run_test(test_foreach_cmdname_begin);
mu_run_test(test_arg_escaping);
mu_run_test(test_double_quoted_arg_escaping);
mu_run_test(test_single_quoted_arg_escaping);