diff --git a/src/libratbag-util.c b/src/libratbag-util.c index 0d77cef3..583dc986 100644 --- a/src/libratbag-util.c +++ b/src/libratbag-util.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "libratbag-util.h" #include "libratbag-private.h" @@ -85,3 +87,93 @@ udev_prop_value(struct udev_device *device, return prop_value; } + +ssize_t +ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc, + const char *format, ...) +{ + va_list args; + iconv_t converter; + char str[buf_len]; + char *in_buf = str, *out_buf = (char*)buf; + size_t in_bytes_left, out_bytes_left = buf_len; + int ret; + + memset(buf, 0, buf_len); + + va_start(args, format); + ret = vsnprintf(str, buf_len, format, args); + va_end(args); + + if (ret < 0) + return ret; + + in_bytes_left = ret; + + converter = iconv_open(to_enc, "UTF-8"); + if (converter == (iconv_t)-1) + return errno; + + ret = iconv(converter, &in_buf, &in_bytes_left, &out_buf, + &out_bytes_left); + if (ret) + ret = errno; + else + ret = buf_len - out_bytes_left; + + iconv_close(converter); + + return ret; +} + +ssize_t +ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc, + char **out) +{ + iconv_t converter; + size_t len = in_len * 6, + in_bytes_left = in_len, + out_bytes_left = len; + char *pos; + ssize_t ret; + + converter = iconv_open("UTF-8", from_enc); + if (converter == (iconv_t)-1) + return errno; + + /* + * We *could* dynamically allocate the out buffer. However iconv's + * obnoxious semantics, mainly the fact that it modifies every pointer + * given to it, would make this code a lot larger and complex then it + * needs to be for a mouse with blinking lights. + * + * So, since there's no encoding that takes more then 6 bytes per + * character, allocate for that and just fail if that's not enough. + */ + *out = zalloc(len); + pos = *out; + if (!*out) { + ret = errno; + goto err; + } + + ret = iconv(converter, &in_buf, &in_bytes_left, &pos, &out_bytes_left); + if (ret) { + ret = errno; + goto err; + } + + /* Now get rid of any space in the buffer we don't need */ + *out = realloc(*out, (len - out_bytes_left) + 1); + if (!*out) + ret = errno; + +err: + if (ret < 0 && *out) { + free(*out); + *out = NULL; + } + + iconv_close(converter); + return ret; +} diff --git a/src/libratbag-util.h b/src/libratbag-util.h index b2cb59bf..8cfe4a52 100644 --- a/src/libratbag-util.h +++ b/src/libratbag-util.h @@ -200,4 +200,21 @@ long_set_bit_state(unsigned long *array, int bit, int state) const char * udev_prop_value(struct udev_device *device, const char *property_name); + +/** + * Converts a string from UTF-8 to the encoding specified. Returns the number + * of bytes written to buf on success, or negative errno value on failure. + */ +ssize_t +ratbag_utf8_to_enc(char *buf, size_t buf_len, const char *to_enc, + const char *format, ...) __attribute__((format(printf, 4, 5))); +/** + * Converts a string from the given encoding into UTF-8. The memory for the + * result is allocated and a pointer to the result is placed in *out. Returns + * the number of bytes in the UTF-8 version of the string on success, negative + * errno value on failure. + */ +ssize_t +ratbag_utf8_from_enc(char *in_buf, size_t in_len, const char *from_enc, + char **out); #endif /* LIBRATBAG_UTIL_H */