radare2/libr/util/charset.c

348 lines
7.4 KiB
C

/* radare - LGPL - Copyright 2020-2022 - gogo, pancake */
#include <r_util.h>
#include <config.h>
#define USE_RUNES 0
#if HAVE_GPERF
extern SdbGperf gperf_ascii;
extern SdbGperf gperf_ebcdic37;
extern SdbGperf gperf_hiragana;
extern SdbGperf gperf_iso8859_1;
extern SdbGperf gperf_katakana;
extern SdbGperf gperf_pokered;
static const SdbGperf * const gperfs[] = {
&gperf_ascii,
&gperf_ebcdic37,
&gperf_hiragana,
&gperf_iso8859_1,
&gperf_katakana,
&gperf_pokered,
NULL
};
R_API SdbGperf *r_charset_get_gperf(const char *k) {
SdbGperf **gp = (SdbGperf**)gperfs;
while (*gp) {
SdbGperf *g = *gp;
if (!strcmp (k, g->name)) {
return *gp;
}
gp++;
}
return NULL;
}
#else
R_API SdbGperf *r_charset_get_gperf(const char *k) {
return NULL;
}
#endif
R_API RList *r_charset_list(RCharset *ch) {
RList *list = r_list_newf (free);
#if HAVE_GPERF
SdbGperf **gp = (SdbGperf**)gperfs;
while (*gp) {
SdbGperf *g = *gp;
r_list_append (list, strdup (g->name));
gp++;
}
#endif
// iterate in disk
const char *cs = R2_PREFIX R_SYS_DIR R2_SDB R_SYS_DIR "charsets" R_SYS_DIR;
RList *files = r_sys_dir (cs);
RListIter *iter;
char *file;
r_list_foreach (files, iter, file) {
char *dot = strstr (file, ".sdb");
if (dot) {
*dot = 0;
r_list_append (list, strdup (file));
}
}
r_list_free (files);
return list;
}
R_API RCharset *r_charset_new(void) {
RCharset *ch = R_NEW0 (RCharset);
if (ch) {
ch->db = sdb_new0 ();
ch->db_char_to_hex = sdb_new0 ();
}
return ch;
}
R_API void r_charset_free(RCharset *c) {
if (c) {
sdb_free (c->db);
sdb_free (c->db_char_to_hex);
free (c);
}
}
R_API void r_charset_close(RCharset *c) {
R_RETURN_IF_FAIL (c);
c->loaded = false;
}
R_API bool r_charset_use(RCharset *c, const char *cf) {
R_RETURN_VAL_IF_FAIL (c && cf, false);
bool rc = false;
SdbGperf *gp = r_charset_get_gperf (cf);
if (gp) {
sdb_free (c->db);
c->db = sdb_new0 ();
if (sdb_open_gperf (c->db, gp) != -1) {
rc = r_charset_open (c, NULL);
r_sys_setenv ("RABIN2_CHARSET", cf);
rc = true;
}
} else {
const char *cs = R2_PREFIX R_SYS_DIR R2_SDB R_SYS_DIR "charsets" R_SYS_DIR;
char *syscs = r_str_newf ("%s%s.sdb", cs, cf);
if (r_file_exists (syscs)) {
rc = r_charset_open (c, syscs);
r_sys_setenv ("RABIN2_CHARSET", cf);
} else {
if (r_file_exists (cf)) {
rc = r_charset_open (c, cf);
r_sys_setenv ("RABIN2_CHARSET", cf);
}
}
free (syscs);
}
return rc;
}
R_API bool r_charset_open(RCharset *c, const char *cs) {
R_RETURN_VAL_IF_FAIL (c, false);
if (cs) {
sdb_reset (c->db);
sdb_open (c->db, cs);
sdb_free (c->db_char_to_hex);
c->db_char_to_hex = sdb_new0 ();
}
SdbListIter *iter;
SdbKv *kv;
SdbList *sdbls = sdb_foreach_list (c->db, true);
c->loaded = false;
ls_foreach (sdbls, iter, kv) {
const char *new_key = kv->base.value;
const char *new_value = kv->base.key;
const size_t key_len = strlen (new_key);
const size_t val_len = strlen (new_value);
if (key_len > c->encode_maxkeylen) {
c->encode_maxkeylen = key_len;
}
if (val_len > c->decode_maxkeylen) {
c->decode_maxkeylen = val_len;
}
sdb_add (c->db_char_to_hex, new_key, new_value, 0);
c->loaded = true;
}
ls_free (sdbls);
return true;
}
// rune
R_API RCharsetRune *r_charset_rune_new(const ut8 *ch, const ut8 *hx) {
R_RETURN_VAL_IF_FAIL (ch && hx, NULL);
RCharsetRune* c = R_NEW0 (RCharsetRune);
if (!c) {
return NULL;
}
c->ch = (ut8 *) strdup ((char *) ch);
c->hx = (ut8 *) strdup ((char *) hx);
c->left = NULL;
c->right = NULL;
return c;
}
R_API void r_charset_rune_free(RCharsetRune *c) {
if (c) {
free (c->ch);
free (c->hx);
free (c);
}
}
#if 0
R_API RCharsetRune *add_rune(RCharsetRune *r, const ut8 *ch, const ut8 *hx) {
if (!r) {
r = r_charset_rune_new (ch, hx);
}
int cmp = strcmp ((char *)hx, (char *)r->hx);
if (cmp < 0) {
r->left = add_rune (r->left, ch, hx);
} else if (cmp > 0) {
r->right = add_rune (r->right, ch, hx);
} else {
int cmp = strcmp ((char *)ch, (char *)r->ch);
if (cmp > 0) {
r->left = add_rune (r->left, ch, hx);
} else if (cmp < 0) {
r->right = add_rune (r->right, ch, hx);
}
}
return r;
}
R_API RCharsetRune *search_from_hex(RCharsetRune *r, const ut8 *hx) {
if (!r) {
return NULL;
}
if (!strcmp ((char *)r->hx, (char *)hx)) {
return r;
}
RCharsetRune *left = search_from_hex (r->left, hx);
return left? left: search_from_hex (r->right, hx);
}
#endif
#if R2_USE_NEW_ABI
R_API size_t r_charset_encode_str(RCharset *rc, ut8 *out, size_t out_len, const ut8 *in, size_t in_len, bool early_exit) {
#else
R_API size_t r_charset_encode_str(RCharset *rc, ut8 *out, size_t out_len, const ut8 *in, size_t in_len) {
#endif
if (!rc->loaded) {
return in_len;
}
char k[32];
char *o = (char*)out;
size_t i, oi;
char *o_end = o + out_len;
bool fine = false;
for (i = oi = 0; i < in_len && o < o_end; i++) {
ut8 ch_in = in[i];
snprintf (k, sizeof (k), "0x%02x", ch_in);
const char *v = sdb_const_get (rc->db, k, 0);
#if R2_USE_NEW_ABI
if (!v && early_exit) {
break;
}
#endif
const char *ret = r_str_get_fail (v, "?");
char *res = strdup (ret);
if (res) {
size_t reslen = strlen (res);
if (reslen >= o_end - o) {
break;
}
fine = true;
r_str_unescape (res);
// memcpy (o, res, out_len - i);
r_str_ncpy (o, res, out_len - oi);
free (res);
}
size_t di = strlen (o);
oi += di;
o += di;
}
if (!fine) {
return 0;
}
return o - (char*)out;
}
// assumes out is as big as in_len
R_API size_t r_charset_decode_str(RCharset *rc, ut8 *out, size_t out_len, const ut8 *in, size_t in_len) {
if (!rc->loaded) {
return in_len;
}
char *o = (char*)out;
size_t maxkeylen = rc->encode_maxkeylen;
size_t cur, j;
for (cur = 0; cur < in_len; cur++) {
size_t left = in_len - cur;
size_t toread = R_MIN (left + 1, maxkeylen);
char *str = calloc (toread + 128, 1);
if (!str) {
break;
}
memcpy (str, in + cur, toread);
bool found = false;
for (j = toread; cur < in_len && j > 0; j--) {
left = in_len - cur + 1;
toread = R_MIN (left, maxkeylen);
str[j] = 0;
const char *v = sdb_const_get (rc->db_char_to_hex, (char *) str, 0);
if (v) {
int repeat = !strncmp (v, "0x", 2)? strlen (v + 2) / 2: 1;
ut64 nv = r_num_get (NULL, v);
if (!nv) {
int i;
// write 0x00 N times (
for (i = 0; i < repeat; i++) {
// write null byte
memcpy (o, "\x00", 2);
o++;
}
o--;
found = true;
break;
}
//convert to ascii
char *str_hx = malloc (1 + maxkeylen);
if (!str_hx) {
break;
}
if (nv > 0xff) {
ut64 d = 0;
r_mem_swapendian ((ut8*)&d, (const ut8*)&nv, 8);
nv = d;
}
int i;
bool skip = true;
int chcount = 0;
for (i = 0; i < 8; i++) {
ut8 bv = nv & 0xff;
// skip until we found one byet
if (bv & 0xff) {
skip = false;
}
if (skip) {
nv >>= 8;
continue;
} else if (!bv) {
break;
}
// eprintf ("-> 0x%02x\n", nv & 0xff);
//in the future handle multiple chars output
str_hx[0] = bv;
str_hx[1] = 0;
const char *ret = r_str_get_fail (str_hx, "?");
// concatenate
const size_t ll = R_MIN (left, strlen (ret) + 1);
if (ll > 0) {
memcpy (o, ret, ll);
o[ll] = 0;
o += ll - 1;
chcount++;
}
found = true;
nv >>= 8;
}
cur += (chcount>1)?chcount - 2:j-1;
free (str_hx);
break;
}
}
if (!found) {
o[0] = '?';
o[1] = 0;
o++;
}
free (str);
}
return o - (char*)out;
}