radare2/libr/util/strbuf.c
pancake 66edf036e6 Fix large offset passed to the pascal demangler ##crash
* Causes a negative memcpy, but it's not detected because size_t
2024-03-25 18:01:15 +01:00

429 lines
8.8 KiB
C

/* radare - LGPL - Copyright 2013-2024 - pancake */
#include <r_util.h>
R_API RStrBuf *r_strbuf_new(const char *str) {
RStrBuf *s = R_NEW0 (RStrBuf);
if (s && str) {
r_strbuf_set (s, str);
}
return s;
}
R_API RStrBuf *r_strbuf_newf(const char *fmt, ...) {
r_return_val_if_fail (fmt, NULL);
RStrBuf *sb = R_NEW0 (RStrBuf);
if (sb) {
va_list ap;
va_start (ap, fmt);
R_UNUSED_RESULT (r_strbuf_vappendf (sb, fmt, ap));
va_end (ap);
}
return sb;
}
R_API bool r_strbuf_equals(RStrBuf *sa, RStrBuf *sb) {
r_return_val_if_fail (sa && sb, false);
if (sa->len != sb->len) {
return false;
}
return strcmp (r_strbuf_get (sa), r_strbuf_get (sb)) == 0;
}
R_API bool r_strbuf_is_empty(RStrBuf *sb) {
return sb->len == 0;
}
R_API int r_strbuf_length(RStrBuf *sb) {
r_return_val_if_fail (sb, 0);
return sb->len;
}
R_API void r_strbuf_init(RStrBuf *sb) {
r_return_if_fail (sb);
memset (sb, 0, sizeof (RStrBuf));
}
R_API const char *r_strbuf_initf(RStrBuf *sb, const char *fmt, ...) {
r_return_val_if_fail (sb && fmt, NULL);
r_strbuf_init (sb);
va_list ap;
va_start (ap, fmt);
const char *r = r_strbuf_vsetf (sb, fmt, ap);
va_end (ap);
return r;
}
R_API bool r_strbuf_copy(RStrBuf *dst, RStrBuf *src) {
r_return_val_if_fail (dst && src, false);
if (src->ptr) {
char *p = malloc (src->ptrlen);
if (!p) {
return false;
}
memcpy (p, src->ptr, src->ptrlen);
free (dst->ptr);
dst->ptr = p;
dst->ptrlen = src->ptrlen;
} else {
R_FREE (dst->ptr);
memcpy (dst->buf, src->buf, sizeof (dst->buf));
}
dst->len = src->len;
return true;
}
R_API bool r_strbuf_reserve(RStrBuf *sb, size_t len) {
r_return_val_if_fail (sb, false);
if ((sb->ptr && len < sb->ptrlen) || (!sb->ptr && len < sizeof (sb->buf))) {
return true;
}
char *newptr = realloc (sb->ptr, len + 1);
if (!newptr) {
return false;
}
if (!sb->ptr) {
memcpy (newptr, sb->buf, sizeof (sb->buf));
}
sb->ptr = newptr;
sb->ptrlen = len + 1;
return true;
}
R_API bool r_strbuf_setbin(RStrBuf *sb, const ut8 *s, size_t l) {
r_return_val_if_fail (sb && s, false);
if (l > ST32_MAX) {
return false;
}
if (l >= sizeof (sb->buf)) {
char *ptr = sb->ptr;
if (!ptr || l + 1 > sb->ptrlen) {
ptr = malloc (l + 1);
if (!ptr) {
return false;
}
R_FREE (sb->ptr);
sb->ptrlen = l + 1;
sb->ptr = ptr;
}
memcpy (ptr, s, l);
ptr[l] = 0;
} else {
R_FREE (sb->ptr);
if (l > 0) {
memcpy (sb->buf, s, l);
}
sb->buf[l] = 0;
}
sb->len = l;
sb->weakref = false;
return true;
}
// TODO: there's room for optimizations here
R_API bool r_strbuf_slice(RStrBuf *sb, int from, int len) {
r_return_val_if_fail (sb && from >= 0 && len >= 0, false);
if (from < 1 && len >= sb->len) {
return false;
}
const char *s = r_strbuf_get (sb);
const char *fr = r_str_ansi_chrn (s, from + 1);
const char *to = r_str_ansi_chrn (s, from + len + 1);
char *r = r_str_newlen (fr, to - fr);
r_strbuf_fini (sb);
r_strbuf_init (sb);
if (from >= len) {
r_strbuf_set (sb, "");
free (r);
return false;
}
r_strbuf_set (sb, r);
free (r);
return true;
}
R_API bool r_strbuf_setptr(RStrBuf *sb, char *s, int len) {
r_return_val_if_fail (sb, false);
if (len < 0) {
sb->len = strlen (s);
sb->ptrlen = sb->len + 1;
} else {
sb->ptrlen = len;
sb->len = len;
}
sb->ptr = s;
sb->weakref = true;
return true;
}
R_API const char *r_strbuf_set(RStrBuf *sb, const char *s) {
r_return_val_if_fail (sb, NULL);
if (!s) {
r_strbuf_init (sb);
return r_strbuf_get (sb);
}
size_t len = strlen (s);
if (!r_strbuf_setbin (sb, (const ut8*)s, len)) {
return NULL;
}
sb->len = len;
return r_strbuf_get (sb);
}
R_API const char *r_strbuf_setf(RStrBuf *sb, const char *fmt, ...) {
r_return_val_if_fail (sb && fmt, false);
va_list ap;
va_start (ap, fmt);
const char *ret = r_strbuf_vsetf (sb, fmt, ap);
va_end (ap);
return ret;
}
R_API const char *r_strbuf_vsetf(RStrBuf *sb, const char *fmt, va_list ap) {
r_return_val_if_fail (sb && fmt, false);
const char *ret = NULL;
va_list ap2;
va_copy (ap2, ap);
char string[1024];
int rc = vsnprintf (string, sizeof (string), fmt, ap);
if (rc >= sizeof (string)) {
char *p = malloc (rc + 1);
if (!p) {
goto done;
}
vsnprintf (p, rc + 1, fmt, ap2);
ret = r_strbuf_set (sb, p);
free (p);
} else if (rc >= 0) {
ret = r_strbuf_set (sb, string);
}
done:
va_end (ap2);
return ret;
}
R_API bool r_strbuf_prepend(RStrBuf *sb, const char *s) {
r_return_val_if_fail (sb && s, false);
int l = strlen (s);
// fast path if no chars to append
if (l == 0) {
return true;
}
int newlen = l + sb->len;
char *ns = malloc (newlen + 1);
bool ret = false;
if (ns) {
memcpy (ns, s, l);
char *s = sb->ptr ? sb->ptr: sb->buf;
memcpy (ns + l, s, sb->len);
ns[newlen] = 0;
ret = r_strbuf_set (sb, ns);
free (ns);
}
return ret;
}
R_API bool r_strbuf_append(RStrBuf *sb, const char *s) {
r_return_val_if_fail (sb && s, false);
size_t l = strlen (s);
return r_strbuf_append_n (sb, s, l);
}
R_API bool r_strbuf_append_n(RStrBuf *sb, const char *s, size_t l) {
r_return_val_if_fail (sb && s, false);
if (l > ST32_MAX) {
R_LOG_WARN ("Negative length used in r_strbuf_append_n");
return false;
}
if (sb->weakref) {
return false;
}
// fast path if no chars to append
if (l == 0) {
return true;
}
if ((sb->len + l + 1) <= sizeof (sb->buf)) {
memcpy (sb->buf + sb->len, s, l);
sb->buf[sb->len + l] = 0;
R_FREE (sb->ptr);
} else {
int ll = (l < 256)? 256: (l * 2);
int newlen = sb->len + ll;
char *p = sb->ptr;
bool allocated = true;
if (!sb->ptr) {
p = malloc (newlen);
if (p && sb->len > 0) {
memcpy (p, sb->buf, sb->len);
}
} else if (sb->len + l + 1 > sb->ptrlen) {
p = realloc (sb->ptr, newlen);
} else {
allocated = false;
}
if (allocated) {
if (!p) {
return false;
}
sb->ptr = p;
sb->ptrlen = newlen;
}
if (p) {
memcpy (p + sb->len, s, l);
*(p + sb->len + l) = 0;
}
}
sb->len += l;
return true;
}
R_API bool r_strbuf_appendf(RStrBuf *sb, const char *fmt, ...) {
va_list ap;
r_return_val_if_fail (sb && fmt, false);
va_start (ap, fmt);
const bool ret = r_strbuf_vappendf (sb, fmt, ap);
va_end (ap);
return ret;
}
R_API bool r_strbuf_vappendf(RStrBuf *sb, const char *fmt, va_list ap) {
va_list ap2;
char string[1024];
r_return_val_if_fail (sb && fmt, false);
if (sb->weakref) {
return false;
}
va_copy (ap2, ap);
int ret = vsnprintf (string, sizeof (string), fmt, ap);
if (ret >= sizeof (string)) {
char *p = malloc (ret + 1);
if (!p) {
va_end (ap2);
return false;
}
*p = 0;
vsnprintf (p, ret + 1, fmt, ap2);
ret = r_strbuf_append_n (sb, p, ret);
free (p);
} else if (ret >= 0) {
ret = r_strbuf_append_n (sb, string, ret);
} else {
ret = false;
}
va_end (ap2);
return ret;
}
R_API char *r_strbuf_get(RStrBuf *sb) {
r_return_val_if_fail (sb, NULL);
return sb->ptr ? sb->ptr : sb->buf;
}
R_API ut8 *r_strbuf_getbin(RStrBuf *sb, int *len) {
r_return_val_if_fail (sb, NULL);
if (len) {
*len = sb->len;
}
return (ut8 *)(sb->ptr ? sb->ptr : sb->buf);
}
static inline char *drain(RStrBuf *sb) {
return sb->ptr
? sb->weakref
? r_mem_dup (sb->ptr, sb->ptrlen)
: sb->ptr
: r_str_ndup (sb->buf, sb->len);
}
R_API char *r_strbuf_drain(RStrBuf *sb) {
r_return_val_if_fail (sb, NULL);
char *ret = drain (sb);
free (sb);
return ret;
}
R_API char *r_strbuf_tostring(RStrBuf *sb) {
r_return_val_if_fail (sb, NULL);
return drain (sb);
}
R_API char *r_strbuf_drain_nofree(RStrBuf *sb) {
r_return_val_if_fail (sb, NULL);
char *ret = drain (sb);
sb->ptr = NULL;
sb->len = 0;
sb->buf[0] = '\0';
return ret;
}
R_API bool r_strbuf_replace(RStrBuf *sb, const char *key, const char *val) {
r_return_val_if_fail (sb && key && val, false);
char *tmp = r_str_replace (strdup (r_strbuf_get (sb)), key, val, 0);
if (!tmp) {
return false;
}
free (r_strbuf_drain_nofree (sb));
return r_strbuf_setptr (sb, tmp, 0);
}
R_API bool r_strbuf_replacef(RStrBuf *sb, const char *key, const char *fmt, ...) {
r_return_val_if_fail (sb && key && fmt, false);
RStrBuf *sb_tmp = r_strbuf_new (NULL);
if (!sb_tmp) {
return false;
}
char *tmp = strdup (r_strbuf_get (sb));
if (!tmp) {
r_strbuf_free (sb_tmp);
return false;
}
va_list ap;
va_start (ap, fmt);
const bool vsret = r_strbuf_vsetf (sb_tmp, fmt, ap);
va_end (ap);
if (!vsret) {
r_strbuf_free (sb_tmp);
free (tmp);
return false;
}
tmp = r_str_replace (tmp, key, r_strbuf_get (sb_tmp), 0);
r_strbuf_free (sb_tmp);
if (!tmp) {
return false;
}
free (r_strbuf_drain_nofree (sb));
return r_strbuf_setptr (sb, tmp, 0);
}
R_API void r_strbuf_free(RStrBuf *sb) {
if (sb) {
r_strbuf_fini (sb);
free (sb);
}
}
R_API void r_strbuf_fini(RStrBuf *sb) {
if (sb && !sb->weakref) {
R_FREE (sb->ptr);
sb->len = 0;
sb->buf[0] = '\0';
}
}
R_API void r_strbuf_trim(RStrBuf *sb) {
char *s = strdup (r_strbuf_get (sb));
r_str_trim (s);
r_strbuf_set (sb, s);
}