-
-
Notifications
You must be signed in to change notification settings - Fork 577
Implemented a command catalog exporting as a json #6597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
|
@@ -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"); | ||
|
|
@@ -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 { | ||
|
|
@@ -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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NONNULL where appropriate |
||
| 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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here |
||
| 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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NONNULL |
||
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe make a define? |
||
| 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) | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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", | ||
|
|
@@ -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; | ||
|
|
@@ -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++) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This probably can be extracted into a small static function |
||
| 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=")) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This solution looks hackish a bit, tbh, we have custom getopt, we should use it somehow |
||
| 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) { | ||
|
|
@@ -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"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing Doxygen, also add
RZ_NONNULLwhere needed