rizin/librz/core/tui/vmenus.c
2024-05-17 18:06:50 +08:00

983 lines
24 KiB
C

// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <string.h>
#include <rz_core.h>
#include <rz_util.h>
#include <rz_asm.h>
#include <rz_util/rz_print.h>
#include <rz_util/rz_strbuf.h>
#include "../core_private.h"
#include "modes.h"
#define MAX_FORMAT 3
// helper
static void function_rename(RzCore *core, ut64 addr, const char *name) {
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_list_foreach (core->analysis->fcns, iter, fcn) {
if (fcn->addr == addr) {
rz_flag_unset_name(core->flags, fcn->name);
free(fcn->name);
fcn->name = strdup(name);
rz_flag_set(core->flags, name, addr, rz_analysis_function_size_from_entry(fcn));
break;
}
}
}
static void variable_rename(RzCore *core, ut64 addr, int vindex, const char *name) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_NULL);
if (!fcn || vindex < 0 || vindex >= rz_pvector_len(&fcn->vars)) {
return;
}
RzAnalysisVar *var = rz_pvector_at(&fcn->vars, vindex);
rz_analysis_var_rename(var, name, true);
}
static void variable_set_type(RzCore *core, ut64 addr, int vindex, const char *type) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_NULL);
if (!fcn || vindex < 0 || vindex >= rz_pvector_len(&fcn->vars)) {
return;
}
RzAnalysisVar *var = rz_pvector_at(&fcn->vars, vindex);
RzType *ttype = rz_type_parse_string_single(core->analysis->typedb->parser, type, NULL);
if (!ttype) {
return;
}
rz_analysis_var_set_type(var, ttype, true);
}
/**
* \brief Convert the string inputing to RzPVector, with WHITESPACE as separators
*
* \param inputing
* \return return the pointer of RzPVector
*/
static RzPVector /*<char *>*/ *capture_filter_keywords(char *inputing) {
rz_return_val_if_fail(inputing, NULL);
RzPVector *keywords = rz_pvector_new(free);
if (!keywords) {
return NULL;
}
char *processing = rz_str_trim_dup(inputing);
char *buf = rz_str_dup("");
for (int i = 0; i < strlen(processing); i++) {
if (IS_WHITESPACE(processing[i])) {
if (strlen(buf)) {
rz_pvector_push(keywords, buf);
buf = rz_str_dup("");
}
} else {
buf = rz_str_appendch(buf, processing[i]);
}
}
if (strlen(buf)) {
rz_pvector_push(keywords, buf);
} else {
RZ_FREE(buf);
}
free(processing);
return keywords;
}
/**
* \brief Filter the functions in visual analysis mode (helper of command f)
*
* \param core
* \param filter_fcn store the filtered functions
* \return return the number of functions that conform to the keywords
*/
static ut32 filter_function(RzCore *core, RzList /*<RzAnalysisFunction *>*/ *filter_fcn, RzPVector /*<char *>*/ *keywords) {
rz_return_val_if_fail(core, 0);
RzListIter *iter;
RzAnalysisFunction *fcn;
size_t num = 0;
rz_list_foreach (core->analysis->fcns, iter, fcn) {
bool contain = true;
void **it;
rz_pvector_foreach (keywords, it) {
contain = contain && strstr(fcn->name, (char *)*it);
}
if (!contain) {
continue;
}
if (filter_fcn) {
rz_list_append(filter_fcn, fcn);
}
num++;
}
return num;
}
// In visual mode, display function list
static ut64 var_functions_show(RzCore *core, int idx, int show, int cols) {
int wdelta = (idx > 5) ? idx - 5 : 0;
char *var_functions;
ut64 seek = core->offset;
ut64 addr = core->offset;
RzAnalysisFunction *fcn;
RzList *filter_fcn = core->analysis->fcns, *visual_filter = NULL;
int window, i = 0, print_full_func;
RzListIter *iter;
RzCoreVisual *visual = core->visual;
// Adjust the windows size automaticaly
(void)rz_cons_get_size(&window);
window -= 2; // size of command line in the bottom
if (visual->inputing) {
window -= 2; // filter size
}
if (!visual->hide_legend) {
window -= 7; // legend size
}
bool color = rz_config_get_i(core->config, "scr.color");
const char *color_addr = core->cons->context->pal.offset;
const char *color_fcn = core->cons->context->pal.fname;
if (visual->inputing) {
visual_filter = rz_list_newf(NULL);
if (visual_filter) {
RzPVector *keywords = capture_filter_keywords(visual->inputing);
if (keywords) {
filter_function(core, visual_filter, keywords);
RZ_FREE_CUSTOM(keywords, rz_pvector_free);
filter_fcn = visual_filter;
}
}
}
rz_list_foreach (filter_fcn, iter, fcn) {
print_full_func = true;
if (i >= wdelta) {
if (i > window + wdelta - 1) {
rz_cons_printf("...\n");
break;
}
if (idx == i) {
addr = fcn->addr;
}
if (show) {
char *tmp;
if (color) {
var_functions = rz_str_newf("%c%c %s0x%08" PFMT64x "" Color_RESET " %4" PFMT64d " %s%s" Color_RESET "",
(seek == fcn->addr) ? '>' : ' ',
(idx == i) ? '*' : ' ',
color_addr, fcn->addr, rz_analysis_function_realsize(fcn),
color_fcn, fcn->name);
} else {
var_functions = rz_str_newf("%c%c 0x%08" PFMT64x " %4" PFMT64d " %s",
(seek == fcn->addr) ? '>' : ' ',
(idx == i) ? '*' : ' ',
fcn->addr, rz_analysis_function_realsize(fcn), fcn->name);
}
if (var_functions) {
if (!rz_cons_singleton()->show_vals) {
int fun_len = rz_str_ansi_len(var_functions);
int columns = fun_len > cols ? cols - 2 : cols;
tmp = rz_str_ansi_crop(var_functions, 0, 0, columns, window);
if (rz_str_ansi_len(tmp) < fun_len) {
rz_cons_printf("%s..%s\n", tmp, Color_RESET);
print_full_func = false;
}
free(tmp);
}
if (print_full_func) {
rz_cons_println(var_functions);
}
free(var_functions);
}
}
}
i++;
}
if (filter_fcn != core->analysis->fcns) {
rz_list_free(filter_fcn);
}
return addr;
}
// In visual mode, display the variables.
static ut64 var_variables_show(RzCore *core, int idx, int *vindex, int show, int cols) {
int i = 0;
const ut64 addr = var_functions_show(core, idx, 0, cols);
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_NULL);
if (!fcn) {
return addr;
}
int window;
int wdelta = (idx > 5) ? idx - 5 : 0;
// Adjust the window size automatically.
(void)rz_cons_get_size(&window);
window -= 8; // Size of printed things.
// A new line so this looks reasonable.
rz_cons_newline();
int llen = rz_pvector_len(&fcn->vars);
if (*vindex >= llen) {
*vindex = llen - 1;
}
void **it;
rz_pvector_foreach (&fcn->vars, it) {
RzAnalysisVar *var = *it;
if (i >= wdelta) {
if (i > window + wdelta) {
rz_cons_printf("...\n");
break;
}
if (show) {
char *vartype = rz_type_as_string(core->analysis->typedb, var->type);
rz_cons_printf("%s%s %s %s @ ", i == *vindex ? "* " : " ", rz_analysis_var_is_arg(var) ? "arg" : "var", vartype, var->name);
free(vartype);
char *storage_str = rz_analysis_var_storage_to_string(core->analysis, var, &var->storage);
rz_cons_strcat(storage_str);
free(storage_str);
}
}
++i;
}
return addr;
}
static int level = 0;
static st64 delta = 0;
static ut64 column_nlines = 0;
// output is used to store the result of printCmds
// and avoid duplicated analysis while j and k is pressed
static char *output = NULL;
// output_mode labels which printCmds' result is stored in output
static int output_mode = -1;
static int output_addr = -1;
static int option = 0;
static int variable_option = 0;
static int printMode = 0;
static bool selectPanel = false;
static void rz_core_visual_analysis_refresh_column(RzCore *core, int colpos) {
ut64 addr = (level != 0 && level != 1)
? core->offset
: var_functions_show(core, option, 0, colpos);
int h, w = rz_cons_get_size(&h);
if (printMode == 1) { // px $r
addr += delta * 16;
}
if (output_mode != printMode || output_addr != addr) {
const char *cmd;
if (printMode > 0 && printMode < lastPrintMode) {
cmd = printCmds[printMode];
} else {
cmd = printCmds[printMode = 0];
}
char *cmdf = rz_str_newf("%s @ 0x%" PFMT64x, cmd, addr);
if (!cmdf) {
return;
}
RZ_FREE(output); // free the result of the last printCmds
output = rz_core_cmd_str(core, cmdf);
output_mode = printMode;
output_addr = addr;
free(cmdf);
}
if (output) {
// count the lines of output for calculating percentage
column_nlines = 0;
char *s = output;
while (*s) {
if (*s == '\n') {
column_nlines++;
}
s++;
}
// crop and print output, 'h - 2' because we have two new lines in rz_cons_printf
char *out;
if (printMode == 1) {
out = rz_str_ansi_crop(output, 0, 0, w - colpos, h - 2);
} else {
out = rz_str_ansi_crop(output, 0, delta, w - colpos, h - 2 + delta);
}
rz_cons_printf("\n%s\n", out);
free(out);
}
}
static const char *help_fun_visual[] = {
"(a)", "analyze ", "(-)", "delete ", "(x)", "xrefs to ", "(X)", "xrefs from\n",
"(r)", "rename ", "(c)", "calls ", "(d)", "define ", "(:)", "shell ", "(v)", "vars\n",
"(j/k)", "next/prev ", "(tab)", "column ", "(_)", "hud ", "(?)", "help\n",
"(f/F)", "set/reset filter ", "(s)", "function signature ", "(q)", "quit\n",
"(=)", "show/hide legend ", "(h/l)", "short/full function name\n\n",
NULL
};
static const char *help_var_visual[] = {
"(a)", "add ", "(x)", "xrefs ", "(r)", "rename\n",
"(t)", "type ", "(g)", "go ", "(-)", "delete\n",
"(q)", "quit ", "(s)", "signature\n\n",
NULL
};
static const char *help_vv_visual[] = {
"j,k", "select next/prev item or scroll if tab pressed",
"J,K", "scroll next/prev page \"\"",
"f,F", "set/reset filter keyword",
"h,q", "go back, quit",
"p,P", "switch next/prev print mode",
"v", "view selected function arguments and variables",
"x,X", "see xrefs to the selected function",
"tab", "toggle disasm column selection (to scroll in code)",
"!", "run 'afls' to sort all functions by address",
".", "seek to current function address",
":", "run rizin commands",
"_", "hud mode. same as: s $(afl~...)",
"enter", "enter function view (variables), xrefs",
NULL
};
static const char *help_vv_actions_visual[] = {
" functions:", "Add, Modify, Delete, Xrefs Calls Vars",
" variables:", "Add, Modify, Delete",
NULL
};
static void rz_core_vmenu_append_help(RzStrBuf *p, const char **help) {
int i;
RzConsContext *cons_ctx = rz_cons_singleton()->context;
const char *pal_args_color = cons_ctx->color_mode ? cons_ctx->pal.args : "",
*pal_help_color = cons_ctx->color_mode ? cons_ctx->pal.help : "",
*pal_reset = cons_ctx->color_mode ? cons_ctx->pal.reset : "";
for (i = 0; help[i]; i += 2) {
rz_strbuf_appendf(p, "%s%s %s%s%s",
pal_args_color, help[i],
pal_help_color, help[i + 1], pal_reset);
}
}
static ut64 rz_core_visual_analysis_refresh(RzCore *core) {
rz_return_val_if_fail(core, 0);
RzCoreVisual *visual = core->visual;
ut64 addr;
RzStrBuf *buf;
char old[1024];
bool color = rz_config_get_i(core->config, "scr.color");
int h, cols = rz_cons_get_size(&h);
old[0] = '\0';
addr = core->offset;
cols -= 50;
if (cols > 60) {
cols = 60;
}
rz_cons_clear00();
rz_core_visual_analysis_refresh_column(core, cols);
if (cols > 30) {
rz_cons_column(cols);
}
switch (level) {
// Show functions list help in visual mode
case 0: {
buf = rz_strbuf_new("");
if (color) {
rz_cons_strcat(core->cons->context->pal.prompt);
}
if (selectPanel) {
rz_cons_printf("-- functions -----------------[ %s ]-->>", printCmds[printMode]);
} else {
rz_cons_printf("-[ functions ]----------------- %s ---", printCmds[printMode]);
}
if (color) {
rz_cons_strcat("\n" Color_RESET);
}
if (!visual->hide_legend) {
rz_core_vmenu_append_help(buf, help_fun_visual);
}
char *drained = rz_strbuf_drain(buf);
rz_cons_printf("%s", drained);
free(drained);
// hints for filtered keywords
if (visual->inputing) {
if (visual->is_inputing) {
rz_cons_printf("input keywords: %s\n\n", visual->inputing);
} else {
rz_cons_printf("keywords: %s\n\n", visual->inputing);
}
}
addr = var_functions_show(core, option, 1, cols);
break;
}
case 1: {
buf = rz_strbuf_new("");
if (color) {
rz_cons_strcat(core->cons->context->pal.prompt);
}
rz_cons_printf("-[ variables ]----- 0x%08" PFMT64x "", addr);
if (color) {
rz_cons_strcat("\n" Color_RESET);
}
rz_core_vmenu_append_help(buf, help_var_visual);
char *drained = rz_strbuf_drain(buf);
rz_cons_printf("%s", drained);
addr = var_variables_show(core, option, &variable_option, 1, cols);
free(drained);
// var_index_show (core->analysis, fcn, addr, option);
break;
}
case 2: {
rz_cons_printf("Press 'q' to quit call refs\n");
if (color) {
rz_cons_strcat(core->cons->context->pal.prompt);
}
rz_cons_printf("-[ calls ]----------------------- 0x%08" PFMT64x " (TODO)\n", addr);
if (color) {
rz_cons_strcat("\n" Color_RESET);
}
// TODO: filter only the callrefs. but we cant grep here
sprintf(old, "afi @ 0x%08" PFMT64x, addr);
char *output = rz_core_cmd_str(core, old);
if (output) {
// 'h - 2' because we have two new lines in rz_cons_printf
if (!rz_cons_singleton()->show_vals) {
char *out = rz_str_ansi_crop(output, 0, 0, cols, h - 2);
rz_cons_printf("\n%s\n", out);
free(out);
RZ_FREE(output);
} else {
rz_cons_printf("\n%s\n", output);
RZ_FREE(output);
}
}
break;
}
default:
rz_warn_if_reached();
break;
}
// print percentage at right corner
cols = rz_cons_get_size(&h);
float p = (float)(delta + h - 2) / (float)column_nlines;
if (p > 1) {
p = 1;
}
char *percentage = rz_str_newf("%.1f%%", p * 100);
// move to the right corner
rz_cons_gotoxy(cols - strlen(percentage) - 1, h);
rz_cons_printf("%s", percentage);
free(percentage);
rz_cons_show_cursor(false);
rz_cons_flush();
return addr;
}
static void rz_core_visual_analysis_refresh_oneshot(RzCore *core) {
rz_core_task_enqueue_oneshot(&core->tasks, (RzCoreTaskOneShot)rz_core_visual_analysis_refresh, core);
}
static void rz_core_visual_debugtraces_help(RzCore *core) {
rz_cons_clear00();
rz_cons_printf(
"vbd: Visual Browse Debugtraces:\n\n"
" q - quit the bit editor\n"
" Q - Quit (jump into the disasm view)\n"
" j/k - Select next/previous trace\n"
" : - enter command\n");
rz_cons_flush();
rz_cons_any_key(NULL);
}
RZ_IPI void rz_core_visual_debugtraces(RzCore *core, const char *input) {
int i, delta = 0;
for (;;) {
char *trace_addr_str = rz_core_cmd_strf(core, "dtdq %d", delta);
ut64 trace_addr = rz_num_get(NULL, trace_addr_str);
free(trace_addr_str);
rz_cons_printf("[0x%08" PFMT64x "]> %d dbg.trace\n", trace_addr, delta);
for (i = 0; i < delta; i++) {
rz_core_cmdf(core, ".dte %d", i);
}
rz_core_cmd0(core, "x 64@r:SP");
rz_core_debug_ri(core);
// limit by rows here
// int rows = rz_cons_get_size (NULL);
rz_core_cmdf(core, "dtd %d", delta);
rz_cons_visual_flush();
char ch;
if (input && *input) {
ch = *input;
input++;
} else {
ch = rz_cons_readchar();
}
if (ch == 4 || ch == -1) {
if (level == 0) {
goto beach;
}
level--;
continue;
}
ch = rz_cons_arrow_to_hjkl(ch); // get ESC+char, return 'hjkl' char
switch (ch) {
case 'Q': // tab
{
ut64 oseek = core->offset;
core->vmode = false;
rz_core_seek(core, trace_addr, true);
rz_core_visual(core, "");
rz_core_seek(core, oseek, true);
} break;
case 'q':
goto beach;
break;
case ']':
rz_config_set_i(core->config, "hex.cols", rz_config_get_i(core->config, "hex.cols") + 1);
break;
case '[':
rz_config_set_i(core->config, "hex.cols", rz_config_get_i(core->config, "hex.cols") - 1);
break;
case 'J':
delta += 10;
break;
case 'K':
delta -= 10;
if (delta < 0) {
delta = 0;
}
break;
case 'j':
delta++;
break;
case 'k':
delta--;
if (delta < 0) {
delta = 0;
}
break;
case ':':
rz_core_visual_prompt(core);
rz_cons_any_key(NULL);
break;
case '?':
rz_core_visual_debugtraces_help(core);
break;
}
}
beach:;
}
static char *__prompt(const char *msg, void *p) {
char res[128];
rz_cons_show_cursor(true);
rz_cons_set_raw(false);
rz_line_set_prompt(rz_cons_singleton()->line, msg);
res[0] = 0;
if (!rz_cons_fgets(res, sizeof(res), 0, NULL)) {
res[0] = 0;
}
return strdup(res);
}
static void addVar(RzCore *core, int ch, const char *msg) {
char *src = __prompt(msg, NULL);
char *name = __prompt("Variable Name: ", NULL);
char *type = __prompt("Type of Variable (int32_t): ", NULL);
char *cmd = rz_str_newf("afv%c %s %s %s", ch, src, name, type);
rz_str_trim(cmd);
rz_core_cmd0(core, cmd);
free(cmd);
free(src);
free(name);
free(type);
}
static void set_current_option_to_seek(RzCore *core) {
RzListIter *iter;
RzAnalysisFunction *fcn;
int i = 0;
rz_list_foreach (core->analysis->fcns, iter, fcn) {
if (core->offset == fcn->addr) {
option = i;
}
i++;
}
}
/* Like emenu but for real */
RZ_IPI void rz_core_visual_analysis(RzCore *core, const char *input) {
char old[218];
int nfcns, ch = 0;
RzLine *line = core->cons->line;
RzCoreVisual *visual = core->visual;
RzConsEvent olde = core->cons->event_resize;
void *olde_user = core->cons->event_data;
ut64 addr = core->offset;
core->cons->event_resize = NULL; // avoid running old event with new data
core->cons->event_data = core;
core->cons->event_resize = (RzConsEvent)rz_core_visual_analysis_refresh_oneshot;
level = 0;
set_current_option_to_seek(core);
int asmbytes = rz_config_get_i(core->config, "asm.bytes");
rz_config_set_i(core->config, "asm.bytes", 0);
for (;;) {
nfcns = rz_list_length(core->analysis->fcns);
if (visual->inputing) {
RzPVector *keywords = capture_filter_keywords(visual->inputing);
if (keywords) {
nfcns = filter_function(core, NULL, keywords);
}
RZ_FREE_CUSTOM(keywords, rz_pvector_free);
}
addr = rz_core_visual_analysis_refresh(core);
// for filter on the go
if (level == 0 && visual->is_inputing) {
int ch = rz_cons_readchar();
switch (ch) {
case 13: // CR
visual->is_inputing = false;
if (!strlen(visual->inputing)) {
RZ_FREE(visual->inputing);
}
break;
case 127: // Backspace
case 8:
if (strlen(visual->inputing) > 0) {
visual->inputing[strlen(visual->inputing) - 1] = '\0';
}
break;
default:
if (!IS_PRINTABLE(ch)) {
continue;
}
visual->inputing = rz_str_appendch(visual->inputing, ch);
break;
}
// mute the following switch while inputing keyword
continue;
}
if (input && *input) {
ch = *input;
input++;
} else {
ch = rz_cons_readchar();
}
if (ch == 4 || ch == -1) {
if (level == 0) {
goto beach;
}
level--;
continue;
}
ch = rz_cons_arrow_to_hjkl(ch); // get ESC+char, return 'hjkl' char
switch (ch) {
case '=':
if (level == 0) {
visual->hide_legend = visual->hide_legend ? false : true;
}
break;
case 'f':
if (level == 0) {
// add new keyword
visual->is_inputing = true;
if (!visual->inputing) {
visual->inputing = rz_str_dup("");
}
option = 0;
}
break;
case 'F':
if (level == 0) {
// reset all keywords
RZ_FREE(visual->inputing);
}
break;
case '[':
rz_cons_singleton()->show_vals = true;
break;
case ']':
rz_cons_singleton()->show_vals = false;
break;
case '?':
rz_cons_clear00();
RzStrBuf *buf = rz_strbuf_new("");
rz_cons_println("|Usage: vv");
rz_core_visual_append_help(buf, "Actions supported", help_vv_actions_visual);
rz_core_visual_append_help(buf, "Keys", help_vv_visual);
rz_cons_printf("%s", rz_strbuf_drain(buf));
rz_cons_flush();
rz_cons_any_key(NULL);
break;
case 9:
selectPanel = !selectPanel;
if (!selectPanel) {
delta = 0;
}
break;
case ':': {
rz_cons_show_cursor(true);
ut64 orig = core->offset;
rz_core_seek(core, addr, false);
while (rz_core_visual_prompt(core))
;
rz_core_seek(core, orig, false);
rz_cons_show_cursor(false);
}
continue;
case '/':
rz_core_prompt_highlight(core);
break;
case 'a':
switch (level) {
case 0:
// Remove the old function information
rz_core_analysis_undefine(core, core->offset);
rz_analysis_fcn_del_locs(core->analysis, core->offset);
rz_analysis_fcn_del(core->analysis, core->offset);
// Reanalyze and create function from scratch
rz_core_analysis_function_add(core, NULL, core->offset, false);
break;
case 1: {
eprintf("Select variable source ('r'egister or 's'tack): ");
int type = rz_cons_readchar();
switch (type) {
case 'r':
addVar(core, type, "Source Register Name: ");
break;
case 's':
addVar(core, type, "Stack Relative Delta: ");
break;
}
} break;
}
break;
case 'r': {
switch (level) {
case 1:
rz_cons_show_cursor(true);
rz_cons_set_raw(false);
rz_line_set_prompt(line, "New name: ");
if (rz_cons_fgets(old, sizeof(old), 0, NULL)) {
if (*old) {
// old[strlen (old)-1] = 0;
variable_rename(core, addr, variable_option, old);
}
}
break;
default:
rz_line_set_prompt(line, "New name: ");
if (rz_cons_fgets(old, sizeof(old), 0, NULL)) {
if (*old) {
// old[strlen (old)-1] = 0;
function_rename(core, addr, old);
}
}
break;
}
rz_cons_set_raw(true);
rz_cons_show_cursor(false);
} break;
case 't':
if (level == 1) {
rz_cons_show_cursor(true);
rz_cons_set_raw(false);
rz_line_set_prompt(line, "New type: ");
if (rz_cons_fgets(old, sizeof(old), 0, NULL)) {
if (*old) {
// old[strlen (old)-1] = 0;
variable_set_type(core, addr, variable_option, old);
}
}
rz_cons_set_raw(true);
rz_cons_show_cursor(false);
}
break;
case '.':
delta = 0;
break;
case 'R':
rz_core_theme_nextpal(core, RZ_CONS_PAL_SEEK_NEXT);
break;
case 'p':
printMode++;
delta = 0;
break;
case 'P':
if (printMode == 0) {
printMode = lastPrintMode;
} else {
printMode--;
}
delta = 0;
break;
case 'd':
rz_core_visual_define(core, "", 0);
break;
case '-':
switch (level) {
case 0:
// Remove the old function information
rz_core_analysis_undefine(core, addr);
rz_analysis_fcn_del_locs(core->analysis, addr);
rz_analysis_fcn_del(core->analysis, addr);
break;
}
break;
case 'x':
rz_core_visual_xrefs(core, false, true);
break;
case 'X':
rz_core_visual_xrefs(core, true, true);
break;
case 's':
rz_core_analysis_function_signature_editor(core, addr);
break;
case 'c':
level = 2;
break;
case 'v':
level = 1;
variable_option = 0;
break;
case '_': {
rz_core_cmd0(core, "s $(afl~...)");
int n = 0;
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_list_foreach (core->analysis->fcns, iter, fcn) {
if (fcn->addr == core->offset) {
option = n;
break;
}
n++;
}
} break;
case 'j':
if (selectPanel) {
delta += 1;
} else {
delta = 0;
switch (level) {
case 1:
variable_option++;
break;
default:
option++;
if (option >= nfcns) {
--option;
}
break;
}
}
break;
case '!':
// TODO: use aflsn/aflsb/aflss/...
{
static int sortMode = 0;
const char *sortModes[4] = { "aflsa", "aflss", "aflsb", "aflsn" };
rz_core_cmd0(core, sortModes[sortMode % 4]);
sortMode++;
}
break;
case 'k':
if (selectPanel) {
if (delta > 0) {
delta -= 1;
}
} else {
delta = 0;
switch (level) {
case 1:
variable_option = (variable_option <= 0) ? 0 : variable_option - 1;
break;
default:
option = (option <= 0) ? 0 : option - 1;
break;
}
}
break;
case 'J':
if (selectPanel) {
delta += 40;
} else {
int rows = 0;
rz_cons_get_size(&rows);
option += (rows - 5);
if (option >= nfcns) {
option = nfcns - 1;
}
}
break;
case 'K':
if (selectPanel) {
if (delta > 40) {
delta -= 40;
}
} else {
int rows = 0;
rz_cons_get_size(&rows);
option -= (rows - 5);
if (option < 0) {
option = 0;
}
}
break;
case 'g': {
rz_core_visual_showcursor(core, true);
rz_core_visual_offset(core); // change the seek to selected offset
set_current_option_to_seek(core);
rz_core_visual_showcursor(core, false);
} break;
case 'G':
rz_core_seek(core, addr, SEEK_SET);
goto beach;
case ' ':
case '\r':
case '\n':
level = 0;
rz_core_seek(core, addr, SEEK_SET);
goto beach;
break;
case 'l':
rz_cons_singleton()->show_vals = true;
break;
case 'h':
rz_cons_singleton()->show_vals = false;
break;
case 'b': // back
level = 0;
break;
case 'Q':
case 'q':
if (level == 0) {
goto beach;
}
level--;
break;
}
}
beach:
RZ_FREE(output);
core->cons->event_resize = NULL; // avoid running old event with new data
core->cons->event_data = olde_user;
core->cons->event_resize = olde;
level = 0;
rz_config_set_i(core->config, "asm.bytes", asmbytes);
}