Skip to content

Commit

Permalink
Optimize alias iteration to linear time and improve alias help
Browse files Browse the repository at this point in the history
  • Loading branch information
Lazula authored and trufae committed Jun 3, 2022
1 parent 67af169 commit c496680
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 66 deletions.
27 changes: 14 additions & 13 deletions libr/core/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,21 @@ static void cmd_debug_reg(RCore *core, const char *str);
#include "cmd_help.c"

static const char *help_msg_dollar[] = {
"Usage:", "$alias[=cmd] [args...]", "Alias commands and strings (See ?$? for help on $variables)",
"Usage:", "$alias[=cmd] [args...]", "Alias commands and data (See ?$? for help on $variables)",
"$", "", "list all defined aliases",
"$*", "", "list all defined aliases and their respective values, unprintable characters escaped",
"$**", "", "same as above, but if an alias has unprintable characters, b64 encode it",
"$*", "", "list all defined aliases and their values, with unprintable characters escaped",
"$**", "", "same as above, but if an alias contains unprintable characters, b64 encode it",
"$", "foo:=123", "alias for 'f foo=123'",
"$", "foo-=4", "alias for 'f foo-=4'",
"$", "foo+=4", "alias for 'f foo+=4'",
"$", "foo", "alias for 's foo' (note that command aliases can override flag resolution)",
"$", "dis=base64:AAA=", "alias $dis to the raw byte output from decoding this base64 string",
"$", "dis=$hello world", "alias $dis to the string after '$' (accepts double-backslash and hex escaping)",
"$", "dis=-", "edit $dis in cfg.editor (accepts backslash and hex escaping)",
"$", "dis=base64:AAA=", "alias $dis to the raw bytes from decoding this base64 string",
"$", "dis=$hello world", "alias $dis to the string after '$'",
"$", "dis=$hello\\\\nworld\\\\0a", "string aliases accept double-backslash and hex escaping",
"$", "dis=-", "edit $dis in cfg.editor (use single-backslashes for escaping)",
"$", "dis=af", "alias $dis to the af command",
"$", "dis=af;pdf", "alias $dis to the af command, then run pdf",
"$", "test=#!pipe node /tmp/test.js", "create command - rlangpipe script",
"\"$", "dis=af;pdf\"", "alias $dis to run af, then pdf. you must quote the whole command.",
"$", "test=. /tmp/test.js", "create command - rlangpipe script",
"$", "dis=", "undefine alias",
"$", "dis", "execute a defined command alias, or print a data alias with unprintable characters escaped",
"$", "dis?", "show commands aliased by $dis",
Expand Down Expand Up @@ -804,13 +805,13 @@ static int cmd_alias(void *data, const char *input) {
bool use_b64 = (buf[1] == '*');
ht_pp_foreach (core->rcmd->aliases, print_aliases, &use_b64);
} else if (!*buf) {
RList *keys = r_cmd_alias_keys (core->rcmd);
const char **keys = r_cmd_alias_keys (core->rcmd);
if (keys) {
RListIter *it;
r_list_foreach_iter (keys, it) {
r_cons_printf ("$%s\n", (char *)it->data);
int i;
for (i = 0; i < core->rcmd->aliases->count; i++) {
r_cons_printf ("$%s\n", keys[i]);
}
r_list_free (keys);
free (keys);
}
} else {
/* Execute or evaluate alias */
Expand Down
25 changes: 18 additions & 7 deletions libr/core/cmd_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,20 +254,31 @@ R_API RCmdDesc *r_cmd_get_desc(RCmd *cmd, const char *cmd_identifier) {
return res;
}

// This struct exists to store the index during hashtable foreach.
typedef struct {
const char **keys;
size_t current_key;
} AliasKeylist;

static bool get_keys(void *keylist_in, const void *k, const void *v) {
RList *keylist = (RList *) keylist_in;
return r_list_append (keylist, (char *) k);
AliasKeylist *keylist = keylist_in;
keylist->keys[keylist->current_key++] = (const char *)k;
return true;
}

R_API RList *r_cmd_alias_keys(RCmd *cmd) {
RList *keylist = r_list_new ();
if (!keylist) {
R_API const char **r_cmd_alias_keys(RCmd *cmd) {
AliasKeylist keylist;

keylist.keys = R_NEWS (const char *, cmd->aliases->count);
if (!keylist.keys) {
return NULL;
}

ht_pp_foreach (cmd->aliases, get_keys, keylist);
keylist.current_key = 0;
ht_pp_foreach (cmd->aliases, get_keys, &keylist);

return keylist;
// We don't need to return a count - it's already in cmd->aliases.
return keylist.keys;
}

R_API void r_cmd_alias_free(RCmd *cmd) {
Expand Down
123 changes: 78 additions & 45 deletions libr/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1215,56 +1215,88 @@ static void autocomplete_ms_path(RLineCompletion *completion, RCore *core, const
free (basename);
}

static void autocomplete_alias(RLineCompletion *completion, RCmd *cmd, const char *needle, bool is_data) {
typedef struct {
const char *needle;
int needle_len;
bool must_be_data;

const char **valid_completions;
const RCmdAliasVal **valid_completion_vals;
int num_completions;
} AliasAutocompletions;

static bool check_alias_completion(void *in, const void *k, const void *v) {
// This repetition kind of sucks but we need
// to carry state somehow
AliasAutocompletions *c = in;
const char *needle = c->needle;
const int needle_len = c->needle_len;

const RCmdAliasVal *val = v;

/* Skip command aliases if we're filtering them out */
if (c->must_be_data && !val->is_data) {
return true;
}

if (!needle_len || !strncmp (k, needle, needle_len)) {
c->valid_completions[c->num_completions] = k;
c->valid_completion_vals[c->num_completions] = v;
c->num_completions++;
}

return true;
}

static void autocomplete_alias(RLineCompletion *completion, RCmd *cmd, const char *needle, bool must_be_data) {
AliasAutocompletions c;
const int needle_len = strlen (needle);
int i;

RList *alias_keys = r_cmd_alias_keys (cmd);
RList *alias_match = r_list_new ();
RListIter *it;

/* Get possible completions */
r_list_foreach_iter (alias_keys, it) {
const char *k = it->data;
const RCmdAliasVal *val = r_cmd_alias_get (cmd, k);
/* Skip aliases that don't match given data/command setting */
if (!val || val->is_data != is_data) {
continue;
}
c.needle = needle;
c.needle_len = needle_len;
// Filter out command aliases?
c.must_be_data = must_be_data;
// Single block, borrowed pointers
c.valid_completions = R_NEWS (const char *, cmd->aliases->count);
c.valid_completion_vals = R_NEWS (const RCmdAliasVal *, cmd->aliases->count);
c.num_completions = 0;

if (!needle_len || !strncmp (k, needle, needle_len)) {
r_list_append (alias_match, (void *)k);
}
}
r_list_free (alias_keys);
ht_pp_foreach (cmd->aliases, check_alias_completion, &c);

const int match_count = r_list_length (alias_match);
const int match_count = c.num_completions;
if (match_count == 1) {
/* If only 1 possible completion, use it */
const char *k = r_list_pop (alias_match);
const RCmdAliasVal *val = r_cmd_alias_get (cmd, k);
if (val) {
const char *v = (char *)val->data;
r_cons_printf ("$%s=%s\n", k, v);
r_cons_flush ();
char *completed_alias = r_str_newf ("$%s", k);
r_line_completion_push (completion, completed_alias);
free (completed_alias);
}
const char *k = c.valid_completions[0];
const RCmdAliasVal *val = c.valid_completion_vals[0];

char *v = r_cmd_alias_val_strdup ((RCmdAliasVal *)val);
r_cons_printf ("$%s=%s%s\n", k, val->is_data? "$": "", v);
r_cons_flush ();

char *completed_alias = r_str_newf ("$%s", k);
r_line_completion_push (completion, completed_alias);

free (completed_alias);
free (v);
} else if (match_count > 1) {
/* If multiple possible completions, show them */
r_list_foreach_iter (alias_match, it) {
const char *k = it->data;
const RCmdAliasVal *val = r_cmd_alias_get (cmd, k);
if (val) {
const char *v = (char *)val->data;
char *line = r_str_newf ("$%s=%s", k, v);
r_line_completion_push (completion, line);
free (line);
}
for (i = 0; i < c.num_completions; i++) {
const char *k = c.valid_completions[i];
const RCmdAliasVal *val = c.valid_completion_vals[i];

char *v = r_cmd_alias_val_strdup ((RCmdAliasVal *)val);
char *line = r_str_newf ("$%s=%s%s", k, val->is_data? "$": "", v);
r_line_completion_push (completion, line);

free (line);
free (v);
}
}
/* If 0 possible completions, do nothing */
r_list_free (alias_match);
free (c.valid_completions);
free (c.valid_completion_vals);
}

static void autocomplete_process_path(RLineCompletion *completion, const char *str, const char *path) {
Expand Down Expand Up @@ -1390,7 +1422,8 @@ static void autocomplete_filename(RLineCompletion *completion, RLineBuffer *buf,
const char *tinput = r_str_trim_head_ro (input);

if (input[0] == '$') {
autocomplete_alias (completion, cmd, input+1, true);
// Only show existing data aliases
autocomplete_alias (completion, cmd, input + 1, true);
goto out;
}

Expand Down Expand Up @@ -1501,16 +1534,16 @@ static void autocomplete_project(RCore *core, RLineCompletion *completion, const
static void autocomplete_minus(RCore *core, RLineCompletion *completion, const char *str) {
r_return_if_fail (str);
int length = strlen (str);
int i;

RList *keys = r_cmd_alias_keys (core->rcmd);
RListIter *it;
r_list_foreach_iter (keys, it) {
if (!strncmp (it->data, str, length)) {
r_line_completion_push (completion, it->data);
const char **keys = r_cmd_alias_keys (core->rcmd);
for (i = 0; i < core->rcmd->aliases->count; i++) {
if (!strncmp (keys[i], str, length)) {
r_line_completion_push (completion, keys[i]);
}
}

r_list_free (keys);
free (keys);
}

static void autocomplete_breakpoints(RCore *core, RLineCompletion *completion, const char *str) {
Expand Down
2 changes: 1 addition & 1 deletion libr/include/r_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ R_API int r_cmd_macro_call(RCmdMacro *mac, const char *name);
R_API int r_cmd_macro_break(RCmdMacro *mac, const char *value);

R_API bool r_cmd_alias_del(RCmd *cmd, const char *k);
R_API RList *r_cmd_alias_keys(RCmd *cmd);
R_API const char **r_cmd_alias_keys(RCmd *cmd);
R_API int r_cmd_alias_set_cmd(RCmd *cmd, const char *k, const char *v);
R_API int r_cmd_alias_set_str(RCmd *cmd, const char *k, const char *v);
R_API int r_cmd_alias_set_raw(RCmd *cmd, const char *k, const ut8 *v, int sz);
Expand Down

0 comments on commit c496680

Please sign in to comment.