Automatically group sub-commands ##newshell (#17663)

* Add GROUP RCmdDesc type and fix where the args_str is shown
* Adjust color of `[?]`
* Differentiate between cmd<?> and cmd[?]
<?> when cmd is not valid by itself
[?] when cmd is valid but there are also other sub-commands available
* Fix usage for commands like `w` that are both commands and groups
* Do not automatically switch to detail=2 for leaf commands
* Fix test due to change in `?`/`??` behaviour
This commit is contained in:
Riccardo Schirone 2020-09-24 17:50:52 +02:00 committed by GitHub
parent 46aff1ec53
commit 9b59074787
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 256 additions and 46 deletions

View File

@ -7149,6 +7149,9 @@ R_API void r_core_cmd_init(RCore *core) {
case R_CMD_DESC_TYPE_ARGV:
cd = r_cmd_desc_argv_new (core->rcmd, root, cmds[i].cmd, cmds[i].argv_cb, cmds[i].help);
break;
case R_CMD_DESC_TYPE_GROUP:
cd = r_cmd_desc_group_new (core->rcmd, root, cmds[i].cmd, cmds[i].help);
break;
}
if (cmds[i].descriptor_init) {
cmds[i].descriptor_init (core, cd);

View File

@ -7,6 +7,26 @@
#include <r_cmd.h>
#include <r_util.h>
/*!
* Number of sub-commands to show as options when displaying the help of a
* command. When a command has more options than MAX_CHILDREN_SHOW, `?` is shown
* instead.
*
* Example with MAX_CHILDREN_SHOW=3:
* w -> wa
* -> wb
* -> wc
*
* When doing `?`, you would see:
* w[abc]
*
* If there is also:
* -> wd
* you would see:
* w[?]
*/
#define MAX_CHILDREN_SHOW 7
static const RCmdDescHelp not_defined_help = {
.usage = "Usage not defined",
.summary = "Help summary not defined",
@ -74,7 +94,7 @@ static RCmdDesc *create_cmd_desc(RCmd *cmd, RCmdDesc *parent, RCmdDescType type,
res->n_children = 0;
res->help = help? help: &not_defined_help;
r_pvector_init (&res->children, (RPVectorFree)cmd_desc_free);
if (!ht_pp_insert (cmd->ht_cmds, name, res)) {
if (type != R_CMD_DESC_TYPE_GROUP && !ht_pp_insert (cmd->ht_cmds, name, res)) {
goto err;
}
cmd_desc_set_parent (res, parent);
@ -396,10 +416,55 @@ static size_t strlen0(const char *s) {
return s? strlen (s): 0;
}
static void fill_usage_strbuf(RStrBuf *sb, RCmdDesc *cd, bool use_color) {
static void fill_children_chars(RStrBuf *sb, RCmdDesc *cd) {
if (cd->help->options) {
r_strbuf_append (sb, cd->help->options);
return;
}
RStrBuf csb;
r_strbuf_init (&csb);
void **it;
r_cmd_desc_children_foreach (cd, it) {
RCmdDesc *child = *(RCmdDesc **)it;
if (r_str_startswith (child->name, cd->name) && strlen (child->name) == strlen (cd->name) + 1) {
r_strbuf_appendf (&csb, "%c", child->name[strlen (cd->name)]);
}
}
if (r_strbuf_is_empty (&csb) || r_strbuf_length (&csb) >= MAX_CHILDREN_SHOW) {
r_strbuf_fini (&csb);
r_strbuf_set (&csb, "?");
}
if (!cd->n_children || r_cmd_desc_has_handler (cd)) {
r_strbuf_prepend (&csb, "[");
r_strbuf_append (&csb, "]");
} else {
r_strbuf_prepend (&csb, "<");
r_strbuf_append (&csb, ">");
}
r_strbuf_append (sb, r_strbuf_drain_nofree (&csb));
}
static bool show_children_shortcut(RCmdDesc *cd, RCmdDesc *parent) {
return (cd != parent && (cd->n_children || cd->help->options)) || cd->type == R_CMD_DESC_TYPE_OLDINPUT;
}
static bool show_args(RCmdDesc *cd, RCmdDesc *parent) {
return (cd->n_children == 0 || cd == parent) && cd->help->args_str;
}
static bool show_group_args(RCmdDesc *cd, RCmdDesc *parent) {
return !show_args (cd, parent) && cd->help->group_args_str;
}
static void fill_usage_strbuf(RStrBuf *sb, RCmdDesc *cd, bool use_color, RCmdDesc *parent) {
RCons *cons = r_cons_singleton ();
const char *pal_label_color = use_color? cons->context->pal.label: "",
*pal_args_color = use_color? cons->context->pal.args: "",
*pal_input_color = use_color? cons->context->pal.input: "",
*pal_help_color = use_color? cons->context->pal.help: "",
*pal_reset = use_color? cons->context->pal.reset: "";
@ -407,8 +472,17 @@ static void fill_usage_strbuf(RStrBuf *sb, RCmdDesc *cd, bool use_color) {
if (cd->help->usage) {
r_strbuf_appendf (sb, "%s%s%s", cd->help->usage, pal_args_color, pal_reset);
} else {
const char *cd_args_str = cd->help->args_str? cd->help->args_str: "";
r_strbuf_appendf (sb, "%s%s%s%s", cd->name, pal_args_color, cd_args_str, pal_reset);
r_strbuf_appendf (sb, "%s%s", pal_input_color, cd->name);
if (show_children_shortcut (cd, parent)) {
r_strbuf_append (sb, pal_reset);
fill_children_chars (sb, cd);
}
if (show_args (cd, parent)) {
const char *cd_args_str = cd->help->args_str? cd->help->args_str: "";
r_strbuf_appendf (sb, "%s%s%s", pal_args_color, cd_args_str, pal_reset);
} else if (show_group_args (cd, parent)) {
r_strbuf_appendf (sb, "%s%s%s", pal_args_color, cd->help->group_args_str, pal_reset);
}
}
if (cd->help->group_summary) {
r_strbuf_appendf (sb, " %s# %s%s", pal_help_color, cd->help->group_summary, pal_reset);
@ -418,52 +492,76 @@ static void fill_usage_strbuf(RStrBuf *sb, RCmdDesc *cd, bool use_color) {
r_strbuf_append (sb, "\n");
}
static size_t update_max_len(RCmdDesc *cd, size_t max_len) {
static size_t calc_padding_len(RCmdDesc *cd, RCmdDesc *parent) {
size_t name_len = strlen (cd->name);
size_t args_len = strlen0 (cd->help->args_str);
if (name_len + args_len > max_len) {
return name_len + args_len;
size_t args_len = 0;
size_t children_length = 0;
if (show_children_shortcut (cd, parent)) {
RStrBuf sb;
r_strbuf_init (&sb);
fill_children_chars (&sb, cd);
children_length += r_strbuf_length (&sb);
r_strbuf_fini (&sb);
}
return max_len;
if (show_args (cd, parent)) {
args_len = strlen0 (cd->help->args_str);
} else if (show_group_args (cd, parent)) {
args_len = strlen0 (cd->help->group_args_str);
}
return name_len + args_len + children_length;
}
static void print_child_help(RStrBuf *sb, RCmdDesc *cd, size_t max_len, bool use_color) {
size_t str_len = strlen (cd->name) + strlen0 (cd->help->args_str);
static size_t update_max_len(RCmdDesc *cd, size_t max_len, RCmdDesc *parent) {
size_t val = calc_padding_len (cd, parent);
return val > max_len? val: max_len;
}
static void print_child_help(RStrBuf *sb, RCmdDesc *cd, size_t max_len, bool use_color, RCmdDesc *parent) {
size_t str_len = calc_padding_len (cd, parent);
size_t padding = str_len < max_len? max_len - str_len: 0;
const char *cd_args_str = cd->help->args_str? cd->help->args_str: "";
const char *cd_summary = cd->help->summary? cd->help->summary: "";
RCons *cons = r_cons_singleton ();
const char *pal_args_color = use_color? cons->context->pal.args: "",
*pal_opt_color = use_color? cons->context->pal.reset: "",
*pal_help_color = use_color? cons->context->pal.help: "",
*pal_input_color = use_color? cons->context->pal.input: "",
*pal_reset = use_color? cons->context->pal.reset: "";
r_strbuf_appendf (sb, "| %s%s%s%s %*s%s# %s%s\n", pal_input_color, cd->name,
pal_args_color, cd_args_str, padding, "", pal_help_color, cd_summary, pal_reset);
r_strbuf_appendf (sb, "| %s%s", pal_input_color, cd->name);
if (show_children_shortcut (cd, parent)) {
r_strbuf_append (sb, pal_opt_color);
fill_children_chars (sb, cd);
}
if (show_args (cd, parent)) {
r_strbuf_appendf (sb, "%s%s", pal_args_color, cd->help->args_str);
} else if (show_group_args (cd, parent)) {
r_strbuf_appendf (sb, "%s%s", pal_args_color, cd->help->group_args_str);
}
r_strbuf_appendf (sb, " %*s%s# %s%s\n", padding, "", pal_help_color, cd_summary, pal_reset);
}
static char *inner_get_help(RCmd *cmd, RCmdDesc *cd, bool use_color) {
RStrBuf *sb = r_strbuf_new (NULL);
fill_usage_strbuf (sb, cd, use_color);
fill_usage_strbuf (sb, cd, use_color, cd->parent);
void **it_cd;
size_t max_len = 0;
if (cd->d.argv_data.cb) {
max_len = update_max_len (cd, max_len);
max_len = update_max_len (cd, max_len, cd);
}
r_cmd_desc_children_foreach (cd, it_cd) {
RCmdDesc *child = *(RCmdDesc **)it_cd;
max_len = update_max_len (child, max_len);
max_len = update_max_len (child, max_len, cd);
}
if (cd->d.argv_data.cb) {
print_child_help (sb, cd, max_len, use_color);
print_child_help (sb, cd, max_len, use_color, cd);
}
r_cmd_desc_children_foreach (cd, it_cd) {
RCmdDesc *child = *(RCmdDesc **)it_cd;
print_child_help (sb, child, max_len, use_color);
print_child_help (sb, child, max_len, use_color, cd);
}
return r_strbuf_drain (sb);
}
@ -477,7 +575,7 @@ static char *argv_get_help(RCmd *cmd, RCmdDesc *cd, RCmdParsedArgs *a, size_t de
RStrBuf *sb = r_strbuf_new (NULL);
fill_usage_strbuf (sb, cd, use_color);
fill_usage_strbuf (sb, cd, use_color, cd);
switch (detail) {
case 1:
@ -545,11 +643,6 @@ R_API char *r_cmd_get_help(RCmd *cmd, RCmdParsedArgs *args, bool use_color) {
}
return inner_get_help (cmd, cd, use_color);
}
if (detail == 1 && r_pvector_empty (&cd->children)) {
// if the current node does not have children, just
// print the full help
detail = 2;
}
return argv_get_help (cmd, cd, args, detail, use_color);
case R_CMD_DESC_TYPE_OLDINPUT:
return oldinput_get_help (cmd, cd, args);
@ -1147,6 +1240,11 @@ R_API RCmdDesc *r_cmd_desc_argv_new(RCmd *cmd, RCmdDesc *parent, const char *nam
return res;
}
R_API RCmdDesc *r_cmd_desc_group_new(RCmd *cmd, RCmdDesc *parent, const char *name, const RCmdDescHelp *help) {
r_return_val_if_fail (cmd && parent && name, NULL);
return create_cmd_desc (cmd, parent, R_CMD_DESC_TYPE_GROUP, name, help);
}
R_API RCmdDesc *r_cmd_desc_oldinput_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdCb cb, const RCmdDescHelp *help) {
r_return_val_if_fail (cmd && parent && name && cb, NULL);
RCmdDesc *res = create_cmd_desc (cmd, parent, R_CMD_DESC_TYPE_OLDINPUT, name, help);
@ -1162,6 +1260,19 @@ R_API RCmdDesc *r_cmd_desc_parent(RCmdDesc *cd) {
return cd->parent;
}
R_API bool r_cmd_desc_has_handler(RCmdDesc *cd) {
r_return_val_if_fail (cd, false);
switch (cd->type) {
case R_CMD_DESC_TYPE_ARGV:
return cd->d.argv_data.cb;
case R_CMD_DESC_TYPE_OLDINPUT:
return cd->d.oldinput_data.cb;
case R_CMD_DESC_TYPE_GROUP:
return false;
}
return false;
}
R_API bool r_cmd_desc_remove(RCmd *cmd, RCmdDesc *cd) {
r_return_val_if_fail (cmd && cd, false);
if (cd->parent) {

View File

@ -213,38 +213,100 @@ const RCmdDescExample w_incdec_help_examples[] = {
{ 0 },
};
const RCmdDescHelp w_incdec_help = {
.summary = "increment/decrement byte,word..",
.group_args_str = " [n]",
.options = "<1248><+->",
};
const RCmdDescHelp w1_incdec_help = {
.summary = "Increment/decrement a byte",
.usage = "w1[+-] [n]",
.options = "<+->",
.args_str = " [n]",
.description = "Increment/decrement a byte at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w1_inc_help = {
.summary = "Increment a byte",
.args_str = " [n]",
.description = "Increment a byte at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w1_dec_help = {
.summary = "Decrement a byte",
.args_str = " [n]",
.description = "Decrement a byte at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w2_incdec_help = {
.summary = "Increment/decrement a word",
.usage = "w2[+-] [n]",
.options = "<+->",
.args_str = " [n]",
.description = "Increment/decrement a word at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w2_inc_help = {
.summary = "Increment a word",
.args_str = " [n]",
.description = "Increment a word at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w2_dec_help = {
.summary = "Decrement a word",
.args_str = " [n]",
.description = "Decrement a word at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w4_incdec_help = {
.summary = "Increment/decrement a dword",
.usage = "w4[+-] [n]",
.options = "<+->",
.args_str = " [n]",
.description = "Increment/decrement a dword at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w4_inc_help = {
.summary = "Increment a dword",
.args_str = " [n]",
.description = "Increment a dword at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w4_dec_help = {
.summary = "Decrement a dword",
.args_str = " [n]",
.description = "Decrement a dword at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w8_incdec_help = {
.summary = "Increment/decrement a qword",
.usage = "w8[+-] [n]",
.options = "<+->",
.args_str = " [n]",
.description = "Increment/decrement a qword at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w8_inc_help = {
.summary = "Increment a qword",
.args_str = " [n]",
.description = "Increment a qword at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
const RCmdDescHelp w8_dec_help = {
.summary = "Decrement a qword",
.args_str = " [n]",
.description = "Decrement a qword at the current offset by 1 or n, if specified",
.examples = w_incdec_help_examples,
};
// wB helps
const RCmdDescExample wB_help_examples[] = {
@ -255,6 +317,7 @@ const RCmdDescExample wB_help_examples[] = {
const RCmdDescHelp wB_help = {
.summary = "Set bits with given value",
.args_str = " [value]",
.group_args_str = " [value]",
.group_summary = "Set or unset bits with given value",
.description = "Set the bits that are set in the value passed as arguments. 0 bits in the value argument are ignored, while the others are set at the current offset",
.examples = wB_help_examples,
@ -274,9 +337,10 @@ const RCmdDescExample wv_help_examples[] = {
};
const RCmdDescHelp wv_help = {
.usage = "wv[size] [value]",
.summary = "Write value as 4 - bytes / 8 - bytes based on value",
.options = "[size]",
.args_str = " [value]",
.group_args_str = " [value]",
.description = "Write the number passed as argument at the current offset as a 4 - bytes value or 8 - bytes value if the input is bigger than UT32_MAX, respecting the cfg.bigendian variable",
.group_summary = "Write value of given size",
.examples = wv_help_examples,

View File

@ -58,10 +58,19 @@ extern const RCmdDescHelp w0_help;
// w[1248][+-] helps
extern const RCmdDescHelp w_incdec_help;
extern const RCmdDescHelp w1_incdec_help;
extern const RCmdDescHelp w1_inc_help;
extern const RCmdDescHelp w1_dec_help;
extern const RCmdDescHelp w2_incdec_help;
extern const RCmdDescHelp w2_inc_help;
extern const RCmdDescHelp w2_dec_help;
extern const RCmdDescHelp w4_incdec_help;
extern const RCmdDescHelp w4_inc_help;
extern const RCmdDescHelp w4_dec_help;
extern const RCmdDescHelp w8_incdec_help;
extern const RCmdDescHelp w8_inc_help;
extern const RCmdDescHelp w8_dec_help;
// wB helps

View File

@ -2086,19 +2086,19 @@ static void cmd_write_init(RCore *core, RCmdDesc *parent) {
DEFINE_CMD_ARGV_DESC (core, w0, parent);
DEFINE_CMD_ARGV_DESC_GROUP (core, w[1248][+-], w_incdec, parent);
DEFINE_CMD_ARGV_DESC_GROUP (core, w, w_incdec, parent);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w1, w1, w_incdec_cd, NULL, &w1_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w1+, w1_inc, w1_cd, w1_incdec_handler, &w1_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w1-, w1_dec, w1_cd, w1_incdec_handler, &w1_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w1+, w1_inc, w1_cd, w1_incdec_handler, &w1_inc_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w1-, w1_dec, w1_cd, w1_incdec_handler, &w1_dec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w2, w2, w_incdec_cd, NULL, &w2_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w2+, w2_inc, w2_cd, w2_incdec_handler, &w2_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w2-, w2_dec, w2_cd, w2_incdec_handler, &w2_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w2+, w2_inc, w2_cd, w2_incdec_handler, &w2_inc_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w2-, w2_dec, w2_cd, w2_incdec_handler, &w2_dec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w4, w4, w_incdec_cd, NULL, &w4_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w4+, w4_inc, w4_cd, w4_incdec_handler, &w4_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w4-, w4_dec, w4_cd, w4_incdec_handler, &w4_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w4+, w4_inc, w4_cd, w4_incdec_handler, &w4_inc_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w4-, w4_dec, w4_cd, w4_incdec_handler, &w4_dec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w8, w8, w_incdec_cd, NULL, &w8_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w8+, w8_inc, w8_cd, w8_incdec_handler, &w8_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w8-, w8_dec, w8_cd, w8_incdec_handler, &w8_incdec_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w8+, w8_inc, w8_cd, w8_incdec_handler, &w8_inc_help);
DEFINE_CMD_ARGV_DESC_DETAIL (core, w8-, w8_dec, w8_cd, w8_incdec_handler, &w8_dec_help);
DEFINE_CMD_OLDINPUT_DESC (core, w6, parent);
DEFINE_CMD_OLDINPUT_DESC (core, wh, parent);

View File

@ -114,6 +114,15 @@ typedef struct r_cmd_desc_help_t {
* Optional.
*/
const char *usage;
/**
* String to use as sub-commands suggestions instead of the
* auto-generated one (e.g. [abcd] or [?] that you can see near command
* names when doing `w?`). If not provided, the options will be
* auto-generated.
*
* Optional.
*/
const char *options;
/**
* When a command is used both as a parent command and as a subcommand
* (e.g. `w` is both the parent of `wv`, `ws`, etc. and it's also the
@ -123,6 +132,16 @@ typedef struct r_cmd_desc_help_t {
* Optional.
*/
const char *group_summary;
/**
* When a command is used both as a parent command and as a subcommand
* (e.g. `w` is both the parent of `wv`, `ws`, etc. and it's also the
* command `w`), this is the argument string used for the parent level,
* while args_str becomes the text used for the subcommand.
*
* Optional.
* TODO: explain how to differentiate between required and optional arguments
*/
const char *group_args_str;
/**
* List of examples used to better explain how to use the command. This
* is shown together with the long description.
@ -137,6 +156,10 @@ typedef enum {
R_CMD_DESC_TYPE_OLDINPUT = 0,
// for handlers that accept argc/argv
R_CMD_DESC_TYPE_ARGV,
// for cmd descriptors that are just used to group together related
// sub-commands. Do not use this if the command can be used by itself or
// if it's necessary to show its help.
R_CMD_DESC_TYPE_GROUP,
} RCmdDescType;
typedef struct r_cmd_desc_t {
@ -198,7 +221,8 @@ typedef struct r_core_plugin_t {
#define DEFINE_CMD_ARGV_DESC_SPECIAL(core, name, c_name, parent) \
DEFINE_CMD_ARGV_DESC_DETAIL (core, name, c_name, parent, c_name##_handler, &c_name##_help)
#define DEFINE_CMD_ARGV_DESC_GROUP(core, name, c_name, parent) \
DEFINE_CMD_ARGV_DESC_DETAIL (core, name, c_name, parent, NULL, NULL)
RCmdDesc *c_name##_cd = r_cmd_desc_group_new (core->rcmd, parent, #name, &c_name##_help); \
r_warn_if_fail (c_name##_cd)
#define DEFINE_CMD_ARGV_DESC(core, name, parent) \
DEFINE_CMD_ARGV_DESC_SPECIAL (core, name, name, parent)
#define DEFINE_CMD_OLDINPUT_DESC(core, name, parent) \
@ -248,8 +272,10 @@ static inline int r_cmd_status2int(RCmdStatus s) {
/* RCmdDescriptor */
R_API RCmdDesc *r_cmd_desc_argv_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdArgvCb cb, const RCmdDescHelp *help);
R_API RCmdDesc *r_cmd_desc_group_new(RCmd *cmd, RCmdDesc *parent, const char *name, const RCmdDescHelp *help);
R_API RCmdDesc *r_cmd_desc_oldinput_new(RCmd *cmd, RCmdDesc *parent, const char *name, RCmdCb cb, const RCmdDescHelp *help);
R_API RCmdDesc *r_cmd_desc_parent(RCmdDesc *cd);
R_API bool r_cmd_desc_has_handler(RCmdDesc *cd);
R_API bool r_cmd_desc_remove(RCmd *cmd, RCmdDesc *cd);
#define r_cmd_desc_children_foreach(root, it_cd) r_pvector_foreach (&root->children, it_cd)

View File

@ -288,8 +288,8 @@ bool test_cmd_help(void) {
r_cmd_desc_oldinput_new (cmd, p_cd, "px", px_handler, &px_help);
const char *p_help_exp = "Usage: p-usage # p summary\n"
"| pd <num> # pd summary\n"
"| px <verylongarg_str_num> # px summary\n";
"| pd <num> # pd summary\n"
"| px[?] <verylongarg_str_num> # px summary\n";
RCmdParsedArgs *a = r_cmd_parsed_args_newcmd ("p?");
char *h = r_cmd_get_help (cmd, a, false);
mu_assert_notnull (h, "help is not null");
@ -297,10 +297,7 @@ bool test_cmd_help(void) {
free (h);
r_cmd_parsed_args_free (a);
const char *pd_help_exp = "Usage: pd <num> # pd summary\n"
"\npd long description\n"
"\nExamples:\n"
"| pd 10 # print 10 disassembled instructions\n";
const char *pd_help_exp = "Usage: pd <num> # pd summary\n";
a = r_cmd_parsed_args_newcmd ("pd?");
h = r_cmd_get_help (cmd, a, false);
mu_assert_notnull (h, "help is not null");