Add CS command to manage meta-spaces

This commit is contained in:
pancake 2015-06-15 04:19:29 +02:00
parent e58c46c0cc
commit 8d0b8c6b78
12 changed files with 451 additions and 19 deletions

View File

@ -23,7 +23,7 @@ Move around the bytes with h,j,k,l! Arrow keys are neither portable nor efficien
Seek at relative offsets with 's +<offset>' or 's -<offset>'
Invert the block bytes using the 'I' key in visual mode
Switch between print modes using the 'p' and 'P' keys in visual mode
Add comments using the ';' key in visual mode or the 'C' command from the radare2 shell
Add comments using the ';' key in visual mode or the 'CC' command from the radare2 shell
Assemble opcodes with the 'a' and 'A' keys in visual mode, which are bindings to the 'wa' and 'wA' commands
Find expanded AES keys in memory with '/Ca'
Find wide-char strings with the '/w <string>' command

View File

@ -57,6 +57,20 @@ R_API void r_anal_unset_limits(RAnal *anal) {
anal->limit = NULL;
}
static void meta_unset_for(void *user, int idx) {
RSpaces *s = (RSpaces*)user;
RAnal *anal = (RAnal*)s->user;
r_meta_space_unset_for (anal, idx);
}
static int meta_count_for(void *user, int idx) {
int ret;
RSpaces *s = (RSpaces*)user;
RAnal *anal = (RAnal*)s->user;
ret = r_meta_space_count_for (anal, idx);
return ret;
}
R_API RAnal *r_anal_new() {
int i;
RAnalPlugin *static_plugin;
@ -70,6 +84,8 @@ R_API RAnal *r_anal_new() {
anal->sdb = sdb_new0 ();
anal->sdb_fcns = sdb_ns (anal->sdb, "fcns", 1);
anal->sdb_meta = sdb_ns (anal->sdb, "meta", 1);
r_space_init (&anal->meta_spaces,
meta_unset_for, meta_count_for, anal);
anal->sdb_hints = sdb_ns (anal->sdb, "hints", 1);
anal->sdb_xrefs = sdb_ns (anal->sdb, "xrefs", 1);
//anal->sdb_vars = sdb_ns (anal->sdb, "vars", 1); // its inside fcns right now
@ -126,6 +142,7 @@ R_API RAnal *r_anal_free(RAnal *a) {
r_list_free (a->fcns);
// might provoke double frees since this is used in r_anal_fcn_insert()
//r_listrange_free (a->fcnstore);
r_space_fini (&a->meta_spaces);
r_list_free (a->refs);
r_list_free (a->types);
r_reg_free (a->reg);

View File

@ -101,6 +101,7 @@ R_API int r_meta_set_string(RAnal *a, int type, ut64 addr, const char *s) {
char key[100], val[2048], *e_str;
int ret;
ut64 size;
int space_idx = a->meta_spaces.space_idx;
meta_type_add (a, type, addr);
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, addr);
@ -111,7 +112,7 @@ R_API int r_meta_set_string(RAnal *a, int type, ut64 addr, const char *s) {
ret = R_TRUE;
} else ret = R_FALSE;
e_str = sdb_encode ((const void*)s, -1);
snprintf (val, sizeof (val)-1, "%d,%s", (int)size, e_str);
snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)size, space_idx, e_str);
sdb_set (DB, key, val, 0);
free ((void*)e_str);
return ret;
@ -193,8 +194,9 @@ R_API RAnalMetaItem *r_meta_item_new(int type) {
}
R_API int r_meta_add(RAnal *a, int type, ut64 from, ut64 to, const char *str) {
int exists;
int space_idx = a->meta_spaces.space_idx;
char *e_str, key[100], val[2048];
int exists;
if (from>to)
return R_FALSE;
if (from == to)
@ -205,8 +207,9 @@ R_API int r_meta_add(RAnal *a, int type, ut64 from, ut64 to, const char *str) {
/* set entry */
e_str = sdb_encode ((const void*)str, -1);
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, from);
snprintf (val, sizeof (val)-1, "%d,%s", (int)(to-from), e_str);
snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)(to-from), space_idx, e_str);
exists = sdb_exists (DB, key);
sdb_set (DB, key, val, 0);
free (e_str);
@ -250,7 +253,7 @@ R_API RAnalMetaItem *r_meta_find(RAnal *a, ut64 off, int type, int where) {
R_API const char *r_meta_type_to_string(int type) {
// XXX: use type as '%c'
switch(type) {
switch (type) {
case R_META_TYPE_HIDE: return "Ch";
case R_META_TYPE_CODE: return "Cc";
case R_META_TYPE_DATA: return "Cd";
@ -263,15 +266,22 @@ R_API const char *r_meta_type_to_string(int type) {
}
static void printmetaitem(RAnal *a, RAnalMetaItem *d, int rad) {
char *pstr, *str = r_str_escape (d->str);
char *pstr, *str;
//eprintf ("%d %d\n", d->space, a->meta_spaces.space_idx);
if (a->meta_spaces.space_idx != -1) {
if (a->meta_spaces.space_idx != d->space) {
return;
}
}
str = r_str_escape (d->str);
if (str || d->type == 'd') {
if (d->type=='s' && !*str) {
free (str);
return;
}
if (!str)
if (!str) {
pstr = "";
else if (d->type != 'C') {
} else if (d->type != 'C') {
r_name_filter (str, 0);
pstr = str;
} else pstr = d->str;
@ -369,6 +379,8 @@ static void printmetaitem(RAnal *a, RAnalMetaItem *d, int rad) {
}
static int meta_print_item(void *user, const char *k, const char *v) {
// const char *v; // size
const char *v2; // space_idx
RAnalMetaUserItem *ui = user;
RAnalMetaItem it;
if (strlen (k)<8)
@ -378,17 +390,22 @@ static int meta_print_item(void *user, const char *k, const char *v) {
it.type = k[5];
it.size = sdb_atoi (v);
it.from = sdb_atoi (k+7);
v2 = strchr (v, ',');
if (!v2) goto beach;
it.space = atoi (v2+1);
it.to = it.from + it.size;
it.str = strchr (v, ',');
it.str = strchr (v2+1, ',');
if (it.str)
it.str = (char *)sdb_decode ((const char*)it.str+1, 0);
printmetaitem (ui->anal, &it, ui->rad);
beach:
free (it.str);
return 1;
}
R_API int r_meta_list_cb(RAnal *a, int type, int rad, SdbForeachCallback cb, void *user) {
RAnalMetaUserItem ui = { a, type, rad, cb, user };
RAnalMetaUserItem ui = { a, type, rad, cb, user, 0 };
if (rad=='j') a->printf ("[");
if (cb) {
sdb_foreach (DB, cb, &ui);
@ -396,13 +413,120 @@ R_API int r_meta_list_cb(RAnal *a, int type, int rad, SdbForeachCallback cb, voi
sdb_foreach (DB, meta_print_item, &ui);
}
if (rad=='j') a->printf ("]\n");
return 0;
return ui.count;
}
R_API int r_meta_list(RAnal *a, int type, int rad) {
return r_meta_list_cb (a, type, rad, NULL, NULL);
}
static int meta_enumerate_cb(void *user, const char *k, const char *v) {
const char *v2;
RAnalMetaUserItem *ui = user;
RList *list = ui->user;
//RAnal *a = ui->anal;
RAnalMetaItem *it;
if (strlen (k)<8)
return 1;
if (memcmp (k+6, ".0x", 3))
return 1;
it = R_NEW0 (RAnalMetaItem);
it->type = k[5];
it->size = sdb_atoi (v);
it->from = sdb_atoi (k+7);
it->to = it->from + it->size;
v2 = strchr (v, ',');
if (!v2) goto beach;
it->space = atoi (v2+1);
it->str = strchr (v2+1, ',');
if (it->str)
it->str = (char *)sdb_decode ((const char*)it->str+1, 0);
//printmetaitem (ui->anal, &it, ui->rad);
r_list_append (list, it);
beach:
return 1;
}
R_API RList *r_meta_enumerate(RAnal *a, int type) {
RList *list = r_list_new ();
r_meta_list_cb (a, type, 0, meta_enumerate_cb, list);
return list;
}
static int deserialize(RAnalMetaItem *it, const char *k, const char *v) {
const char *v2;
if (strlen (k)<8)
return 1;
if (memcmp (k+6, ".0x", 3))
return 1;
it->type = k[5];
it->size = sdb_atoi (v);
it->from = sdb_atoi (k+7);
it->to = it->from + it->size;
v2 = strchr (v, ',');
if (!v2) goto beach;
it->space = atoi (v2+1);
it->str = strchr (v2+1, ',');
//printmetaitem (ui->anal, &it, ui->rad);
beach:
return 1;
}
static void serialize(RAnalMetaItem *it, char *k, char *v) {
sprintf (k, "meta.%c.0x%"PFMT64x, it->type, it->from);
snprintf (v, 4095, "%d,%d,%s", (int)it->size, it->space, it->str);
}
static int meta_unset_cb(void *user, const char *k, const char *v) {
char nk[128], nv[4096];
RAnalMetaUserItem *ui = user;
RAnal *a = ui->anal;
RAnalMetaItem it;
if (!strstr(k, ".0x"))
return 1;
deserialize (&it, k, v);
if (it.space != -1) {
it.space = -1;
serialize (&it, nk, nv);
sdb_set (DB, nk, nv, 0);
}
return 1;
}
R_API void r_meta_space_unset_for(RAnal *a, int type) {
r_meta_list_cb (a, type, 0, meta_unset_cb, NULL);
}
typedef struct {
int count;
int index;
int ctx;
} myMetaUser;
static int meta_count_cb(void *user, const char *k, const char *v) {
RAnalMetaUserItem *ui = user;
myMetaUser *mu = ui->user;
RAnalMetaItem it;
if (!strstr(k, ".0x"))
return 1;
deserialize (&it, k, v);
if (mu) {
if (it.space == mu->ctx) {
mu->count++;
}
}
return 1;
}
R_API int r_meta_space_count_for(RAnal *a, int ctx) {
myMetaUser mu = {0};
mu.ctx = ctx;
int type = a->meta_spaces.space_idx;
r_meta_list_cb (a, type, 0, meta_count_cb, &mu);
return mu.count;
}
#if 0
R_API char *r_anal_meta_bar (RAnal *anal, ut64 from, ut64 to, int blocks) {
int i, n, blocksize;

View File

@ -1014,7 +1014,7 @@ static int bin_symbols (RCore *r, int mode, ut64 baddr, ut64 laddr, int va, ut64
int i = 0;
symbols = r_bin_get_symbols (r->bin);
r_space_set (&r->anal->meta_spaces, "bin");
if (mode & R_CORE_BIN_JSON) {
r_cons_printf ("[");
r_list_foreach (symbols, iter, symbol) {
@ -1091,7 +1091,6 @@ static int bin_symbols (RCore *r, int mode, ut64 baddr, ut64 laddr, int va, ut64
// set the new sym.[cname].[name] with comment
snprintf (str, R_FLAG_NAME_SIZE, "sym.%s.%s", cname, name);
fi = r_flag_set (r->flags, str, addr, symbol->size, 0);
if (comment) {
r_flag_item_set_comment (fi, comment);
free (comment);
@ -1196,6 +1195,7 @@ static int bin_symbols (RCore *r, int mode, ut64 baddr, ut64 laddr, int va, ut64
}
if (!at && !mode) r_cons_printf ("\n%i symbols\n", i);
}
r_space_set (&r->anal->meta_spaces, NULL);
return R_TRUE;
}

View File

@ -548,6 +548,7 @@ static int cmd_meta(void *data, const char *input) {
"C*", "", "list meta info in r2 commands",
"C-", " [len] [[@]addr]", "delete metadata at given address range",
"CL", "[-][*] [file:line] [addr]", "show or add 'code line' information (bininfo)",
"CS", "[-][space]", "manage meta-spaces to filter comments, etc..",
"CC", "[-] [comment-text] [@addr]", "add/remove comment",
"CC!", " [@addr]", "edit comment with $EDITOR",
"CCa", "[-at]|[at] [text] [@addr]", "add/remove comment at given address",
@ -567,6 +568,80 @@ static int cmd_meta(void *data, const char *input) {
if (f) r_anal_str_to_fcn (core->anal, f, input+2);
else eprintf ("Cannot find function here\n");
break;
case 'S':
{
RSpaces *ms = &core->anal->meta_spaces;
/** copypasta from `fs`.. this must be refactorized to be shared */
switch (input[1]) {
case '?':
{
const char *help_msg[] = {
"Usage: CS","[*] [+-][metaspace|addr]", " # Manage metaspaces",
"CS","","display metaspaces",
"CS"," *","select all metaspaces",
"CS"," metaspace","select metaspace or create if it doesn't exist",
"CS","-metaspace","remove metaspace",
"CS","-*","remove all metaspaces",
"CS","+foo","push previous metaspace and set",
"CS","-","pop to the previous metaspace",
// "CSm"," [addr]","move metas at given address to the current metaspace",
"CSr"," newname","rename selected metaspace",
NULL};
r_core_cmd_help (core, help_msg);
}
break;
case '+':
r_space_push (ms, input+2);
break;
case 'r':
if (input[2]==' ')
r_space_rename (ms, NULL, input+2);
else eprintf ("Usage: CSr [newname]\n");
break;
case '-':
if (input[2]) {
if (input[2]=='*') {
r_space_unset (ms, NULL);
} else {
r_space_unset (ms, input+2);
}
} else {
r_space_pop (ms);
}
break;
case 'j':
case '\0':
case '*':
r_space_list (ms, input[1]);
break;
case ' ':
r_space_set (ms, input+2);
break;
#if 0
case 'm':
{ RFlagItem *f;
ut64 off = core->offset;
if (input[2] == ' ')
off = r_num_math (core->num, input+2);
f = r_flag_get_i (core->flags, off);
if (f) {
f->space = core->flags->space_idx;
} else eprintf ("Cannot find any flag at 0x%"PFMT64x".\n", off);
}
break;
#endif
default: {
int i, j = 0;
for (i=0; i<R_FLAG_SPACES_MAX; i++) {
if (ms->spaces[i])
r_cons_printf ("%02d %c %s\n", j++,
(i==ms->space_idx)?'*':' ',
ms->spaces[i]);
}
} break;
}
}
break;
}
return R_TRUE;
}

View File

@ -872,6 +872,7 @@ R_API int r_core_init(RCore *core) {
core->assembler->num = core->num;
r_asm_set_user_ptr (core->assembler, core);
core->anal = r_anal_new ();
core->anal->meta_spaces.printf = r_cons_printf;
core->anal->cb.on_fcn_new = on_fcn_new;
core->anal->cb.on_fcn_delete = on_fcn_delete;
core->anal->cb.on_fcn_rename = on_fcn_rename;

View File

@ -14,7 +14,7 @@ R_API int r_flag_space_get(RFlag *f, const char *name) {
}
R_API const char *r_flag_space_get_i (RFlag *f, int idx) {
if (idx==-1 || idx>255 || !f || !f->spaces[idx] || !*f->spaces[idx])
if (idx==-1 || idx>=R_FLAG_SPACES_MAX || !f || !f->spaces[idx] || !*f->spaces[idx])
return "";
return f->spaces[idx];
}

View File

@ -49,6 +49,7 @@ typedef struct r_anal_meta_item_t {
ut64 size;
int type;
char *str;
int space;
} RAnalMetaItem;
typedef struct {
@ -57,6 +58,7 @@ typedef struct {
int rad;
SdbForeachCallback cb;
void *user;
int count;
} RAnalMetaUserItem;
typedef struct r_anal_range_t {
@ -597,6 +599,7 @@ typedef struct r_anal_t {
Sdb *sdb_xrefs;
Sdb *sdb_types;
Sdb *sdb_meta; // TODO: Future r_meta api
RSpaces meta_spaces;
PrintfCallback printf;
//moved from RAnalFcn
Sdb *sdb; // root
@ -1346,6 +1349,9 @@ R_API void r_anal_data_free (RAnalData *d);
R_API char *r_anal_data_to_string (RAnalData *d);
R_API void r_meta_free(RAnal *m);
R_API void r_meta_space_unset_for(RAnal *a, int type);
R_API int r_meta_space_count_for(RAnal *a, int ctx);
R_API RList *r_meta_enumerate(RAnal *a, int type);
R_API int r_meta_count(RAnal *m, int type, ut64 from, ut64 to);
R_API char *r_meta_get_string(RAnal *m, int type, ut64 addr);
R_API int r_meta_set_string(RAnal *m, int type, ut64 addr, const char *s);
@ -1354,6 +1360,7 @@ R_API int r_meta_add(RAnal *m, int type, ut64 from, ut64 size, const char *str);
R_API RAnalMetaItem *r_meta_find(RAnal *m, ut64 off, int type, int where);
R_API int r_meta_cleanup(RAnal *m, ut64 from, ut64 to);
R_API const char *r_meta_type_to_string(int type);
R_API RList *r_meta_enumerate(RAnal *a, int type);
R_API int r_meta_list(RAnal *m, int type, int rad);
R_API int r_meta_list_cb(RAnal *m, int type, int rad, SdbForeachCallback cb, void *user);
R_API void r_meta_item_free(void *_item);

View File

@ -53,6 +53,18 @@ R_REFCTR_REF (foo)
R_REFCTR_UNREF (foo)
#endif
#define R_SPACES_MAX 512
typedef struct r_space_t {
int space_idx;
int space_idx2;
char *spaces[R_SPACES_MAX];
RList *spacestack; // metaspaces
PrintfCallback printf;
void (*unset_for)(void *user, int idx);
int (*count_for)(void *user, int idx);
void *user;
} RSpaces;
/* empty classes */
typedef struct { } RSystem;
@ -763,6 +775,19 @@ R_API char **r_sys_get_environ (void);
R_API void r_sys_set_environ (char **e);
/* spaces */
R_API void r_space_init(RSpaces *f, void (*unset_for)(void*, int), int (*count_for)(void *,int), void *user);
R_API void r_space_fini(RSpaces *f);
R_API int r_space_get(RSpaces *f, const char *name);
R_API const char *r_space_get_i (RSpaces *f, int idx);
R_API int r_space_push(RSpaces *f, const char *name);
R_API int r_space_pop(RSpaces *f);
R_API int r_space_set(RSpaces *f, const char *name);
R_API int r_space_unset (RSpaces *f, const char *fs);
R_API int r_space_list(RSpaces *f, int mode);
R_API int r_space_rename (RSpaces *f, const char *oname, const char *nname);
/* Some "secured" functions, to do basic operation (mul, sub, add...) on integers */
static inline int UT64_ADD(ut64 *r, ut64 a, ut64 b) {
if(UT64_MAX - a < b)

View File

@ -10,7 +10,7 @@ OBJS+=regex/regcomp.o regex/regerror.o regex/regexec.o uleb128.o
OBJS+=sandbox.o calc.o thread.o thread_lock.o thread_msg.o
OBJS+=strpool.o bitmap.o strht.o p_date.o p_format.o print.o
OBJS+=p_seven.o slist.o randomart.o log.o zip.o debruijn.o
OBJS+=utf8.o strbuf.o lib.o name.o
OBJS+=utf8.o strbuf.o lib.o name.o spaces.o
OBJS+=diff.o bdiff.o stack.o queue.o tree.o
# DO NOT BUILD r_big api (not yet used and its buggy)

181
libr/util/spaces.c Normal file
View File

@ -0,0 +1,181 @@
/* radare - LGPL - Copyright 2015 - pancake */
#include <r_anal.h>
R_API int r_space_get(RSpaces *f, const char *name) {
int i;
for (i=0; i<R_SPACES_MAX; i++) {
if (f->spaces[i] != NULL)
if (!strcmp (name, f->spaces[i]))
return i;
}
return -1;
}
R_API const char *r_space_get_i (RSpaces *f, int idx) {
if (idx==-1 || idx>=R_SPACES_MAX|| !f || !f->spaces[idx] || !*f->spaces[idx])
return "";
return f->spaces[idx];
}
R_API void r_space_init(RSpaces *f, void (*unset_for)(void*,int), int (*count_for)(void*,int), void *user) {
int i;
f->space_idx = -1;
f->space_idx2 = -1;
f->spacestack = r_list_new ();
f->printf = (PrintfCallback)printf;
f->unset_for = unset_for;
f->count_for = count_for;
f->user = user;
for (i=0;i<R_SPACES_MAX;i++)
f->spaces[i] = NULL;
}
R_API void r_space_fini(RSpaces *f) {
int i;
for (i=0;i<R_SPACES_MAX;i++) {
free (f->spaces[i]);
f->spaces[i] = NULL;
}
r_list_free (f->spacestack);
}
R_API int r_space_push(RSpaces *f, const char *name) {
int ret = R_FALSE;
if (name && *name) {
if (f->space_idx != -1 && f->spaces[f->space_idx]) {
r_list_push (f->spacestack, f->spaces[f->space_idx]);
} else {
r_list_push (f->spacestack, "*");
}
r_space_set (f, name);
ret = R_TRUE;
}
return ret;
}
R_API int r_space_pop(RSpaces *f) {
char *p = r_list_pop (f->spacestack);
if (p) {
if (*p) {
r_space_set (f, p);
}
return R_TRUE;
}
return R_FALSE;
}
R_API int r_space_set(RSpaces *f, const char *name) {
int i;
if (name == NULL || *name == '*') {
f->space_idx = -1;
return f->space_idx;
}
for (i=0; i<R_SPACES_MAX; i++) {
if (f->spaces[i] != NULL)
if (!strcmp (name, f->spaces[i])) {
f->space_idx = i;
return f->space_idx;
}
}
/* not found */
for (i=0; i<R_SPACES_MAX; i++) {
if (f->spaces[i] == NULL) {
f->spaces[i] = strdup (name);
f->space_idx = i;
break;
}
}
return f->space_idx;
}
R_API int r_space_unset (RSpaces *f, const char *fs) {
int i, count = 0;
if (!fs) {
return r_space_set (f, NULL);
}
for (i=0; i<R_SPACES_MAX; i++) {
if (!f->spaces[i]) continue;
if (!fs || !strcmp (fs, f->spaces[i])) {
if (f->space_idx == i) {
f->space_idx = -1;
}
if (f->space_idx2 == i) {
f->space_idx2 = -1;
}
R_FREE (f->spaces[i]);
if (f->unset_for) {
f->unset_for (f, i);
}
count++;
}
}
return count;
}
static int r_space_count (RSpaces *f, int n) {
if (f->count_for) {
return f->count_for (f, n);
}
return 0;
}
R_API int r_space_list(RSpaces *f, int mode) {
const char *defspace = NULL;
int count, len, i, j = 0;
if (mode == 'j')
f->printf ("[");
for (i=0; i<R_SPACES_MAX; i++) {
if (!f->spaces[i]) continue;
count = r_space_count (f, i);
if (mode=='j') {
f->printf ("%s{\"name\":\"%s\"%s,\"count\":%d}",
j? ",":"", f->spaces[i],
(i==f->space_idx)? ",\"selected\":true":"",
count);
} else if (mode=='*') {
f->printf ("fs %s\n", f->spaces[i]);
if (i==f->space_idx) defspace = f->spaces[i];
} else {
#define INDENT 5
char num0[64], num1[64], spaces[32];
snprintf (num0, sizeof (num0), "%d", i);
snprintf (num1, sizeof (num1), "%d", count);
memset (spaces, ' ', sizeof (spaces));
len = strlen (num0) + strlen (num1);
if (len<INDENT) {
spaces[INDENT-len] = 0;
} else spaces[0] = 0;
f->printf ("%s%s %s %c %s\n", num0, spaces, num1,
(i==f->space_idx)?'*':'.',
f->spaces[i]);
}
j++;
}
if (defspace)
f->printf ("fs %s # current\n", defspace);
if (mode == 'j')
f->printf ("]\n");
return j;
}
R_API int r_space_rename (RSpaces *f, const char *oname, const char *nname) {
int i;
if (!oname) {
if (f->space_idx == -1)
return R_FALSE;
oname = f->spaces[f->space_idx];
}
if (!nname) return R_FALSE;
while (*oname==' ') oname++;
while (*nname==' ') nname++;
for (i=0; i<R_SPACES_MAX; i++) {
if (f->spaces[i] && !strcmp (oname, f->spaces[i])) {
free (f->spaces[i]);
f->spaces[i] = strdup (nname);
return R_TRUE;
}
}
return R_FALSE;
}

View File

@ -488,12 +488,14 @@ R_API char *r_sys_cmd_str(const char *cmd, const char *input, int *len) {
R_API int r_sys_rmkdir(const char *dir) {
int ret = R_TRUE;
const char slash = R_SYS_DIR[0];
char *path = strdup (dir), *ptr = path, *p;
char *path = strdup (dir), *ptr = path;
if (*ptr==slash) ptr++;
#if __WINDOWS__
p = strstr (ptr, ":\\");
if (p) {
ptr = p + 2;
{
char *p = strstr (ptr, ":\\");
if (p) {
ptr = p + 2;
}
}
#endif
while ((ptr = strchr (ptr, slash))) {