Skip to content

Commit

Permalink
core: allow to sort subcommands of a command group
Browse files Browse the repository at this point in the history
  • Loading branch information
ret2libc committed Mar 1, 2021
1 parent 38e8b2f commit 890ddb6
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 12 deletions.
52 changes: 50 additions & 2 deletions librz/core/cmd_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ static const RzCmdDescHelp not_defined_help = {
static const RzCmdDescHelp root_help = {
.usage = "[.][times][cmd][~grep][@[@iter]addr!size][|>pipe] ; ...",
.description = "",
.sort_subcommands = true,
};

static const struct argv_modes_t {
Expand Down Expand Up @@ -81,7 +82,13 @@ static int value = 0;
#define NCMDS (sizeof(cmd->cmds) / sizeof(*cmd->cmds))
RZ_LIB_VERSION(rz_cmd);

static bool cmd_desc_set_parent(RzCmdDesc *cd, RzCmdDesc *parent) {
static int cd_sort(const void *a, const void *b) {
RzCmdDesc *ca = (RzCmdDesc *)a;
RzCmdDesc *cb = (RzCmdDesc *)b;
return rz_str_casecmp(ca->name, cb->name);
}

static bool cmd_desc_set_parent(RzCmd *cmd, RzCmdDesc *cd, RzCmdDesc *parent) {
rz_return_val_if_fail(cd && !cd->parent, false);
if (parent) {
switch (parent->type) {
Expand All @@ -99,6 +106,9 @@ static bool cmd_desc_set_parent(RzCmdDesc *cd, RzCmdDesc *parent) {
if (parent) {
cd->parent = parent;
rz_pvector_push(&parent->children, cd);
if (!cmd->batch && parent->help->sort_subcommands) {
rz_pvector_sort(&parent->children, cd_sort);
}
parent->n_children++;
}
return true;
Expand Down Expand Up @@ -148,7 +158,7 @@ static RzCmdDesc *create_cmd_desc(RzCmd *cmd, RzCmdDesc *parent, RzCmdDescType t
if (ht_insert && !ht_pp_insert(cmd->ht_cmds, name, res)) {
goto err;
}
cmd_desc_set_parent(res, parent);
cmd_desc_set_parent(cmd, res, parent);
return res;
err:
cmd_desc_free(res);
Expand Down Expand Up @@ -200,10 +210,48 @@ RZ_API RzCmd *rz_cmd_free(RzCmd *cmd) {
return NULL;
}

/**
* \brief Get the root command descriptor
*/
RZ_API RzCmdDesc *rz_cmd_get_root(RzCmd *cmd) {
return cmd->root_cmd_desc;
}

/**
* \brief Mark the start of the batched changes to RzCmd
*
* Commands added after this call won't be sorted until \p rz_cmd_batch_end is
* called.
*/
RZ_API void rz_cmd_batch_start(RzCmd *cmd) {
cmd->batch = true;
}

static void sort_groups(RzCmdDesc *group) {
void **it_cd;

if (group->help->sort_subcommands) {
rz_pvector_sort(&group->children, cd_sort);
}
rz_cmd_desc_children_foreach(group, it_cd) {
RzCmdDesc *cd = *(RzCmdDesc **)it_cd;
if (cd->n_children) {
sort_groups(cd);
}
}
}

/**
* \brief Mark the end of the batched changes to RzCmd
*
* All groups are sorted, if necessary. Call \p rz_cmd_batch_start before using
* this function.
*/
RZ_API void rz_cmd_batch_end(RzCmd *cmd) {
cmd->batch = false;
sort_groups(rz_cmd_get_root(cmd));
}

static RzOutputMode suffix2mode(const char *suffix) {
size_t i;
for (i = 0; i < RZ_ARRAY_SIZE(argv_modes); i++) {
Expand Down
2 changes: 2 additions & 0 deletions librz/core/cmd_descs/cmd_descs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3269,6 +3269,7 @@ static const RzCmdDescHelp grep_help = {

RZ_IPI void newshell_cmddescs_init(RzCore *core) {
RzCmdDesc *root_cd = rz_cmd_get_root(core->rcmd);
rz_cmd_batch_start(core->rcmd);

RzCmdDesc *cmd_system_cd = rz_cmd_desc_oldinput_new(core->rcmd, root_cd, "!", rz_cmd_system, &cmd_system_help);
rz_warn_if_fail(cmd_system_cd);
Expand Down Expand Up @@ -3944,4 +3945,5 @@ RZ_IPI void newshell_cmddescs_init(RzCore *core) {

RzCmdDesc *grep_cd = rz_cmd_desc_fake_new(core->rcmd, root_cd, "~", &grep_help);
rz_warn_if_fail(grep_cd);
rz_cmd_batch_end(core->rcmd);
}
2 changes: 2 additions & 0 deletions librz/core/cmd_descs/cmd_descs_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
{helps}
RZ_IPI void newshell_cmddescs_init(RzCore *core) {{
\tRzCmdDesc *root_cd = rz_cmd_get_root(core->rcmd);
\trz_cmd_batch_start(core->rcmd);
{init_code}
\trz_cmd_batch_end(core->rcmd);
}}
"""

Expand Down
16 changes: 16 additions & 0 deletions librz/include/rz_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,13 @@ typedef struct rz_cmd_desc_help_t {
* Optional.
*/
const char *options;
/**
* When true, the subcommands are automatically sorted alphabetically. By
* default subcommands are shown in the order provided by the developer.
*
* Optional.
*/
bool sort_subcommands;
/**
* NULL-terminated array of details sections used to better explain how
* to use the command. This is shown together with the long description.
Expand Down Expand Up @@ -408,6 +415,13 @@ typedef struct rz_cmd_t {
* non-initialized RzCons.
*/
bool has_cons;
/**
* True when you want to add multiple commands in batch. This is an
* optimization mainly for groups that require sorted sub-commands, so
* instead of sorting on each addition we just sort one time at the end.
* False by default.
*/
bool batch;
} RzCmd;

// TODO: remove this once transitioned to RzCmdDesc
Expand All @@ -425,6 +439,8 @@ typedef bool (*RzCmdForeachNameCb)(RzCmd *cmd, const RzCmdDesc *desc, void *user
RZ_API RzCmd *rz_cmd_new(bool has_cons);
RZ_API RzCmd *rz_cmd_free(RzCmd *cmd);
RZ_API int rz_cmd_set_data(RzCmd *cmd, void *data);
RZ_API void rz_cmd_batch_start(RzCmd *cmd);
RZ_API void rz_cmd_batch_end(RzCmd *cmd);
RZ_API int rz_cmd_add(RzCmd *cmd, const char *command, RzCmdCb callback);
RZ_API int rz_core_del(RzCmd *cmd, const char *command);
RZ_API int rz_cmd_call(RzCmd *cmd, const char *command);
Expand Down
9 changes: 7 additions & 2 deletions librz/util/str.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@ static const char *rwxstr[] = {
};

RZ_API int rz_str_casecmp(const char *s1, const char *s2) {
int res;
#ifdef _MSC_VER
return stricmp(s1, s2);
res = stricmp(s1, s2);
#else
return strcasecmp(s1, s2);
res = strcasecmp(s1, s2);
#endif
if (res == 0) {
res = strcmp(s1, s2);
}
return res;
}

RZ_API int rz_str_ncasecmp(const char *s1, const char *s2, size_t n) {
Expand Down
16 changes: 8 additions & 8 deletions test/unit/test_autocmplt.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ static bool test_autocmplt_newcommand(void) {
mu_assert_eq(r->start, 0, "should autocomplete starting from 0");
mu_assert_eq(r->end, 0, "should autocomplete ending at 0");
mu_assert_eq(rz_pvector_len(&r->options), 4, "there are 4 commands available");
mu_assert_streq(rz_pvector_at(&r->options, 0), "xd", "one is xd");
mu_assert_streq(rz_pvector_at(&r->options, 1), "xe", "one is xe");
mu_assert_streq(rz_pvector_at(&r->options, 2), "p", "one is p");
mu_assert_streq(rz_pvector_at(&r->options, 3), "s", "one is s");
mu_assert_streq(rz_pvector_at(&r->options, 0), "p", "one is p");
mu_assert_streq(rz_pvector_at(&r->options, 1), "s", "one is s");
mu_assert_streq(rz_pvector_at(&r->options, 2), "xd", "one is xd");
mu_assert_streq(rz_pvector_at(&r->options, 3), "xe", "one is xe");
rz_line_ns_completion_result_free(r);

strcpy(buf->data, "p @@c:");
Expand All @@ -152,10 +152,10 @@ static bool test_autocmplt_newcommand(void) {
mu_assert_eq(r->start, buf->length, "start should be ok");
mu_assert_eq(r->end, buf->length, "end should be ok");
mu_assert_eq(rz_pvector_len(&r->options), 4, "there are 4 commands available");
mu_assert_streq(rz_pvector_at(&r->options, 0), "xd", "one is xd");
mu_assert_streq(rz_pvector_at(&r->options, 1), "xe", "one is xe");
mu_assert_streq(rz_pvector_at(&r->options, 2), "p", "one is p");
mu_assert_streq(rz_pvector_at(&r->options, 3), "s", "one is s");
mu_assert_streq(rz_pvector_at(&r->options, 0), "p", "one is p");
mu_assert_streq(rz_pvector_at(&r->options, 1), "s", "one is s");
mu_assert_streq(rz_pvector_at(&r->options, 2), "xd", "one is xd");
mu_assert_streq(rz_pvector_at(&r->options, 3), "xe", "one is xe");
rz_line_ns_completion_result_free(r);

rz_core_free(core);
Expand Down
38 changes: 38 additions & 0 deletions test/unit/test_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,43 @@ bool test_default_value(void) {
mu_end;
}

bool test_sort_subcommands(void) {
RzCmdDescHelp z_group_help = { 0 };
z_group_help.summary = "z summary";
z_group_help.sort_subcommands = true;
RzCmd *cmd = rz_cmd_new(false);
RzCmdDesc *root = rz_cmd_get_root(cmd);
RzCmdDesc *z_cd = rz_cmd_desc_group_new(cmd, root, "z", NULL, NULL, &z_group_help);
rz_cmd_desc_argv_new(cmd, z_cd, "zx", x_array_handler, &fake_help);
rz_cmd_desc_argv_new(cmd, z_cd, "zc", x_array_handler, &fake_help);
rz_cmd_desc_argv_new(cmd, z_cd, "za", x_array_handler, &fake_help);

const char *exp1[] = { "za", "zc", "zx" };
void **it_cd;
size_t i = 0;

rz_cmd_desc_children_foreach(z_cd, it_cd) {
RzCmdDesc *child = *(RzCmdDesc **)it_cd;
mu_assert_streq(child->name, exp1[i++], "children of z should be sorted");
}

rz_cmd_batch_start(cmd);
rz_cmd_desc_argv_new(cmd, z_cd, "zz", x_array_handler, &fake_help);
rz_cmd_desc_argv_new(cmd, z_cd, "zb", x_array_handler, &fake_help);
rz_cmd_desc_argv_new(cmd, z_cd, "zi", x_array_handler, &fake_help);
rz_cmd_batch_end(cmd);

const char *exp2[] = { "za", "zb", "zc", "zi", "zx", "zz" };
i = 0;
rz_cmd_desc_children_foreach(z_cd, it_cd) {
RzCmdDesc *child = *(RzCmdDesc **)it_cd;
mu_assert_streq(child->name, exp2[i++], "children of z should be sorted even with batch");
}

rz_cmd_free(cmd);
mu_end;
}

int all_tests() {
mu_run_test(test_parsed_args_noargs);
mu_run_test(test_parsed_args_onearg);
Expand Down Expand Up @@ -943,6 +980,7 @@ int all_tests() {
mu_run_test(test_get_arg);
mu_run_test(test_parent_details);
mu_run_test(test_default_value);
mu_run_test(test_sort_subcommands);
return tests_passed != tests_run;
}

Expand Down

0 comments on commit 890ddb6

Please sign in to comment.