/** * @file util.c */ #include "internal.h" #define _XOPEN_SOURCE 700 #include #include #include #include #include /** * Portable strdup. * * @param string C "string" to copy. * @return Copy of the given C "string". */ char* _bmStrdup(const char *string) { size_t len = strlen(string); if (len == 0) return NULL; void *copy = calloc(1, len + 1); if (copy == NULL) return NULL; return (char *)memcpy(copy, string, len); } /** * Shrink bmItem** list pointer. * * Useful helper function for filter functions. * * @param list Pointer to pointer to list of bmItem pointers. * @param osize Current size of the list. * @param nsize New size the list will be shrinked to. * @return Pointer to list of bmItem pointers. */ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) { if (nsize >= osize) return *list; void *tmp = malloc(sizeof(bmItem*) * nsize); if (!tmp) return *list; memcpy(tmp, *list, sizeof(bmItem*) * nsize); free(*list); *list = tmp; return *list; } /** * Determite columns needed to display UTF8 string. * * @param string C "string" to determite. * @return Number of columns, or -1 on failure. */ int _bmUtf8StringScreenWidth(const char *string) { if (!string) return 0; int num_char = mbstowcs(NULL, string, 0) + 1; wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); if (mbstowcs(wstring, string, num_char) == (size_t)(-1)) { free(wstring); return strlen(string); } int length = wcswidth(wstring, num_char); free(wstring); return length; } /** * Figure out how many bytes to shift to next UTF8 rune. * * @param string C "string" which contains the runes. * @param start Offset where to figure out next rune. (cursor) * @return Number of bytes to next UTF8 rune. */ size_t _bmUtf8RuneNext(const char *string, size_t start) { assert(string != NULL); size_t len = strlen(string), i = start; if (len == 0 || len <= i || !*string) return 0; while (++i < len && (string[i] & 0xc0) == 0x80); return i - start; } /** * Figure out how many bytes to shift to previous UTF8 rune. * * @param string C "string" which contains the runes. * @param start Offset where to figure out previous rune. (cursor) * @return Number of bytes to previous UTF8 rune. */ size_t _bmUtf8RunePrev(const char *string, size_t start) { assert(string != NULL); size_t len = strlen(string), i = start; if (i == 0 || len < start || !*string) return 0; while (--i > 0 && (string[i] & 0xc0) == 0x80); return start - i; } /** * Figure out how many columns are needed to display UTF8 rune. * * @param rune Buffer which contains the rune. * @param u8len Byte length of the rune. * @return Number of columns, or -1 on failure. */ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) { assert(rune != NULL); char mb[5] = { 0, 0, 0, 0, 0 }; memcpy(mb, rune, (u8len > 4 ? 4 : u8len)); return _bmUtf8StringScreenWidth(mb); } /** * Remove previous UTF8 rune from buffer. * * @param string Null terminated C "string". * @param start Start offset where to delete from. (cursor) * @param runeWidth Reference to size_t, return number of columns for removed rune, or -1 on failure. * @return Number of bytes removed from buffer. */ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) { assert(string != NULL); if (runeWidth) *runeWidth = 0; size_t len = strlen(string), oldStart = start; if (len == 0 || len < start || !*string) return 0; start -= _bmUtf8RunePrev(string, start); if (runeWidth) *runeWidth = _bmUtf8RuneWidth(string + start, oldStart - start); memmove(string + start, string + oldStart, len - oldStart); string[len - (oldStart - start)] = 0; return (oldStart - start); } /** * Insert UTF8 rune to buffer. * * @param string Null terminated C "string". * @param bufSize Size of the buffer. * @param start Start offset where to insert to. (cursor) * @param rune Buffer to insert to string. * @param u8len Byte length of the rune. * @param runeWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) { assert(string != NULL); if (runeWidth) *runeWidth = 0; size_t len = strlen(string); if (len + u8len >= bufSize) return 0; if (u8len == 1 && iscntrl(*rune)) return 0; char *str = string + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); if (runeWidth) *runeWidth = _bmUtf8RuneWidth(rune, u8len); return u8len; } /** * Insert unicode character to UTF8 buffer. * * @param string Null terminated C "string". * @param bufSize Size of the buffer. * @param start Start offset where to insert to. (cursor) * @param unicode Unicode character to insert. * @param runeWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. * @return Number of bytes inserted to buffer. */ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) { assert(string != NULL); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; if (u8len == 1) { mb[0] = unicode; } else { size_t i, j; for (i = j = u8len; j > 1; --j) mb[j - 1] = 0x80 | (0x3F & (unicode >> ((i - j) * 6))); mb[0] = (~0) << (8 - i); mb[0] |= (unicode >> (i * 6 - 6)); } return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, runeWidth); } /* vim: set ts=8 sw=4 tw=0 :*/