2014-04-12 18:59:45 +02:00
|
|
|
|
#define _XOPEN_SOURCE
|
|
|
|
|
#include <wchar.h> /* wcswidth */
|
|
|
|
|
#undef _XOPEN_SOURCE
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
2014-04-10 00:09:35 +02:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Portable strdup.
|
|
|
|
|
*
|
|
|
|
|
* @param string C "string" to copy.
|
|
|
|
|
* @return Copy of the given C "string".
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
char*
|
|
|
|
|
bm_strdup(const char *string)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-04-10 00:41:32 +02:00
|
|
|
|
assert(string);
|
|
|
|
|
|
2014-04-10 00:09:35 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-12 18:59:21 +02:00
|
|
|
|
/**
|
|
|
|
|
* Replaces next token in string with '\0' and returns position for the replaced token.
|
|
|
|
|
*
|
|
|
|
|
* @param string C "string" where token will be replaced.
|
2014-10-22 21:46:51 +02:00
|
|
|
|
* @param out_next Reference to position of next delimiter, or 0 if none.
|
2014-04-12 18:59:21 +02:00
|
|
|
|
* @return Position of the replaced token.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_strip_token(char *string, const char *token, size_t *out_next)
|
2014-04-12 18:59:21 +02:00
|
|
|
|
{
|
|
|
|
|
size_t len = strcspn(string, token);
|
2014-04-12 19:16:33 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (out_next)
|
|
|
|
|
*out_next = len + (string[len] != 0);
|
2014-04-12 19:16:33 +02:00
|
|
|
|
|
2014-04-12 18:59:21 +02:00
|
|
|
|
string[len] = 0;
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-10 00:09:35 +02:00
|
|
|
|
/**
|
2014-04-10 21:02:47 +02:00
|
|
|
|
* Portable case-insensitive strcmp.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
*
|
2014-04-10 21:02:47 +02:00
|
|
|
|
* @param hay C "string" to match against.
|
|
|
|
|
* @param needle C "string" to match.
|
2014-04-12 11:55:05 +02:00
|
|
|
|
* @return Less than, equal to or greater than zero if hay is lexicographically less than, equal to or greater than needle.
|
2014-04-10 21:02:47 +02:00
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
int
|
|
|
|
|
bm_strupcmp(const char *hay, const char *needle)
|
2014-04-10 21:02:47 +02:00
|
|
|
|
{
|
2014-10-22 21:46:51 +02:00
|
|
|
|
return bm_strnupcmp(hay, needle, strlen(hay));
|
2014-04-12 11:55:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Portable case-insensitive strncmp.
|
|
|
|
|
*
|
|
|
|
|
* @param hay C "string" to match against.
|
|
|
|
|
* @param needle C "string" to match.
|
|
|
|
|
* @return Less than, equal to or greater than zero if hay is lexicographically less than, equal to or greater than needle.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
int
|
|
|
|
|
bm_strnupcmp(const char *hay, const char *needle, size_t len)
|
2014-04-12 11:55:05 +02:00
|
|
|
|
{
|
|
|
|
|
const unsigned char *p1 = (const unsigned char*)hay;
|
|
|
|
|
const unsigned char *p2 = (const unsigned char*)needle;
|
2014-04-10 21:02:47 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
unsigned char a = 0, b = 0;
|
|
|
|
|
for (size_t i = 0; len > 0; --len, ++i)
|
2014-04-12 11:55:05 +02:00
|
|
|
|
if ((a = toupper(*p1++)) != (b = toupper(*p2++)))
|
|
|
|
|
return a - b;
|
|
|
|
|
|
|
|
|
|
return a - b;
|
2014-04-10 21:02:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Portable case-insensitive strstr.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
*
|
2014-04-10 21:02:47 +02:00
|
|
|
|
* @param hay C "string" to substring against.
|
|
|
|
|
* @param needle C "string" to substring.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
char*
|
|
|
|
|
bm_strupstr(const char *hay, const char *needle)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t r = 0, p = 0, len, len2;
|
2014-04-10 21:02:47 +02:00
|
|
|
|
|
|
|
|
|
if ((len = strlen(hay)) < (len2 = strlen(needle)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (!bm_strnupcmp(hay, needle, len2))
|
2014-04-12 11:55:05 +02:00
|
|
|
|
return (char*)hay;
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
2014-04-10 21:02:47 +02:00
|
|
|
|
if (p == len2)
|
|
|
|
|
return (char*)hay + r;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-04-10 21:02:47 +02:00
|
|
|
|
if (toupper(hay[i]) == toupper(needle[p++])) {
|
|
|
|
|
if (!r)
|
|
|
|
|
r = i;
|
|
|
|
|
} else {
|
|
|
|
|
if (r)
|
|
|
|
|
i = r;
|
|
|
|
|
r = p = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-04-10 21:02:47 +02:00
|
|
|
|
return (p == len2 ? (char*)hay + r : NULL);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determite columns needed to display UTF8 string.
|
|
|
|
|
*
|
|
|
|
|
* @param string C "string" to determite.
|
|
|
|
|
* @return Number of columns, or -1 on failure.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
int32_t
|
|
|
|
|
bm_utf8_string_screen_width(const char *string)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-04-10 00:41:32 +02:00
|
|
|
|
assert(string);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
char *mstr;
|
|
|
|
|
if (!(mstr = bm_strdup(string)))
|
2014-04-12 22:21:43 +02:00
|
|
|
|
return strlen(string);
|
|
|
|
|
|
|
|
|
|
char *s;
|
|
|
|
|
for (s = mstr; *s; ++s) if (*s == '\t') *s = ' ';
|
|
|
|
|
|
|
|
|
|
int num_char = mbstowcs(NULL, mstr, 0) + 1;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0]));
|
|
|
|
|
|
2014-04-12 22:21:43 +02:00
|
|
|
|
if (mbstowcs(wstring, mstr, num_char) == (size_t)(-1)) {
|
2014-04-10 00:09:35 +02:00
|
|
|
|
free(wstring);
|
2014-04-12 22:21:43 +02:00
|
|
|
|
int len = strlen(mstr);
|
|
|
|
|
free(mstr);
|
|
|
|
|
return len;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
int32_t length = wcswidth(wstring, num_char);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
free(wstring);
|
2014-04-12 22:21:43 +02:00
|
|
|
|
free(mstr);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
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.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_utf8_rune_next(const char *string, size_t start)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-04-10 00:41:32 +02:00
|
|
|
|
assert(string);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_utf8_rune_prev(const char *string, size_t start)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-04-10 00:41:32 +02:00
|
|
|
|
assert(string);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_utf8_rune_width(const char *rune, uint32_t u8len)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-04-10 00:41:32 +02:00
|
|
|
|
assert(rune);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
char mb[5] = { 0, 0, 0, 0, 0 };
|
|
|
|
|
memcpy(mb, rune, (u8len > 4 ? 4 : u8len));
|
2014-10-22 21:46:51 +02:00
|
|
|
|
return bm_utf8_string_screen_width(mb);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove previous UTF8 rune from buffer.
|
|
|
|
|
*
|
|
|
|
|
* @param string Null terminated C "string".
|
|
|
|
|
* @param start Start offset where to delete from. (cursor)
|
2014-10-22 21:46:51 +02:00
|
|
|
|
* @param out_rune_width Reference to size_t, return number of columns for removed rune, or -1 on failure.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
* @return Number of bytes removed from buffer.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_utf8_rune_remove(char *string, size_t start, size_t *out_rune_width)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-04-10 00:41:32 +02:00
|
|
|
|
assert(string);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (out_rune_width)
|
|
|
|
|
*out_rune_width = 0;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
|
|
|
|
size_t len = strlen(string), oldStart = start;
|
|
|
|
|
if (len == 0 || len < start || !*string)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
start -= bm_utf8_rune_prev(string, start);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (out_rune_width)
|
|
|
|
|
*out_rune_width = bm_utf8_rune_width(string + start, oldStart - start);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
|
|
|
|
memmove(string + start, string + oldStart, len - oldStart);
|
|
|
|
|
string[len - (oldStart - start)] = 0;
|
|
|
|
|
return (oldStart - start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert UTF8 rune to buffer.
|
|
|
|
|
*
|
2014-10-22 21:46:51 +02:00
|
|
|
|
* @param in_out_string Reference to buffer.
|
|
|
|
|
* @param in_out_buf_size Reference to size of the buffer.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
* @param start Start offset where to insert to. (cursor)
|
|
|
|
|
* @param rune Buffer to insert to string.
|
|
|
|
|
* @param u8len Byte length of the rune.
|
2014-10-22 21:46:51 +02:00
|
|
|
|
* @param out_rune_width Reference to size_t, return number of columns for inserted rune, or -1 on failure.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
* @return Number of bytes inserted to buffer.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_utf8_rune_insert(char **in_out_string, size_t *in_out_buf_size, size_t start, const char *rune, uint32_t u8len, size_t *out_rune_width)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-10-22 21:46:51 +02:00
|
|
|
|
assert(in_out_string);
|
|
|
|
|
assert(in_out_buf_size);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (out_rune_width)
|
|
|
|
|
*out_rune_width = 0;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-04-14 17:08:13 +02:00
|
|
|
|
if (u8len == 1 && !isprint(*rune))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t len = (*in_out_string ? strlen(*in_out_string) : 0);
|
|
|
|
|
if (!*in_out_string && !(*in_out_string = calloc(1, (*in_out_buf_size = u8len + 1))))
|
2014-04-10 00:09:35 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (len + u8len >= *in_out_buf_size) {
|
2014-04-12 19:52:29 +02:00
|
|
|
|
void *tmp;
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (!(tmp = realloc(*in_out_string, (*in_out_buf_size * 2)))) {
|
|
|
|
|
if (!(tmp = malloc((*in_out_buf_size * 2))))
|
2014-04-12 19:52:29 +02:00
|
|
|
|
return 0;
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
memcpy(tmp, *in_out_string, *in_out_buf_size);
|
|
|
|
|
free(*in_out_string);
|
2014-04-12 19:52:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
memset(tmp + *in_out_buf_size, 0, *in_out_buf_size);
|
|
|
|
|
*in_out_string = tmp;
|
|
|
|
|
*in_out_buf_size *= 2;
|
2014-04-12 19:52:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
char *str = *in_out_string + start;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
memmove(str + u8len, str, len - start);
|
|
|
|
|
memcpy(str, rune, u8len);
|
2014-10-22 21:46:51 +02:00
|
|
|
|
(*in_out_string)[len + u8len] = 0;
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
if (out_rune_width)
|
|
|
|
|
*out_rune_width = bm_utf8_rune_width(rune, u8len);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
return u8len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Insert unicode character to UTF8 buffer.
|
|
|
|
|
*
|
2014-10-22 21:46:51 +02:00
|
|
|
|
* @param in_out_string Reference to buffer.
|
|
|
|
|
* @param in_out_buf_size Reference to size of the buffer.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
* @param start Start offset where to insert to. (cursor)
|
|
|
|
|
* @param unicode Unicode character to insert.
|
2014-10-22 21:46:51 +02:00
|
|
|
|
* @param out_rune_width Reference to size_t, return number of columns for inserted rune, or -1 on failure.
|
2014-04-10 00:09:35 +02:00
|
|
|
|
* @return Number of bytes inserted to buffer.
|
|
|
|
|
*/
|
2014-10-22 21:46:51 +02:00
|
|
|
|
size_t
|
|
|
|
|
bm_unicode_insert(char **in_out_string, size_t *in_out_buf_size, size_t start, uint32_t unicode, size_t *out_rune_width)
|
2014-04-10 00:09:35 +02:00
|
|
|
|
{
|
2014-10-22 21:46:51 +02:00
|
|
|
|
assert(in_out_string && in_out_buf_size);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-22 21:46:51 +02:00
|
|
|
|
return bm_utf8_rune_insert(in_out_string, in_out_buf_size, start, mb, u8len, out_rune_width);
|
2014-04-10 00:09:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* vim: set ts=8 sw=4 tw=0 :*/
|