rizin/librz/core/cmd/cmd_shell.c
Giovanni 6131a0e187
Fix RZ_IPI usage on public headers & fix memory leak in the ROP code. (#6171)
* Fix RZ_IPI declaration on public api for rz_pdb

* Fix RZ_IPI declaration on public api for jemalloc/glibc

* Fix RZ_IPI declaration on public api for rz_io

* Remove functions from public header for rz_panels

* Fix public functions not following rizin nomenclature in rz_graph

* Fix public functions not following rizin nomenclature in rz_rop

* move resolve_fcn_name into rz_analysis

* Fix public functions not following rizin nomenclature in rz_ascii_table

* Fix leak in rop code

* Rewrite parser for rop

* Add doxygen comment
2026-04-08 13:37:17 +08:00

766 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-FileCopyrightText: 2021 Dhruv Maroo <dhruvmaru007@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#define FMT_NONE 0
#define FMT_RAW 1
#define FMT_JSON 2
static char *showfile(char *res, const int nth, const char *fpath, const char *name, int printfmt) {
static int needs_newline = 0;
#if __UNIX__
struct stat sb;
#endif
const char *n = fpath;
char *nn, *u_rwx = NULL;
int sz = rz_file_size(n);
int perm, uid = 0, gid = 0;
int fch = '-';
if (!strncmp(fpath, "./", 2)) {
fpath = fpath + 2;
}
const bool isdir = rz_file_is_directory(n);
if (isdir) {
nn = rz_str_append(rz_str_dup(fpath), "/");
} else {
nn = rz_str_dup(fpath);
}
if (!*nn) {
free(nn);
return res;
}
perm = isdir ? 0755 : 0644;
if (!printfmt) {
needs_newline = ((nth + 1) % 4) ? 1 : 0;
res = rz_str_appendf(res, "%18s%s", nn, needs_newline ? " " : "\n");
free(nn);
return res;
}
// TODO: escape non-printable chars in filenames
// TODO: Implement more real info in ls -l
// TODO: handle suid
#if __UNIX__
if (lstat(n, &sb) != -1) {
ut32 ifmt = sb.st_mode & S_IFMT;
uid = sb.st_uid;
gid = sb.st_gid;
perm = sb.st_mode & 0777;
if (!(u_rwx = rz_str_dup(rz_str_rwx_i(perm >> 6)))) {
free(nn);
return res;
}
if (sb.st_mode & S_ISUID) {
u_rwx[2] = (sb.st_mode & S_IXUSR) ? 's' : 'S';
}
if (isdir) {
fch = 'd';
} else {
switch (ifmt) {
case S_IFCHR: fch = 'c'; break;
case S_IFBLK: fch = 'b'; break;
case S_IFLNK: fch = 'l'; break;
case S_IFIFO: fch = 'p'; break;
#ifdef S_IFSOCK
case S_IFSOCK: fch = 's'; break;
#endif
}
}
}
#else
u_rwx = rz_str_dup("-");
fch = isdir ? 'd' : '-';
#endif
if (printfmt == 'q') {
res = rz_str_appendf(res, "%s\n", nn);
} else if (printfmt == 'e') {
const char *eDIR = "📁";
const char *eIMG = "🌅";
const char *eHID = "👀";
const char *eANY = " ";
// --
const char *icon = eANY;
if (isdir) {
icon = eDIR;
#if __UNIX__
} else if ((sb.st_mode & S_IFMT) == S_IFLNK) {
const char *eLNK = "📎";
icon = eLNK;
} else if (sb.st_mode & S_ISUID) {
const char *eUID = "🔼";
icon = eUID;
#endif
} else if (rz_str_casestr(nn, ".jpg") || rz_str_casestr(nn, ".png") || rz_str_casestr(nn, ".gif")) {
icon = eIMG;
} else if (*nn == '.') {
icon = eHID;
}
res = rz_str_appendf(res, "%s %s\n", icon, nn);
} else if (printfmt == FMT_RAW) {
res = rz_str_appendf(res, "%c%s%s%s 1 %4d:%-4d %-10d %s\n",
isdir ? 'd' : fch,
u_rwx ? u_rwx : "-",
rz_str_rwx_i((perm >> 3) & 7),
rz_str_rwx_i(perm & 7),
uid, gid, sz, nn);
} else if (printfmt == FMT_JSON) {
if (nth > 0) {
res = rz_str_append(res, ",");
}
res = rz_str_appendf(res, "{\"name\":\"%s\",\"size\":%d,\"uid\":%d,"
"\"gid\":%d,\"perm\":%d,\"isdir\":%s}",
name, sz, uid, gid, perm, isdir ? "true" : "false");
}
free(nn);
free(u_rwx);
return res;
}
static char *syscmd_ls(RZ_NONNULL const int argc, const char **argv) {
static int needs_newline = 0;
char *res = NULL;
const char *path = ".";
char *d = NULL;
char *p = NULL;
char *homepath = NULL;
char *pattern = NULL;
int printfmt = 0;
RzListIter *iter;
RzList *files;
char *name;
char *dir;
int off;
int path_in_second_arg = 0;
if (argc > 1) {
if ((!strncmp(argv[1], "-h", 2))) {
eprintf("Usage: ls ([-e,-l,-j,-q]) ([path]) # long, json, quiet\n");
return NULL;
} else if ((!strncmp(argv[1], "-e", 2))) {
printfmt = 'e';
} else if ((!strncmp(argv[1], "-q", 2))) {
printfmt = 'q';
} else if ((!strncmp(argv[1], "-l", 2)) || (!strncmp(argv[1], "-j", 2))) {
printfmt = (argv[1][1] == 'j') ? FMT_JSON : FMT_RAW;
} else {
path = argv[1];
path_in_second_arg = 1;
}
if (argc >= 3) {
if (!path_in_second_arg) {
path = argv[2];
}
}
}
if (RZ_STR_ISEMPTY(path)) {
path = ".";
} else if (rz_str_startswith(path, "~")) {
homepath = rz_path_home_expand(path);
if (homepath) {
path = (const char *)homepath;
}
} else if (*path == '$') {
if (!strncmp(path + 1, "home", 4) || !strncmp(path + 1, "HOME", 4)) {
homepath = rz_str_home((strlen(path) > 5) ? path + 6 : NULL);
if (homepath) {
path = (const char *)homepath;
}
}
}
if (!rz_file_is_directory(path)) {
p = strrchr(path, '/');
if (p) {
off = p - path;
d = (char *)calloc(1, off + 1);
if (!d) {
free(homepath);
return NULL;
}
memcpy(d, path, off);
path = (const char *)d;
pattern = rz_str_dup(p + 1);
} else {
pattern = rz_str_dup(path);
path = ".";
}
} else {
pattern = rz_str_dup("*");
}
if (rz_file_is_regular(path)) {
res = showfile(res, 0, path, path, printfmt);
free(homepath);
free(pattern);
free(d);
return res;
}
files = rz_sys_dir(path);
if (path[strlen(path) - 1] == '/') {
dir = rz_str_dup(path);
} else {
dir = rz_str_append(rz_str_dup(path), "/");
}
int nth = 0;
if (printfmt == FMT_JSON) {
res = rz_str_dup("[");
}
needs_newline = 0;
rz_list_foreach (files, iter, name) {
char *n = rz_str_append(rz_str_dup(dir), name);
if (!n) {
break;
}
if (rz_str_glob(name, pattern)) {
if (*n) {
res = showfile(res, nth, n, name, printfmt);
}
nth++;
}
free(n);
}
if (printfmt == FMT_JSON) {
res = rz_str_append(res, "]");
}
if (needs_newline) {
res = rz_str_append(res, "\n");
}
free(dir);
free(d);
free(homepath);
free(pattern);
rz_list_free(files);
return res;
}
static ut32 vernum(const char *s) {
// XXX this is known to be buggy, only works for strings like "x.x.x"
// XXX anything like "x.xx.x" will break the parsing
// XXX -git is ignored, maybe we should shift for it
char *a = rz_str_dup(s);
a = rz_str_replace(a, ".", "0", 1);
char *dash = strchr(a, '-');
if (dash) {
*dash = 0;
}
ut32 res = atoi(a);
free(a);
return res;
}
// ascii
RZ_IPI RzCmdStatus rz_cmd_shell_ascii_table_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%s", rz_get_ascii_table());
return RZ_CMD_STATUS_OK;
}
// env
RZ_IPI RzCmdStatus rz_cmd_shell_env_handler(RzCore *core, int argc, const char **argv) {
char *p, **e;
switch (argc) {
case 1:
e = rz_sys_get_environ();
while (!RZ_STR_ISEMPTY(e)) {
rz_cons_println(*e);
e++;
}
return RZ_CMD_STATUS_OK;
case 2:
p = rz_sys_getenv(argv[1]);
if (!p) {
return RZ_CMD_STATUS_OK;
}
rz_cons_println(p);
free(p);
return RZ_CMD_STATUS_OK;
case 3:
rz_sys_setenv(argv[1], argv[2]);
return RZ_CMD_STATUS_OK;
default:
return RZ_CMD_STATUS_WRONG_ARGS;
}
}
// exit
RZ_IPI RzCmdStatus rz_cmd_shell_exit_handler(RzCore *core, int argc, const char **argv) {
core->num->value = 0LL;
return RZ_CMD_STATUS_EXIT;
}
// ls
RZ_IPI RzCmdStatus rz_cmd_shell_ls_handler(RzCore *core, int argc, const char **argv) {
char *res = syscmd_ls(argc, argv);
if (!res) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_print(res);
free(res);
return RZ_CMD_STATUS_OK;
}
// rm
RZ_IPI RzCmdStatus rz_cmd_shell_rm_handler(RzCore *core, int argc, const char **argv) {
return rz_file_rm(argv[1]) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
// sleep
RZ_IPI RzCmdStatus rz_cmd_shell_sleep_handler(RzCore *core, int argc, const char **argv) {
void *bed = rz_cons_sleep_begin();
rz_sys_sleep(atoi(argv[1]));
rz_cons_sleep_end(bed);
return RZ_CMD_STATUS_OK;
}
// uniq
RZ_IPI RzCmdStatus rz_cmd_shell_uniq_handler(RzCore *core, int argc, const char **argv) {
if (argc < 2) {
return RZ_CMD_STATUS_OK;
}
char *res = rz_syscmd_uniq(argv[1]);
if (!res) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_print(res);
free(res);
return RZ_CMD_STATUS_OK;
}
// uname
RZ_IPI RzCmdStatus rz_cmd_shell_uname_handler(RzCore *core, int argc, const char **argv) {
RSysInfo *si = rz_sys_info();
if (!si) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_printf("%s", si->sysname);
if (argc > 1 && strcmp(argv[1], "-r") == 0) {
rz_cons_printf(" %s", si->release);
}
rz_cons_newline();
rz_sys_info_free(si);
return RZ_CMD_STATUS_OK;
}
// echo
RZ_IPI RzCmdStatus rz_cmd_shell_echo_handler(RzCore *core, int argc, const char **argv) {
if (argc >= 2) {
char *output = rz_str_array_join(argv + 1, argc - 1, " ");
rz_str_unescape(output);
rz_cons_print(output);
free(output);
}
rz_cons_newline();
return RZ_CMD_STATUS_OK;
}
// cp
RZ_IPI RzCmdStatus rz_cmd_shell_cp_handler(RzCore *core, int argc, const char **argv) {
bool rc = rz_file_copy(argv[1], argv[2]);
if (!rc) {
RZ_LOG_ERROR("Failed to copy %s to %s\n", argv[1], argv[2]);
}
return rc ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
// cd
RZ_IPI RzCmdStatus rz_cmd_shell_cd_handler(RzCore *core, int argc, const char **argv) {
static char *olddir = NULL;
bool ret = false;
const char *dir = "~";
if (argc > 1) {
dir = argv[1];
}
if (!strcmp(dir, "-")) {
if (olddir) {
char *newdir = olddir;
olddir = rz_sys_getdir();
if (!rz_sys_chdir(newdir)) {
RZ_LOG_ERROR("Cannot chdir to %s\n", newdir);
free(olddir);
olddir = newdir;
} else {
free(newdir);
ret = true;
}
} else {
RZ_LOG_ERROR("No old directory found\n");
}
} else {
char *cwd = rz_sys_getdir();
if (!rz_sys_chdir(dir)) {
RZ_LOG_ERROR("Cannot chdir to %s\n", dir);
free(cwd);
} else {
free(olddir);
olddir = cwd;
ret = true;
}
}
return ret ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
// cat
RZ_IPI RzCmdStatus rz_cmd_shell_cat_handler(RzCore *core, int argc, const char **argv) {
const char *path = argv[1];
if (*path == '$') {
const char *oldText = rz_cmd_alias_get(core->rcmd, path, 1);
if (oldText) {
rz_cons_printf("%s\n", oldText + 1);
} else {
RZ_LOG_ERROR("Invalid alias\n");
}
return oldText ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
char *res = rz_syscmd_cat(path);
if (res) {
rz_cons_print(res);
free(res);
}
return RZ_CMD_STATUS_OK;
}
// mv
RZ_IPI RzCmdStatus rz_cmd_shell_mv_handler(RzCore *core, int argc, const char **argv) {
char *input = rz_str_newf("mv %s %s", argv[1], argv[2]);
int ec = rz_sys_system(input);
free(input);
return ec == 0 ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
// mkdir
RZ_IPI RzCmdStatus rz_cmd_shell_mkdir_handler(RzCore *core, int argc, const char **argv) {
RzStrBuf *buf = rz_strbuf_new(NULL);
for (int i = 1; i < argc; i++) {
rz_strbuf_appendf(buf, " %s", argv[i]);
}
char *input = rz_strbuf_drain(buf);
char *res = rz_syscmd_mkdir(input);
free(input);
if (res) {
rz_cons_print(res);
free(res);
}
return RZ_CMD_STATUS_OK;
}
// pwd
RZ_IPI RzCmdStatus rz_cmd_shell_pwd_handler(RzCore *core, int argc, const char **argv) {
char *cwd = rz_sys_getdir();
if (cwd) {
rz_cons_println(cwd);
free(cwd);
}
return RZ_CMD_STATUS_OK;
}
// sort
RZ_IPI RzCmdStatus rz_cmd_shell_sort_handler(RzCore *core, int argc, const char **argv) {
if (argc < 2) {
return RZ_CMD_STATUS_OK;
}
char *res = rz_syscmd_sort(argv[1]);
if (!res) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_print(res);
free(res);
return RZ_CMD_STATUS_OK;
}
// clear
// cls
RZ_IPI RzCmdStatus rz_cmd_shell_clear_handler(RzCore *core, int argc, const char **argv) {
rz_cons_clear00();
return RZ_CMD_STATUS_OK;
}
// flush
RZ_IPI RzCmdStatus rz_cmd_shell_flush_handler(RzCore *core, int argc, const char **argv) {
rz_cons_flush();
return RZ_CMD_STATUS_OK;
}
// which
RZ_IPI RzCmdStatus rz_cmd_shell_which_handler(RzCore *core, int argc, const char **argv) {
char *solved = rz_file_path(argv[1]);
if (!solved) {
RZ_LOG_ERROR("Could not get the full path of '%s'\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(solved);
free(solved);
return RZ_CMD_STATUS_OK;
}
// fortune
RZ_IPI RzCmdStatus rz_cmd_shell_fortune_handler(RzCore *core, int argc, const char **argv) {
rz_core_fortune_print_random(core);
return RZ_CMD_STATUS_OK;
}
// diff
RZ_IPI RzCmdStatus rz_cmd_shell_diff_handler(RzCore *core, int argc, const char **argv) {
char *a = rz_file_slurp(argv[1], NULL);
if (!a) {
RZ_LOG_ERROR("core: Cannot open file A: \"%s\"\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
char *b = rz_file_slurp(argv[2], NULL);
if (!b) {
RZ_LOG_ERROR("core: Cannot open file B: \"%s\"\n", argv[2]);
free(a);
return RZ_CMD_STATUS_ERROR;
}
RzDiff *dff = rz_diff_lines_new(a, b, NULL);
bool color = rz_config_get_i(core->config, "scr.color") > 0;
char *uni = rz_diff_unified_text(dff, argv[1], argv[2], false, color);
rz_diff_free(dff);
rz_cons_printf("%s\n", uni);
free(uni);
free(a);
free(b);
return RZ_CMD_STATUS_OK;
}
// date
RZ_IPI RzCmdStatus rz_cmd_shell_date_handler(RzCore *core, int argc, const char **argv) {
char *now = rz_time_date_now_to_string();
rz_cons_printf("%s\n", now);
free(now);
return RZ_CMD_STATUS_OK;
}
// pkill
RZ_IPI RzCmdStatus rz_cmd_shell_pkill_handler(RzCore *core, int argc, const char **argv) {
RzListIter *iter;
RzDebugPid *pid;
RzList *pids = (core->dbg->cur && core->dbg->cur->pids)
? core->dbg->cur->pids(core->dbg, 0)
: NULL;
rz_list_foreach (pids, iter, pid) {
if (strstr(pid->path, argv[1])) {
rz_debug_kill(core->dbg, pid->pid, 0, 9);
}
}
rz_list_free(pids);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_print_init_time_values_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("plug.init = %" PFMT64d "\n"
"plug.load = %" PFMT64d "\n"
"file.load = %" PFMT64d "\n",
core->times->loadlibs_init_time,
core->times->loadlibs_time,
core->times->file_open_time);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_calculate_command_time_handler(RzCore *core, int argc, const char **argv) {
ut64 start = rz_time_now_mono();
rz_core_cmd(core, argv[1], 0);
ut64 end = rz_time_now_mono();
double seconds = (double)(end - start) / RZ_USEC_PER_SEC;
core->num->value = (ut64)seconds;
rz_cons_printf("%lf\n", seconds);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_show_version_info_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD: {
char *v = rz_version_str(core->sys_path, NULL);
rz_cons_printf("%s\n", v);
free(v);
break;
}
case RZ_OUTPUT_MODE_QUIET: {
rz_cons_println(RZ_VERSION);
break;
}
case RZ_OUTPUT_MODE_JSON: {
PJ *pj = state->d.pj;
pj_o(pj);
pj_ks(pj, "arch", RZ_SYS_ARCH);
pj_ks(pj, "os", RZ_SYS_OS);
pj_ki(pj, "bits", RZ_SYS_BITS);
pj_ki(pj, "major", RZ_VERSION_MAJOR);
pj_ki(pj, "minor", RZ_VERSION_MINOR);
pj_ki(pj, "patch", RZ_VERSION_PATCH);
pj_ki(pj, "number", RZ_VERSION_NUMBER);
pj_ki(pj, "nversion", vernum(RZ_VERSION));
pj_ks(pj, "version", RZ_VERSION);
pj_end(pj);
break;
}
default:
rz_warn_if_reached();
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_show_version_numeric_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%d\n", vernum(RZ_VERSION));
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_show_version_numeric2_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%d\n", RZ_VERSION_NUMBER);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_show_version_major_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%d\n", RZ_VERSION_MAJOR);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_show_version_minor_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%d\n", RZ_VERSION_MINOR);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_show_version_patch_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%d\n", RZ_VERSION_PATCH);
return RZ_CMD_STATUS_OK;
}
static const char *avatar_orangg[] = {
" _______\n"
" / \\ .-%s-.\n"
" _| ( o) (o)\\_ | %s |\n"
" / _ .\\. | \\ <| %s |\n"
" \\| \\ ____ / 7` | %s |\n"
" '|\\| `---'/ `-%s-'\n"
" | /----. \\\n"
" | \\___/ |___\n"
" `-----'`-----'\n"
};
static const char *avatar_clippy[] = {
" .--. .-%s-.\n"
" | _| | %s |\n"
" | O O < %s |\n"
" | | | | %s |\n"
" || | / `-%s-'\n"
" |`-'|\n"
" `---'\n",
" .--. .-%s-.\n"
" | \\ | %s |\n"
" | O o < %s |\n"
" | | / | %s |\n"
" | ( / `-%s-'\n"
" | / \n"
" `--'\n",
" .--. .-%s-.\n"
" | _|_ | %s |\n"
" | O O < %s |\n"
" | || | %s |\n"
" | _:| `-%s-'\n"
" | |\n"
" `---'\n",
};
static const char *avatar_clippy_utf8[] = {
" ╭──╮ ╭─%s─╮\n"
" │ _│ │ %s │\n"
" │ O O < %s │\n"
" │ │╭ │ %s │\n"
" ││ ││ ╰─%s─╯\n"
" │└─┘│\n"
" ╰───╯\n",
" ╭──╮ ╭─%s─╮\n"
" │ ╶│╶ │ %s │\n"
" │ O o < %s │\n"
" │ │ │ %s │\n"
" │ ╭┘ ╰─%s─╯\n"
" │ ╰ \n"
" ╰──'\n",
" ╭──╮ ╭─%s─╮\n"
" │ _│_ │ %s │\n"
" │ O O < %s │\n"
" │ │╷ │ %s │\n"
" │ ││ ╰─%s─╯\n"
" │ ─╯│\n"
" ╰───╯\n",
};
static const char *avatar_cybcat[] = {
" /\\.---./\\ .-%s-.\n"
" '-- --' | %s |\n"
"---- ^ ^ ---- < %s |\n"
" _.- Y -._ | %s |\n"
" `-%s-'\n",
" /\\.---./\\ .-%s-.\n"
" '-- @ @ --' | %s |\n"
"---- Y ---- < %s |\n"
" _.- O -._ | %s |\n"
" `-%s-'\n",
" /\\.---./\\ .-%s-.\n"
" '-- = = --' | %s |\n"
"---- Y ---- < %s |\n"
" _.- U -._ | %s |\n"
" `-%s-'\n",
};
enum {
RZ_AVATAR_ORANGG,
RZ_AVATAR_CYBCAT,
RZ_AVATAR_CLIPPY,
};
/**
* \brief Get clippy echo string.
* \param msg The message to echo.
*/
RZ_API RZ_OWN char *rz_core_clippy(RZ_NONNULL RzCore *core, RZ_NONNULL const char *msg) {
rz_return_val_if_fail(core && msg, NULL);
int type = RZ_AVATAR_CLIPPY;
if (*msg == '+' || *msg == '3') {
char *space = strchr(msg, ' ');
if (!space) {
return NULL;
}
type = (*msg == '+') ? RZ_AVATAR_ORANGG : RZ_AVATAR_CYBCAT;
msg = space + 1;
}
const char *f;
int msglen = rz_str_utf8_cols(msg);
char *s = rz_str_pad(' ', msglen);
char *l;
if (type == RZ_AVATAR_ORANGG) {
l = rz_str_pad('-', msglen);
f = avatar_orangg[0];
} else if (type == RZ_AVATAR_CYBCAT) {
l = rz_str_pad('-', msglen);
f = avatar_cybcat[rz_num_rand32(RZ_ARRAY_SIZE(avatar_cybcat))];
} else if (rz_config_get_i(core->config, "scr.utf8")) {
l = (char *)rz_str_repeat("", msglen);
f = avatar_clippy_utf8[rz_num_rand32(RZ_ARRAY_SIZE(avatar_clippy_utf8))];
} else {
l = rz_str_pad('-', msglen);
f = avatar_clippy[rz_num_rand32(RZ_ARRAY_SIZE(avatar_clippy))];
}
char *string = rz_str_newf(f, l, s, msg, s, l);
free(l);
free(s);
return string;
}
RZ_IPI void rz_core_clippy_print(RzCore *core, const char *msg) {
char *string = rz_core_clippy(core, msg);
if (string) {
rz_cons_print(string);
free(string);
}
}
RZ_IPI RzCmdStatus rz_cmd_shell_clippy_handler(RzCore *core, int argc, const char **argv) {
if (argc >= 2) {
char *output = rz_str_array_join(argv + 1, argc - 1, " ");
rz_core_clippy_print(core, output);
free(output);
}
rz_cons_newline();
return RZ_CMD_STATUS_OK;
}