mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-23 13:19:54 +00:00
* Fix LIBR_PLUGINS in env.sh
* Initial implementation of the visual mode in core * Added pc and ps print formats (c code and string) * Added '/' command in core (search hexa and str) * Add '-d' to radare2 test program * New r_file_path to resolve file path thru $PATH * 'make install' works everywhere
This commit is contained in:
parent
389cce05f6
commit
9bd9cbe135
2
env.sh
2
env.sh
@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
new_env='
|
||||
LIBR_PLUGINS=$PATH:$PWD/prefix/lib/radare2
|
||||
LIBR_PLUGINS=$PWD/prefix/lib/radare2
|
||||
PATH=$PATH:$PWD/prefix/bin
|
||||
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/prefix/lib
|
||||
'
|
||||
|
@ -34,9 +34,9 @@ install:
|
||||
@for a in `find */t -perm /u+x -type f | grep 2`; \
|
||||
do echo " $$a"; cp $$a ${PREFIX}/bin ; done
|
||||
# plugins
|
||||
@mkdir -p ${PREFIX}/plugins
|
||||
@mkdir -p ${PREFIX}/lib/radare2
|
||||
@for a in `find */p -perm /u+x -type f`; \
|
||||
do echo " $$a"; cp $$a ${PREFIX}/plugins ; done
|
||||
do echo " $$a"; cp $$a ${PREFIX}/lib/radare2 ; done
|
||||
# test programs
|
||||
@mkdir -p ${PREFIX}/bin-test
|
||||
@for a in `find */t -perm /u+x -type f | grep -v 2`; \
|
||||
|
@ -57,12 +57,15 @@ const char *r_cons_color_names[CONS_COLORS_SIZE+1] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
void r_cons_invert(int set)
|
||||
void r_cons_invert(int set, int color)
|
||||
{
|
||||
if (set)
|
||||
r_cons_strcat("\x1b[7m");
|
||||
else
|
||||
r_cons_strcat("\x1b[0m");
|
||||
if (color) {
|
||||
if (set) r_cons_strcat("\x1b[7m");
|
||||
else r_cons_strcat("\x1b[0m");
|
||||
} else {
|
||||
if (set) r_cons_strcat("[");
|
||||
else r_cons_strcat("]");
|
||||
}
|
||||
}
|
||||
|
||||
const char *r_cons_colors[CONS_COLORS_SIZE+1] = {
|
||||
|
@ -105,9 +105,8 @@ void r_cons_gotoxy(int x, int y)
|
||||
|
||||
void r_cons_clear00()
|
||||
{
|
||||
r_cons_lines = 0;
|
||||
r_cons_clear();
|
||||
r_cons_gotoxy(0,0);
|
||||
r_cons_gotoxy(0, 0);
|
||||
}
|
||||
|
||||
void r_cons_clear()
|
||||
@ -137,6 +136,7 @@ void r_cons_reset()
|
||||
if (r_cons_buffer)
|
||||
r_cons_buffer[0] = '\0';
|
||||
r_cons_buffer_len = 0;
|
||||
r_cons_lines = 0;
|
||||
}
|
||||
|
||||
const char *r_cons_get_buffer()
|
||||
@ -144,7 +144,6 @@ const char *r_cons_get_buffer()
|
||||
return r_cons_buffer;
|
||||
}
|
||||
|
||||
|
||||
void r_cons_grep(const char *str)
|
||||
{
|
||||
char *ptr, *ptr2, *ptr3;
|
||||
|
@ -108,8 +108,10 @@ int r_cons_readchar()
|
||||
return -1;
|
||||
SetConsoleMode(h, mode);
|
||||
#else
|
||||
r_cons_set_raw(1);
|
||||
if (read(0, buf, 1)==-1)
|
||||
return -1;
|
||||
r_cons_set_raw(0);
|
||||
#endif
|
||||
return buf[0];
|
||||
}
|
||||
|
@ -86,15 +86,19 @@ static int cmd_seek(void *data, const char *input)
|
||||
r_core_seek(core, off);
|
||||
break;
|
||||
case '+':
|
||||
r_core_seek(core, core->seek + off);
|
||||
if (input[1]=='+') r_core_seek(core, core->seek + core->blocksize);
|
||||
else r_core_seek(core, core->seek + off);
|
||||
break;
|
||||
case '-':
|
||||
r_core_seek(core, core->seek - off);
|
||||
if (input[1]=='-') r_core_seek(core, core->seek - core->blocksize);
|
||||
else r_core_seek(core, core->seek - off);
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr,
|
||||
"Usage: s[+-] [addr]\n"
|
||||
" s 0x320 ; seek to this address\n"
|
||||
" s++ ; seek blocksize bytes forward\n"
|
||||
" s-- ; seek blocksize bytes backward\n"
|
||||
" s+ 512 ; seek 512 bytes forward\n"
|
||||
" s- 512 ; seek 512 bytes backward\n");
|
||||
break;
|
||||
@ -238,10 +242,17 @@ static int cmd_print(void *data, const char *input)
|
||||
for(idx=ret=0; idx < len; idx+=ret) {
|
||||
r_asm_set_pc(&a, a.pc + ret);
|
||||
ret = r_asm_disasm(&a, buf+idx, len-idx);
|
||||
r_cons_printf("0x%08llx %14s %s\n", core->seek+idx, a.buf_hex, a.buf_asm);
|
||||
r_cons_printf("0x%08llx %14s %s\n",
|
||||
core->seek+idx, a.buf_hex, a.buf_asm);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
r_print_string(core->seek, core->block, len, 1, 78, 1);
|
||||
break;
|
||||
case 'c':
|
||||
r_print_code(core->seek, core->block, len, 1, 78, 1);
|
||||
break;
|
||||
case 'r':
|
||||
r_print_raw(core->block, len);
|
||||
break;
|
||||
@ -252,9 +263,11 @@ static int cmd_print(void *data, const char *input)
|
||||
r_print_bytes(core->block, len, "%02x");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usage: p[8] [len]"
|
||||
fprintf(stderr, "Usage: p[8] [len]\n"
|
||||
" p8 [len] 8bit hexpair list of bytes\n"
|
||||
" px [len] hexdump of N bytes\n"
|
||||
" pc [len] output C format\n"
|
||||
" ps [len] print string\n"
|
||||
" pd [len] disassemble N bytes\n"
|
||||
" pr [len] print N raw bytes\n");
|
||||
break;
|
||||
@ -482,10 +495,68 @@ static int cmd_write(void *data, const char *input)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cb_hit(struct r_search_kw_t *kw, void *user, u64 addr)
|
||||
{
|
||||
r_cons_printf("f hit0_%d @ 0x%08llx\n", kw->count, addr);
|
||||
return R_TRUE;
|
||||
}
|
||||
|
||||
static int cmd_search(void *data, const char *input)
|
||||
{
|
||||
//struct r_core_t *core = (struct r_core_t *)data;
|
||||
/* TODO */
|
||||
struct r_core_t *core = (struct r_core_t *)data;
|
||||
u64 at;
|
||||
u32 n32;
|
||||
int ret, dosearch = 0;
|
||||
u8 *buf;
|
||||
switch (input[0]) {
|
||||
case '/':
|
||||
r_search_initialize(core->search);
|
||||
dosearch = 1;
|
||||
break;
|
||||
case 'v':
|
||||
r_search_free(core->search);
|
||||
core->search = r_search_new(R_SEARCH_KEYWORD);
|
||||
n32 = r_num_math(&core->num, input+1);
|
||||
r_search_kw_add_bin(core->search, &n32, 4, "",0);
|
||||
r_search_initialize(core->search);
|
||||
dosearch = 1;
|
||||
break;
|
||||
case ' ': /* search string */
|
||||
r_search_free(core->search);
|
||||
core->search = r_search_new(R_SEARCH_KEYWORD);
|
||||
r_search_kw_add(core->search, input+1, "");
|
||||
r_search_initialize(core->search);
|
||||
dosearch = 1;
|
||||
break;
|
||||
case 'x': /* search hex */
|
||||
r_search_free(core->search);
|
||||
core->search = r_search_new(R_SEARCH_KEYWORD);
|
||||
r_search_kw_add_hex(core->search, input+2, "");
|
||||
r_search_initialize(core->search);
|
||||
dosearch = 1;
|
||||
break;
|
||||
default:
|
||||
r_cons_printf("Usage: /[x/] [arg]\n"
|
||||
" / foo ; search for string 'foo'\n"
|
||||
" /x ff0033 ; search for hex string\n"
|
||||
" // ; repeat last search\n");
|
||||
break;
|
||||
}
|
||||
if (dosearch) {
|
||||
/* set callback */
|
||||
/* TODO: handle last block of data */
|
||||
/* TODO: handle ^C */
|
||||
/* TODO: launch search in background support */
|
||||
buf = (u8 *)malloc(core->blocksize);
|
||||
r_search_set_callback(core->search, &__cb_hit, &core);
|
||||
for(at = core->seek; at < core->file->size; at += core->blocksize) {
|
||||
r_io_lseek(&core->io, core->file->fd, at, R_IO_SEEK_SET);
|
||||
ret = r_io_read(&core->io, core->file->fd, buf, core->blocksize);
|
||||
if (ret != core->blocksize)
|
||||
break;
|
||||
r_search_update(core->search, &at, buf, ret);
|
||||
}
|
||||
}
|
||||
return R_TRUE;
|
||||
}
|
||||
|
||||
@ -567,7 +638,7 @@ static int cmd_hash(void *data, const char *input)
|
||||
static int cmd_visual(void *data, const char *input)
|
||||
{
|
||||
struct r_core_t *core = (struct r_core_t *)data;
|
||||
r_core_visual(core);
|
||||
r_core_visual(core, input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,7 @@ int r_core_init(struct r_core_t *core)
|
||||
core->num.userptr = core;
|
||||
r_lang_init(&core->lang);
|
||||
r_cons_init();
|
||||
core->search = r_search_new(R_SEARCH_KEYWORD);
|
||||
r_io_init(&core->io);
|
||||
r_macro_init(&core->macro);
|
||||
core->macro.num = &core->num;
|
||||
|
@ -28,6 +28,7 @@ struct r_core_file_t *r_core_file_open(struct r_core_t *r, const char *file, int
|
||||
fh->filename = p+3;
|
||||
fh->rwx = mode;
|
||||
r->file = fh;
|
||||
fh->size = r_io_size(&r->io, fd);
|
||||
r_core_block_read(r, 0);
|
||||
list_add(&(fh->list), &r->files);
|
||||
return fh;
|
||||
|
@ -1,6 +1,6 @@
|
||||
OBJ=radare2.o
|
||||
BIN=radare2
|
||||
BINDEPS=r_core r_cons r_macro r_util r_flags r_lib r_io r_hash r_cmd r_config r_asm r_lang r_debug r_print r_line r_bin
|
||||
BINDEPS=r_core r_cons r_macro r_util r_flags r_lib r_io r_hash r_cmd r_config r_asm r_lang r_debug r_print r_line r_bin r_search
|
||||
|
||||
#LIBS=-lr_core -L..
|
||||
### shared
|
||||
|
@ -9,8 +9,9 @@ struct r_core_t r;
|
||||
|
||||
int main_help(int line)
|
||||
{
|
||||
printf("Usage: radare2 [-wn] [-e k=v] [file]\n");
|
||||
printf("Usage: radare2 [-dwn] [-e k=v] [file] [...]\n");
|
||||
if (!line) printf(
|
||||
" -d use 'file' as a program to debug\n"
|
||||
" -w open file in write mode\n"
|
||||
" -n do not run ~/.radarerc\n"
|
||||
" -e k=v evaluate config var\n");
|
||||
@ -22,16 +23,20 @@ int main(int argc, char **argv)
|
||||
struct r_core_file_t *fh;
|
||||
int c, perms = R_IO_READ;
|
||||
int run_rc = 1;
|
||||
int debug = 0;
|
||||
|
||||
if (argc<2)
|
||||
return main_help(1);
|
||||
|
||||
r_core_init(&r);
|
||||
|
||||
while((c = getopt(argc, argv, "when"))!=-1) {
|
||||
while((c = getopt(argc, argv, "whend"))!=-1) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
return main_help(0);
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'e':
|
||||
r_config_eval(&r.config, optarg);
|
||||
break;
|
||||
@ -46,6 +51,33 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
char file[1024];
|
||||
|
||||
strcpy(file, "dbg://");
|
||||
if (optind<argc) {
|
||||
char *ptr = r_file_path(argv[optind]);
|
||||
if (ptr) {
|
||||
strcat(file, ptr);
|
||||
free(ptr);
|
||||
optind++;
|
||||
}
|
||||
}
|
||||
while (optind<argc) {
|
||||
strcat(file, argv[optind]);
|
||||
strcat(file, " ");
|
||||
optind++;
|
||||
if (optind!=argc)
|
||||
strcat(file, " ");
|
||||
}
|
||||
fh = r_core_file_open(&r, file, perms);
|
||||
if (fh == NULL) {
|
||||
fprintf(stderr,
|
||||
"Cannot open file '%s'\n", file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
} else
|
||||
while (optind<argc) {
|
||||
const char *file = argv[optind];
|
||||
fh = r_core_file_open(&r, argv[optind++], perms);
|
||||
|
@ -2,10 +2,114 @@
|
||||
|
||||
#include "r_core.h"
|
||||
|
||||
int r_core_visual(struct r_core_t *core)
|
||||
static int cursor = 0;
|
||||
static int flags = R_PRINT_FLAGS_ADDRMOD;
|
||||
static int printidx = 0;
|
||||
|
||||
/* TODO: use r_cmd here in core->vcmd..optimize over 255 table */
|
||||
int r_core_visual_cmd(struct r_core_t *core, int ch)
|
||||
{
|
||||
/* TODO */
|
||||
fprintf(stderr,
|
||||
"Sorry, no visual mode yet for radare2\n");
|
||||
char buf[1024];
|
||||
|
||||
switch(ch) {
|
||||
case 'c':
|
||||
cursor ^= 1;
|
||||
if (cursor) flags|=R_PRINT_FLAGS_CURSOR;
|
||||
else flags &= !(flags&R_PRINT_FLAGS_CURSOR);
|
||||
r_print_set_flags(flags);
|
||||
break;
|
||||
case 'C':
|
||||
cursor ^= 1;
|
||||
if (cursor) flags|=R_PRINT_FLAGS_COLOR;
|
||||
else flags &= !(flags&R_PRINT_FLAGS_COLOR);
|
||||
r_print_set_flags(flags);
|
||||
break;
|
||||
case 'H':
|
||||
r_core_cmd(core, "s- 2", 0);
|
||||
break;
|
||||
case 'L':
|
||||
r_core_cmd(core, "s+ 2", 0);
|
||||
break;
|
||||
case 'h':
|
||||
r_core_cmd(core, "s- 1", 0);
|
||||
break;
|
||||
case 'l':
|
||||
r_core_cmd(core, "s+ 1", 0);
|
||||
break;
|
||||
case 'j':
|
||||
r_core_cmd(core, "s+ 16", 0);
|
||||
break;
|
||||
case 'k':
|
||||
r_core_cmd(core, "s- 16", 0);
|
||||
break;
|
||||
case 'J':
|
||||
r_core_cmd(core, "s++", 0);
|
||||
break;
|
||||
case 'K':
|
||||
r_core_cmd(core, "s--", 0);
|
||||
break;
|
||||
case 'p':
|
||||
printidx++;
|
||||
break;
|
||||
case 'P':
|
||||
printidx--;
|
||||
break;
|
||||
case '-':
|
||||
r_core_block_size( core, core->blocksize-1);
|
||||
break;
|
||||
case '+':
|
||||
r_core_block_size( core, core->blocksize+1);
|
||||
break;
|
||||
case '/':
|
||||
r_core_block_size( core, core->blocksize-=16);
|
||||
break;
|
||||
case '*':
|
||||
r_core_block_size( core, core->blocksize+=16);
|
||||
break;
|
||||
case ':':
|
||||
r_cons_fgets(buf, 1023, 0, NULL);
|
||||
r_core_cmd(core, buf, 0);
|
||||
break;
|
||||
case '?':
|
||||
r_cons_clear00();
|
||||
r_cons_printf(
|
||||
"\nVisual mode help:\n\n"
|
||||
" hjkl - move around\n"
|
||||
" HJKL - move around faster\n"
|
||||
" P||p - rotate print modes\n"
|
||||
" /*+- - change block size\n"
|
||||
" :cmd - run radare command\n"
|
||||
" q - back to radare shell\n");
|
||||
r_cons_flush();
|
||||
r_cons_any_key();
|
||||
break;
|
||||
case 'q':
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int r_core_visual(struct r_core_t *core, const char *input)
|
||||
{
|
||||
int ch;
|
||||
char *printfmt[] = { "x", "pd", "p8", "pc", "ps" };
|
||||
|
||||
while(input[0]) {
|
||||
if (!r_core_visual_cmd(core, input[0])) {
|
||||
r_cons_clear00();
|
||||
r_core_cmd(core, printfmt[printidx%5], 0);
|
||||
r_cons_flush();
|
||||
r_cons_any_key();
|
||||
return 0;
|
||||
}
|
||||
input = input + 1;
|
||||
}
|
||||
|
||||
do {
|
||||
r_cons_clear00();
|
||||
r_core_cmd(core, printfmt[printidx%5], 0);
|
||||
r_cons_flush();
|
||||
ch = r_cons_readchar();
|
||||
} while (r_core_visual_cmd(core, ch));
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "r_line.h"
|
||||
#include "r_print.h"
|
||||
#include "r_macro.h"
|
||||
#include "r_search.h"
|
||||
#include "r_debug.h"
|
||||
#include "r_flags.h"
|
||||
#include "r_config.h"
|
||||
@ -43,8 +44,9 @@ struct r_core_t {
|
||||
struct r_lang_t lang;
|
||||
struct r_debug_t dbg;
|
||||
struct r_flag_t flags;
|
||||
struct r_config_t config;
|
||||
struct r_macro_t macro;
|
||||
struct r_config_t config;
|
||||
struct r_search_t *search;
|
||||
};
|
||||
|
||||
int r_core_init(struct r_core_t *core);
|
||||
@ -60,9 +62,11 @@ int r_core_seek(struct r_core_t *core, u64 addr);
|
||||
int r_core_block_read(struct r_core_t *core, int next);
|
||||
int r_core_block_size(struct r_core_t *core, u32 bsize);
|
||||
int r_core_cmd_init(struct r_core_t *core);
|
||||
int r_core_visual(struct r_core_t *core);
|
||||
int r_core_visual(struct r_core_t *core, const char *input);
|
||||
int r_core_visual_cmd(struct r_core_t *core, int ch);
|
||||
|
||||
struct r_core_file_t *r_core_file_open(struct r_core_t *r, const char *file, int mode);
|
||||
int r_core_file_close(struct r_core_t *r, struct r_core_file_t *fh);
|
||||
|
||||
int r_core_write_at(struct r_core_t *core, u64 addr, const u8 *buf, int size);
|
||||
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
#define R_PRINT_FLAGS_COLOR 0x00000001
|
||||
#define R_PRINT_FLAGS_ADDRMOD 0x00000002
|
||||
#define R_PRINT_FLAGS_CURSOR 0x00000003
|
||||
|
||||
extern int r_print_cursor;
|
||||
void r_print_set_flags(int flags);
|
||||
void r_print_addr(u64 addr);
|
||||
void r_print_hexdump(u64 addr, u8 *buf, int len, int step, int columns, int header);
|
||||
|
@ -83,6 +83,7 @@ int r_hex_bin2str(const u8 *in, int len, char *out);
|
||||
int r_hex_to_byte(u8 *val, u8 c);
|
||||
|
||||
/* file */
|
||||
char *r_file_path(const char *bin);
|
||||
char *r_file_slurp(const char *str, u32 *usz);
|
||||
char *r_file_slurp_range(const char *str, u64 off, u64 sz);
|
||||
char *r_file_slurp_random_line(const char *file);
|
||||
|
@ -12,6 +12,8 @@ void r_print_set_flags(int _flags)
|
||||
flags = _flags;
|
||||
}
|
||||
|
||||
int r_print_cursor = 0;
|
||||
|
||||
void r_print_addr(u64 addr)
|
||||
{
|
||||
//config_get_i("cfg.addrmod");
|
||||
@ -24,6 +26,39 @@ void r_print_addr(u64 addr)
|
||||
} else r_cons_printf("0x%08llx%c ", addr, ch);
|
||||
}
|
||||
|
||||
void r_print_byte(int idx, u8 ch)
|
||||
{
|
||||
if (flags & R_PRINT_FLAGS_CURSOR && idx == r_print_cursor)
|
||||
r_cons_printf("[%c]", ch);
|
||||
else r_cons_printf("%c", ch);
|
||||
}
|
||||
|
||||
void r_print_code(u64 addr, u8 *buf, int len, int step, int columns, int header)
|
||||
{
|
||||
int i, w = 0;
|
||||
r_cons_printf("#define _BUFFER_SIZE %d\n", len);
|
||||
r_cons_printf("unsigned char buffer[%d] = {", len);
|
||||
for(i=0;i<len;i++) {
|
||||
if (!(w%columns)) {
|
||||
r_cons_printf("\n ");
|
||||
}
|
||||
r_cons_printf("0x%02x, ", buf[i]);
|
||||
w+=6;
|
||||
}
|
||||
r_cons_printf("}\n");
|
||||
}
|
||||
|
||||
void r_print_string(u64 addr, u8 *buf, int len, int step, int columns, int header)
|
||||
{
|
||||
int i;
|
||||
for(i=0;i<len;i++) {
|
||||
if (IS_PRINTABLE(buf[i]))
|
||||
r_cons_printf("%c", buf[i]);
|
||||
else r_cons_printf("\\x%02x", buf[i]);
|
||||
}
|
||||
r_cons_newline();
|
||||
}
|
||||
|
||||
static const char hex[16] = "0123456789ABCDEF";
|
||||
void r_print_hexdump(u64 addr, u8 *buf, int len, int step, int columns, int header)
|
||||
{
|
||||
|
@ -43,6 +43,9 @@ ${LIBSO}: ${OBJ}
|
||||
${LIBAR}: ${OBJ}
|
||||
${CC_AR} ${OBJ}
|
||||
|
||||
install:
|
||||
cd .. && ${MAKE} install
|
||||
|
||||
clean:
|
||||
-rm -f ${LIBSO} ${LIBAR} ${OBJ} ${BIN} *.so a.out *.a *.exe
|
||||
@if [ -e t/Makefile ]; then (cd t && ${MAKE} clean) ; fi
|
||||
|
@ -18,6 +18,32 @@ int r_file_exist(const char *str)
|
||||
return R_TRUE;
|
||||
}
|
||||
|
||||
char *r_file_path(const char *bin)
|
||||
{
|
||||
char file[1024];
|
||||
char *path_env = getenv("PATH");
|
||||
char *path = NULL;
|
||||
char *str, *ptr;
|
||||
|
||||
if (path_env) {
|
||||
str = path = strdup(path_env);
|
||||
do {
|
||||
ptr = strchr(str, ':');
|
||||
if (ptr) {
|
||||
ptr[0]='\0';
|
||||
snprintf(file, "%s/%s", str, bin);
|
||||
if (r_file_exist(file)) {
|
||||
free(path);
|
||||
return strdup(file);
|
||||
}
|
||||
str = ptr+1;
|
||||
}
|
||||
} while(ptr);
|
||||
} else return strdup(bin);
|
||||
free(path);
|
||||
return strdup(bin);
|
||||
}
|
||||
|
||||
char *r_file_slurp(const char *str, u32 *usz)
|
||||
{
|
||||
char *ret;
|
||||
|
Loading…
Reference in New Issue
Block a user