Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion librz/core/cmd/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1447,7 +1447,10 @@ DEFINE_HANDLE_TS_FCN_AND_SYMBOL(redirect_stmt) {

DEFINE_HANDLE_TS_FCN_AND_SYMBOL(help_stmt) {
size_t node_str_len = strlen(node_string);
if (node_str_len >= 2 && !strcmp(node_string + node_str_len - 2, "?*")) {
if (RZ_STR_EQ(node_string, "?+j")) {
const char *argv[1] = { node_string };
return rz_cmd_catalog_json_handler(state->core, 1, argv);
} else if (node_str_len >= 2 && !strcmp(node_string + node_str_len - 2, "?*")) {
node_string[node_str_len - 2] = 0;
const char *argv[2] = { NULL, node_string };
int argc = node_str_len > 2 ? 2 : 1;
Expand Down
161 changes: 142 additions & 19 deletions librz/core/cmd/cmd_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,18 @@ static const RzCmdDescHelp root_help = {
static const struct argv_modes_t {
const char *suffix;
const char *summary_suffix;
const char *name;
RzOutputMode mode;
} argv_modes[] = {
{ "", "", RZ_OUTPUT_MODE_STANDARD },
{ "j", " (JSON mode)", RZ_OUTPUT_MODE_JSON },
{ "q", " (quiet mode)", RZ_OUTPUT_MODE_QUIET },
{ "Q", " (quietest mode)", RZ_OUTPUT_MODE_QUIETEST },
{ "k", " (sdb mode)", RZ_OUTPUT_MODE_SDB },
{ "l", " (verbose mode)", RZ_OUTPUT_MODE_LONG },
{ "J", " (verbose JSON mode)", RZ_OUTPUT_MODE_LONG_JSON },
{ "t", " (table mode)", RZ_OUTPUT_MODE_TABLE },
{ "g", " (graph mode)", RZ_OUTPUT_MODE_GRAPH },
{ "", "", "standard", RZ_OUTPUT_MODE_STANDARD },
{ "j", " (JSON mode)", "json", RZ_OUTPUT_MODE_JSON },
{ "q", " (quiet mode)", "quiet", RZ_OUTPUT_MODE_QUIET },
{ "Q", " (quietest mode)", "quietest", RZ_OUTPUT_MODE_QUIETEST },
{ "k", " (sdb mode)", "sdb", RZ_OUTPUT_MODE_SDB },
{ "l", " (verbose mode)", "long", RZ_OUTPUT_MODE_LONG },
{ "J", " (verbose JSON mode)", "long_json", RZ_OUTPUT_MODE_LONG_JSON },
{ "t", " (table mode)", "table", RZ_OUTPUT_MODE_TABLE },
{ "g", " (graph mode)", "graph", RZ_OUTPUT_MODE_GRAPH },
};

RZ_IPI int rz_output_mode_to_char(RzOutputMode mode) {
Expand Down Expand Up @@ -1501,16 +1502,11 @@ static void fill_details_json(const RzCmdDescDetail *details, PJ *j) {
*
* \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 = "unknown";
switch (cd->type) {
static const char *cmd_desc_type_name(RzCmdDescType type) {
switch (type) {
#define CASE_CDTYPE(x, y) \
case (x): \
type = (y); \
break
return (y)
CASE_CDTYPE(RZ_CMD_DESC_TYPE_ARGV, "argv");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_GROUP, "group");
CASE_CDTYPE(RZ_CMD_DESC_TYPE_INNER, "inner");
Expand All @@ -1519,9 +1515,15 @@ RZ_API bool rz_cmd_get_help_json(RzCmd *cmd, const RzCmdDesc *cd, PJ *j) {
CASE_CDTYPE(RZ_CMD_DESC_TYPE_ARGV_STATE, "argv_state");
#undef CASE_CDTYPE
default:
break;
return "unknown";
}
pj_ks(j, "type", type);
}

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);
pj_ks(j, "type", cmd_desc_type_name(cd->type));
if (cd->help->args_str) {
pj_ks(j, "args_str", cd->help->args_str);
} else {
Expand All @@ -1539,6 +1541,127 @@ RZ_API bool rz_cmd_get_help_json(RzCmd *cmd, const RzCmdDesc *cd, PJ *j) {
return true;
}

static void cmd_desc_foreach_tree(RzCmd *cmd, RzCmdDesc *cd, RzCmdDescVisitCb pre, RzCmdDescVisitCb post, void *user) {
if (!cd) {
return;
}
if (pre) {
pre(cmd, cd, user);
}

void **it_cd;
rz_cmd_desc_children_foreach(cd, it_cd) {
RzCmdDesc *child = *it_cd;
cmd_desc_foreach_tree(cmd, child, pre, post, user);
}

if (post) {
post(cmd, cd, user);
}
}

/**
* \brief Visit every real command descriptor in a subtree.
*
* The pre callback is called before visiting children. The post callback is
* called after all children have been visited. Traversal starts at the root descriptor.
*/
RZ_API void rz_cmd_desc_foreach_tree(RzCmd *cmd, RzCmdDescVisitCb pre, RzCmdDescVisitCb post, void *user) {
rz_return_if_fail(cmd);
cmd_desc_foreach_tree(cmd, rz_cmd_get_root(cmd), pre, post, user);
}

/**
* \brief Visit every real command descriptor in a subtree.
*
* The pre callback is called before visiting children. The post callback is
* called after all children have been visited. Traversal starts at \p begin.
*/
RZ_API void rz_cmd_desc_foreach_tree_from(RzCmd *cmd, RzCmdDesc *begin, RzCmdDescVisitCb pre, RzCmdDescVisitCb post, void *user) {
rz_return_if_fail(cmd && begin);
cmd_desc_foreach_tree(cmd, begin, pre, post, user);
}

static void cmd_tree_json_modes(PJ *j, const RzCmdDesc *cd) {
RzCmdDesc *exec_cd = rz_cmd_desc_get_exec((RzCmdDesc *)cd);
pj_ka(j, "modes");
if (exec_cd) {
int modes = 0;
switch (exec_cd->type) {
case RZ_CMD_DESC_TYPE_ARGV_MODES:
modes = exec_cd->d.argv_modes_data.modes;
break;
case RZ_CMD_DESC_TYPE_ARGV_STATE:
modes = exec_cd->d.argv_state_data.modes;
break;
default:
break;
}
for (size_t i = 0; i < RZ_ARRAY_SIZE(argv_modes); i++) {
if (modes & argv_modes[i].mode) {
pj_s(j, argv_modes[i].name);
}
}
}
pj_end(j);
}

typedef struct cmd_tree_json_t {
PJ *j;
RzStrBuf *args;
} CmdTreeJson;

static void cmd_tree_json_pre(RzCmd *cmd, const RzCmdDesc *cd, void *user) {
CmdTreeJson *ctx = user;
PJ *j = ctx->j;
pj_o(j);
pj_ks(j, "cmd", cd->name);
pj_ks(j, "type", cmd_desc_type_name(cd->type));
pj_ks(j, "summary", rz_str_get(cd->help->summary));
pj_ks(j, "description", rz_str_get(cd->help->description));
if (cd->help->args_str) {
pj_ks(j, "args_str", cd->help->args_str);
} else {
rz_strbuf_set(ctx->args, "");
fill_args(ctx->args, cd);
pj_ks(j, "args_str", rz_strbuf_get(ctx->args));
}
fill_args_json(cmd, cd, j);
fill_details_json(cd->help->details, j);
pj_kb(j, "executable", rz_cmd_desc_has_handler(cd));
pj_ki(j, "n_children", cd->n_children);
cmd_tree_json_modes(j, cd);
pj_ka(j, "children");
}

static void cmd_tree_json_post(RZ_UNUSED RzCmd *cmd, RZ_UNUSED const RzCmdDesc *cd, void *user) {
CmdTreeJson *ctx = user;
PJ *j = ctx->j;
pj_end(j);
pj_end(j);
}

/**
* \brief Generates a recursive JSON representation of the real command descriptor tree.
*/
RZ_API bool rz_cmd_get_tree_json(RzCmd *cmd, PJ *j) {
rz_return_val_if_fail(cmd && j, false);
RzStrBuf args;
rz_strbuf_init(&args);
CmdTreeJson ctx = {
.j = j,
.args = &args,
};
pj_o(j);
pj_ki(j, "version", 1);
pj_ks(j, "generated_from", "runtime");
pj_k(j, "root");
rz_cmd_desc_foreach_tree(cmd, cmd_tree_json_pre, cmd_tree_json_post, &ctx);
pj_end(j);
rz_strbuf_fini(&args);
return true;
}

/**
* \brief Generates a text output of the given help message description (summary format)
*
Expand Down
12 changes: 12 additions & 0 deletions librz/core/cmd/cmd_help.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ static bool help_search_cmd_desc_details(RzCmd *cmd, const RzCmdDesc *cd, void *
goto beach;
}

// "?+j"
RZ_IPI RzCmdStatus rz_cmd_catalog_json_handler(RzCore *core, int argc, const char **argv) {
PJ *pj = pj_new();
if (!pj || !rz_cmd_get_tree_json(core->rcmd, pj)) {
pj_free(pj);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(pj_string(pj));
pj_free(pj);
return RZ_CMD_STATUS_OK;
}

// "?*"
RZ_IPI RzCmdStatus rz_cmd_help_search_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
RzCmdStatus status = RZ_CMD_STATUS_OK;
Expand Down
11 changes: 11 additions & 0 deletions librz/core/cmd_descs/cmd_descs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3214,6 +3214,14 @@ static const RzCmdDescHelp cmd_help_search_interactive_everything_help = {
.args = cmd_help_search_interactive_everything_args,
};

static const RzCmdDescArg cmd_catalog_json_args[] = {
{ 0 },
};
static const RzCmdDescHelp cmd_catalog_json_help = {
.summary = "Export full command catalog as JSON",
.args = cmd_catalog_json_args,
};

static const RzCmdDescHelp cmd_math_help = {
.summary = "Math commands",
};
Expand Down Expand Up @@ -22854,6 +22862,9 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) {
RzCmdDesc *cmd_help_search_interactive_everything_cd = rz_cmd_desc_argv_new(core->rcmd, question__star__cd, "?***", rz_cmd_help_search_interactive_everything_handler, &cmd_help_search_interactive_everything_help);
rz_warn_if_fail(cmd_help_search_interactive_everything_cd);

RzCmdDesc *cmd_catalog_json_cd = rz_cmd_desc_argv_new(core->rcmd, root_cd, "?+j", rz_cmd_catalog_json_handler, &cmd_catalog_json_help);
rz_warn_if_fail(cmd_catalog_json_cd);

RzCmdDesc *cmd_math_cd = rz_cmd_desc_group_state_new(core->rcmd, root_cd, "%", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_calculate_expr_handler, &calculate_expr_help, &cmd_math_help);
rz_warn_if_fail(cmd_math_cd);
RzCmdDesc *list_rizin_vars_cd = rz_cmd_desc_argv_new(core->rcmd, cmd_math_cd, "%$", rz_list_rizin_vars_handler, &list_rizin_vars_help);
Expand Down
2 changes: 2 additions & 0 deletions librz/core/cmd_descs/cmd_descs.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ RZ_IPI RzCmdStatus rz_cmd_help_search_interactive_handler(RzCore *core, int argc
RZ_IPI RzCmdStatus rz_cmd_help_search_interactive_settings_handler(RzCore *core, int argc, const char **argv);
// "?***"
RZ_IPI RzCmdStatus rz_cmd_help_search_interactive_everything_handler(RzCore *core, int argc, const char **argv);
// "?+j"
RZ_IPI RzCmdStatus rz_cmd_catalog_json_handler(RzCore *core, int argc, const char **argv);
// "%"
RZ_IPI RzCmdStatus rz_calculate_expr_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
// "%$"
Expand Down
4 changes: 4 additions & 0 deletions librz/core/cmd_descs/cmd_descs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ commands:
summary: Search help commands
type: RZ_CMD_DESC_TYPE_GROUP
subcommands: cmd_help
- name: "?+j"
cname: cmd_catalog_json
summary: Export full command catalog as JSON
args: []
- name: "%"
cname: cmd_math
summary: Math commands
Expand Down
4 changes: 4 additions & 0 deletions librz/include/rz_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ typedef struct rz_cmd_descriptor_t {
} RzCmdDescriptor;

typedef bool (*RzCmdForeachNameCb)(RzCmd *cmd, const RzCmdDesc *desc, void *user);
typedef void (*RzCmdDescVisitCb)(RzCmd *cmd, const RzCmdDesc *desc, void *user);
typedef bool (*RzCmdForeachMacroCb)(RzCmd *cmd, const RzCmdMacro *macro, void *user);

#ifdef RZ_API
Expand All @@ -530,6 +531,7 @@ RZ_API RzCmdDesc *rz_cmd_get_desc(RzCmd *cmd, const char *cmd_identifier);
RZ_API RzCmdDesc *rz_cmd_get_desc_best(RzCmd *cmd, const char *cmd_identifier);
RZ_API RZ_OWN char *rz_cmd_get_help(RZ_BORROW RzCmd *cmd, RZ_BORROW RzCmdParsedArgs *args, bool use_color, int gutter_size);
RZ_API bool rz_cmd_get_help_json(RzCmd *cmd, const RzCmdDesc *cd, PJ *j);
RZ_API bool rz_cmd_get_tree_json(RzCmd *cmd, PJ *j);
RZ_API bool rz_cmd_get_help_strbuf(RzCmd *cmd, const RzCmdDesc *cd, bool use_color, RzStrBuf *sb, int gutter_size);

static inline RzCmdStatus rz_cmd_int2status(int v) {
Expand Down Expand Up @@ -571,6 +573,8 @@ RZ_API RzCmdDesc *rz_cmd_desc_get_exec(RzCmdDesc *cd);
RZ_API bool rz_cmd_desc_set_default_mode(RzCmdDesc *cd, RzOutputMode mode);
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_desc_foreach_tree(RzCmd *cmd, RzCmdDescVisitCb pre, RzCmdDescVisitCb post, void *user);
RZ_API void rz_cmd_desc_foreach_tree_from(RzCmd *cmd, RzCmdDesc *begin, RzCmdDescVisitCb pre, RzCmdDescVisitCb post, 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(const RzCmdDesc *cd, size_t i);

Expand Down
55 changes: 55 additions & 0 deletions librz/main/rizin.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ static int main_help(RZ_BORROW RZ_NONNULL RzCore *core, int line) {
"-B", "baddr", "Set base address for PIE binaries",
"-c", "'cmd..'", "Execute rizin command",
"-C", "", "File is host:port (alias for -cR+http://%%s/cmd/)",
"--cmd-catalog", "[json]", "Print command tree catalog and exit",
"-d", "", "Debug the executable 'file' or running process 'pid'",
"-D", "backend", "Enable debug mode (e cfg.debug=true)",
"-e", "k=v", "Evaluate config var",
Expand Down Expand Up @@ -426,6 +427,7 @@ RZ_API int rz_main_rizin(int argc, const char **argv) {
ut64 baddr = UT64_MAX;
ut64 seek = UT64_MAX;
bool do_list_io_plugins = false;
bool do_print_cmd_tree_json = false;
char *file = NULL;
char *pfile = NULL;
const char *asmarch = NULL;
Expand Down Expand Up @@ -513,6 +515,38 @@ RZ_API int rz_main_rizin(int argc, const char **argv) {
char *debugbackend = rz_str_dup("native");

RzGetopt opt;
for (int i = 1; i < argc; i++) {
bool remove_arg = false;
bool remove_next_arg = false;
if (!strcmp(argv[i], "--cmd-catalog")) {
do_print_cmd_tree_json = true;
remove_arg = true;
if (i + 1 < argc && argv[i + 1][0] != '-') {
if (strcmp(argv[i + 1], "json")) {
RZ_LOG_ERROR("Unsupported command catalog format '%s'\n", argv[i + 1]);
ret = 1;
goto beach;
}
remove_next_arg = true;
}
} else if (!strcmp(argv[i], "--cmd-catalog=json")) {
do_print_cmd_tree_json = true;
remove_arg = true;
} else if (rz_str_startswith(argv[i], "--cmd-catalog=")) {
RZ_LOG_ERROR("Unsupported command catalog format '%s'\n", argv[i] + strlen("--cmd-catalog="));
ret = 1;
goto beach;
}
if (remove_arg) {
int n_remove = remove_next_arg ? 2 : 1;
for (int j = i; j + n_remove < argc; j++) {
argv[j] = argv[j + n_remove];
}
argc -= n_remove;
i--;
}
}

rz_getopt_init(&opt, argc, argv, "=012AMCwxfF:H:hm:E:e:nk:NdqQs:p:b:B:a:Lui:I:l:R:r:c:D:vVSTzuXt");
while (argc >= 2 && (c = rz_getopt_next(&opt)) != -1) {
switch (c) {
Expand Down Expand Up @@ -940,6 +974,27 @@ RZ_API int rz_main_rizin(int argc, const char **argv) {
rz_config_set(r->config, "scr.utf8", "false");
}

if (do_print_cmd_tree_json) {
PJ *pj = pj_new();
if (!pj || !rz_cmd_get_tree_json(r->rcmd, pj)) {
pj_free(pj);
RZ_LOG_ERROR("Cannot build command tree JSON\n");
LISTS_FREE();
RZ_FREE(pfile);
RZ_FREE(debugbackend);
ret = 1;
goto beach;
}
rz_cons_printf("%s\n", pj_string(pj));
pj_free(pj);
rz_cons_flush();
LISTS_FREE();
RZ_FREE(pfile);
RZ_FREE(debugbackend);
ret = 0;
goto beach;
}

if (pfile && rz_file_is_directory(pfile)) {
if (debug) {
RZ_LOG_ERROR("Error: Cannot debug directories, yet.\n");
Expand Down
7 changes: 6 additions & 1 deletion subprojects/rizin-shell-parser/src/scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ static bool is_recursive_help_json(const int32_t trd_last_ch, const int32_t snd_
return trd_last_ch == '?' && snd_last_ch == '*' && last_ch == 'j';
}

static bool is_cmd_catalog_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) {
if (!valid_symbols[FILE_DESCRIPTOR]) {
return false;
Expand Down Expand Up @@ -171,7 +175,8 @@ bool tree_sitter_rzcmd_external_scanner_scan(void *payload, TSLexer *lexer, cons
}
if ((res[i_res - 1] == '?') ||
(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]))) {
(i_res >= 3 && is_recursive_help_json(res[i_res - 3], res[i_res - 2], res[i_res - 1])) ||
(i_res >= 3 && is_cmd_catalog_json(res[i_res - 3], res[i_res - 2], res[i_res - 1]))) {
if (i_res == 1) {
return false;
}
Expand Down
Loading
Loading