2018-01-17 19:22:57 +01:00
|
|
|
#include "string_buffer.h"
|
2018-11-03 21:50:30 +01:00
|
|
|
#include "properties.h"
|
2018-03-21 18:46:27 +01:00
|
|
|
#include "wcwidth.h"
|
2018-05-06 12:12:28 +02:00
|
|
|
#include <assert.h>
|
2018-04-17 19:14:50 +02:00
|
|
|
#include <stddef.h>
|
2019-08-14 21:01:57 +02:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2018-05-06 12:12:28 +02:00
|
|
|
#include <wchar.h>
|
2019-08-14 21:01:57 +02:00
|
|
|
#endif
|
|
|
|
#if defined(FT_HAVE_UTF8)
|
|
|
|
#include "utf8.h"
|
|
|
|
#endif
|
2018-03-21 18:46:27 +01:00
|
|
|
|
2018-04-17 19:14:50 +02:00
|
|
|
static ptrdiff_t str_iter_width(const char *beg, const char *end)
|
2018-03-21 18:46:27 +01:00
|
|
|
{
|
2018-04-17 19:14:50 +02:00
|
|
|
assert(end >= beg);
|
|
|
|
return (end - beg);
|
2018-03-21 18:46:27 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:45:34 +02:00
|
|
|
|
2018-09-01 14:51:19 +02:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2018-04-17 19:14:50 +02:00
|
|
|
static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end)
|
2018-03-21 18:46:27 +01:00
|
|
|
{
|
2018-04-17 19:14:50 +02:00
|
|
|
assert(end >= beg);
|
2019-01-01 17:02:12 +01:00
|
|
|
return mk_wcswidth(beg, (size_t)(end - beg));
|
2018-03-21 18:46:27 +01:00
|
|
|
}
|
2018-09-01 14:51:19 +02:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2018-03-21 18:46:27 +01:00
|
|
|
|
|
|
|
|
2018-03-31 12:33:37 +02:00
|
|
|
static size_t buf_str_len(const string_buffer_t *buf)
|
2018-03-05 19:08:14 +01:00
|
|
|
{
|
|
|
|
assert(buf);
|
2019-08-14 21:01:57 +02:00
|
|
|
|
|
|
|
switch (buf->type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
return strlen(buf->str.cstr);
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
case W_CHAR_BUF:
|
|
|
|
return wcslen(buf->str.wstr);
|
|
|
|
#endif
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
|
|
|
return utf8len(buf->str.u8str);
|
|
|
|
#endif
|
2018-03-05 19:08:14 +01:00
|
|
|
}
|
2019-08-14 21:01:57 +02:00
|
|
|
|
|
|
|
assert(0);
|
|
|
|
return 0;
|
2018-03-05 19:08:14 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-03-31 12:33:37 +02:00
|
|
|
size_t strchr_count(const char *str, char ch)
|
2018-01-21 09:19:18 +01:00
|
|
|
{
|
|
|
|
if (str == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
str = strchr(str, ch);
|
|
|
|
while (str) {
|
|
|
|
count++;
|
|
|
|
str++;
|
|
|
|
str = strchr(str, ch);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2018-03-31 12:33:37 +02:00
|
|
|
size_t wstrchr_count(const wchar_t *str, wchar_t ch)
|
2018-03-05 19:08:14 +01:00
|
|
|
{
|
|
|
|
if (str == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
str = wcschr(str, ch);
|
|
|
|
while (str) {
|
|
|
|
count++;
|
|
|
|
str++;
|
|
|
|
str = wcschr(str, ch);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
2019-08-14 21:01:57 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(FT_HAVE_UTF8)
|
|
|
|
/* todo: do something with code below!!! */
|
|
|
|
FT_INTERNAL
|
|
|
|
void *ut8next(const void *str)
|
|
|
|
{
|
|
|
|
utf8_int32_t out_codepoint;
|
|
|
|
return utf8codepoint(str, &out_codepoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
FT_INTERNAL
|
|
|
|
size_t utf8chr_count(const void *str, utf8_int32_t ch)
|
|
|
|
{
|
|
|
|
if (str == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t count = 0;
|
|
|
|
str = utf8chr(str, ch);
|
|
|
|
while (str) {
|
|
|
|
count++;
|
|
|
|
str = ut8next(str);
|
|
|
|
str = utf8chr(str, ch);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
#endif /* FT_HAVE_UTF8 */
|
2018-03-05 19:08:14 +01:00
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-03-31 12:33:37 +02:00
|
|
|
const char *str_n_substring_beg(const char *str, char ch_separator, size_t n)
|
2018-01-21 09:19:18 +01:00
|
|
|
{
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
return str;
|
|
|
|
|
|
|
|
str = strchr(str, ch_separator);
|
|
|
|
--n;
|
|
|
|
while (n > 0) {
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
--n;
|
|
|
|
str++;
|
|
|
|
str = strchr(str, ch_separator);
|
|
|
|
}
|
|
|
|
return str ? (str + 1) : NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-17 21:55:05 +01:00
|
|
|
|
2018-11-04 09:22:53 +01:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2018-03-31 12:33:37 +02:00
|
|
|
const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, size_t n)
|
2018-03-05 19:08:14 +01:00
|
|
|
{
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
return str;
|
|
|
|
|
|
|
|
str = wcschr(str, ch_separator);
|
|
|
|
--n;
|
|
|
|
while (n > 0) {
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
--n;
|
|
|
|
str++;
|
|
|
|
str = wcschr(str, ch_separator);
|
|
|
|
}
|
|
|
|
return str ? (str + 1) : NULL;
|
|
|
|
}
|
2018-11-04 09:22:53 +01:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2018-03-05 19:08:14 +01:00
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
#if defined(FT_HAVE_UTF8)
|
|
|
|
FT_INTERNAL
|
|
|
|
const void *utf8_n_substring_beg(const void *str, utf8_int32_t ch_separator, size_t n)
|
|
|
|
{
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
return str;
|
|
|
|
|
|
|
|
str = utf8chr(str, ch_separator);
|
|
|
|
--n;
|
|
|
|
while (n > 0) {
|
|
|
|
if (str == NULL)
|
|
|
|
return NULL;
|
|
|
|
--n;
|
|
|
|
str = ut8next(str);
|
|
|
|
str = utf8chr(str, ch_separator);
|
|
|
|
}
|
|
|
|
return str ? (ut8next(str)) : NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-03-31 12:33:37 +02:00
|
|
|
void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end)
|
2018-01-21 09:19:18 +01:00
|
|
|
{
|
|
|
|
const char *beg = str_n_substring_beg(str, ch_separator, n);
|
|
|
|
if (beg == NULL) {
|
|
|
|
*begin = NULL;
|
|
|
|
*end = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *en = strchr(beg, ch_separator);
|
|
|
|
if (en == NULL) {
|
|
|
|
en = str + strlen(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
*begin = beg;
|
|
|
|
*end = en;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-17 21:55:05 +01:00
|
|
|
|
2018-11-04 09:22:53 +01:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2018-03-31 12:33:37 +02:00
|
|
|
void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const wchar_t **begin, const wchar_t **end)
|
2018-03-05 19:08:14 +01:00
|
|
|
{
|
|
|
|
const wchar_t *beg = wstr_n_substring_beg(str, ch_separator, n);
|
|
|
|
if (beg == NULL) {
|
|
|
|
*begin = NULL;
|
|
|
|
*end = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const wchar_t *en = wcschr(beg, ch_separator);
|
|
|
|
if (en == NULL) {
|
|
|
|
en = str + wcslen(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
*begin = beg;
|
|
|
|
*end = en;
|
|
|
|
return;
|
|
|
|
}
|
2018-11-04 09:22:53 +01:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2018-03-05 19:08:14 +01:00
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
#if defined(FT_HAVE_UTF8)
|
|
|
|
FT_INTERNAL
|
|
|
|
void utf8_n_substring(const void *str, utf8_int32_t ch_separator, size_t n, const void **begin, const void **end)
|
|
|
|
{
|
2019-08-26 11:33:17 +02:00
|
|
|
const char *beg = (const char *)utf8_n_substring_beg(str, ch_separator, n);
|
2019-08-14 21:01:57 +02:00
|
|
|
if (beg == NULL) {
|
|
|
|
*begin = NULL;
|
|
|
|
*end = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-08-26 11:33:17 +02:00
|
|
|
const char *en = (const char *)utf8chr(beg, ch_separator);
|
2019-08-14 21:01:57 +02:00
|
|
|
if (en == NULL) {
|
2019-08-26 11:33:17 +02:00
|
|
|
en = (const char *)str + strlen((const char *)str);
|
2019-08-14 21:01:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
*begin = beg;
|
|
|
|
*end = en;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif /* FT_HAVE_UTF8 */
|
|
|
|
|
|
|
|
|
2018-01-21 09:19:18 +01:00
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2019-08-27 13:27:19 +02:00
|
|
|
string_buffer_t *create_string_buffer(size_t n_chars, enum str_buf_type type)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
2019-08-27 13:27:19 +02:00
|
|
|
size_t char_sz = 0;
|
|
|
|
switch (type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
char_sz = 1;
|
|
|
|
break;
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
case W_CHAR_BUF:
|
|
|
|
char_sz = sizeof(wchar_t);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
|
|
|
char_sz = 4;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t sz = n_chars * char_sz;
|
2018-01-17 19:22:57 +01:00
|
|
|
string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t));
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
2018-03-17 19:53:38 +01:00
|
|
|
result->str.data = F_MALLOC(sz);
|
|
|
|
if (result->str.data == NULL) {
|
2018-01-17 19:22:57 +01:00
|
|
|
F_FREE(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-03-05 19:08:14 +01:00
|
|
|
result->data_sz = sz;
|
|
|
|
result->type = type;
|
2018-01-17 19:22:57 +01:00
|
|
|
|
2019-08-27 13:27:19 +02:00
|
|
|
if (sz) {
|
|
|
|
switch (type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
result->str.cstr[0] = '\0';
|
|
|
|
break;
|
2018-11-04 09:22:53 +01:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2019-08-27 13:27:19 +02:00
|
|
|
case W_CHAR_BUF:
|
|
|
|
result->str.wstr[0] = L'\0';
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
|
|
|
result->str.cstr[0] = '\0';
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
}
|
2018-05-04 20:25:29 +02:00
|
|
|
}
|
|
|
|
|
2018-01-17 19:22:57 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-01-17 19:22:57 +01:00
|
|
|
void destroy_string_buffer(string_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
if (buffer == NULL)
|
|
|
|
return;
|
2018-03-17 19:53:38 +01:00
|
|
|
F_FREE(buffer->str.data);
|
|
|
|
buffer->str.data = NULL;
|
2018-01-17 19:22:57 +01:00
|
|
|
F_FREE(buffer);
|
|
|
|
}
|
|
|
|
|
2018-11-02 22:16:20 +01:00
|
|
|
FT_INTERNAL
|
2019-01-02 07:42:32 +01:00
|
|
|
string_buffer_t *copy_string_buffer(const string_buffer_t *buffer)
|
2018-11-02 22:16:20 +01:00
|
|
|
{
|
|
|
|
assert(buffer);
|
|
|
|
string_buffer_t *result = create_string_buffer(buffer->data_sz, buffer->type);
|
|
|
|
if (result == NULL)
|
|
|
|
return NULL;
|
|
|
|
switch (buffer->type) {
|
2019-08-14 21:01:57 +02:00
|
|
|
case CHAR_BUF:
|
2018-11-02 22:16:20 +01:00
|
|
|
if (FT_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) {
|
2019-01-02 07:42:32 +01:00
|
|
|
destroy_string_buffer(result);
|
2018-11-02 22:16:20 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
2019-08-14 21:01:57 +02:00
|
|
|
case W_CHAR_BUF:
|
2018-11-02 22:16:20 +01:00
|
|
|
if (FT_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) {
|
2019-01-02 07:42:32 +01:00
|
|
|
destroy_string_buffer(result);
|
2018-11-02 22:16:20 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif /* FT_HAVE_WCHAR */
|
|
|
|
default:
|
|
|
|
destroy_string_buffer(result);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-01-17 19:22:57 +01:00
|
|
|
fort_status_t realloc_string_buffer_without_copy(string_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
2018-03-31 12:33:37 +02:00
|
|
|
char *new_str = (char *)F_MALLOC(buffer->data_sz * 2);
|
2018-01-17 19:22:57 +01:00
|
|
|
if (new_str == NULL) {
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_MEMORY_ERROR;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
2018-03-17 19:53:38 +01:00
|
|
|
F_FREE(buffer->str.data);
|
|
|
|
buffer->str.data = new_str;
|
2018-03-05 19:08:14 +01:00
|
|
|
buffer->data_sz *= 2;
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_SUCCESS;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-01-17 19:22:57 +01:00
|
|
|
fort_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
|
|
|
assert(str);
|
|
|
|
|
2018-03-31 12:33:37 +02:00
|
|
|
char *copy = F_STRDUP(str);
|
2018-01-17 19:22:57 +01:00
|
|
|
if (copy == NULL)
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_MEMORY_ERROR;
|
2018-01-17 19:22:57 +01:00
|
|
|
|
2018-03-17 19:53:38 +01:00
|
|
|
F_FREE(buffer->str.data);
|
|
|
|
buffer->str.cstr = copy;
|
2019-08-14 21:01:57 +02:00
|
|
|
buffer->type = CHAR_BUF;
|
2018-03-05 19:08:14 +01:00
|
|
|
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_SUCCESS;
|
2018-03-05 19:08:14 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
FT_INTERNAL
|
2018-03-05 19:08:14 +01:00
|
|
|
fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
|
|
|
assert(str);
|
|
|
|
|
2018-03-31 12:33:37 +02:00
|
|
|
wchar_t *copy = F_WCSDUP(str);
|
2018-03-05 19:08:14 +01:00
|
|
|
if (copy == NULL)
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_MEMORY_ERROR;
|
2018-03-05 19:08:14 +01:00
|
|
|
|
2018-03-17 19:53:38 +01:00
|
|
|
F_FREE(buffer->str.data);
|
|
|
|
buffer->str.wstr = copy;
|
2019-08-14 21:01:57 +02:00
|
|
|
buffer->type = W_CHAR_BUF;
|
2018-01-17 19:22:57 +01:00
|
|
|
|
2018-03-19 21:07:18 +01:00
|
|
|
return FT_SUCCESS;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
2018-09-01 14:37:01 +02:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2018-01-17 19:22:57 +01:00
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
FT_INTERNAL
|
|
|
|
fort_status_t fill_buffer_from_u8string(string_buffer_t *buffer, const void *str)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
|
|
|
assert(str);
|
|
|
|
|
|
|
|
void *copy = F_UTF8DUP(str);
|
|
|
|
if (copy == NULL)
|
|
|
|
return FT_MEMORY_ERROR;
|
|
|
|
|
|
|
|
F_FREE(buffer->str.u8str);
|
|
|
|
buffer->str.u8str = copy;
|
|
|
|
buffer->type = UTF8_BUF;
|
|
|
|
|
|
|
|
return FT_SUCCESS;
|
|
|
|
}
|
|
|
|
#endif /* FT_HAVE_UTF8 */
|
2018-01-17 19:22:57 +01:00
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2019-08-14 21:01:57 +02:00
|
|
|
size_t buffer_text_visible_height(const string_buffer_t *buffer)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
2018-03-17 19:53:38 +01:00
|
|
|
if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) {
|
2018-01-17 19:22:57 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2019-08-14 21:01:57 +02:00
|
|
|
if (buffer->type == CHAR_BUF)
|
2018-03-17 19:53:38 +01:00
|
|
|
return 1 + strchr_count(buffer->str.cstr, '\n');
|
2019-08-14 21:01:57 +02:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
else if (buffer->type == W_CHAR_BUF)
|
2018-03-17 19:53:38 +01:00
|
|
|
return 1 + wstrchr_count(buffer->str.wstr, L'\n');
|
2019-08-14 21:01:57 +02:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
else if (buffer->type == UTF8_BUF)
|
|
|
|
return 1 + utf8chr_count(buffer->str.u8str, '\n');
|
|
|
|
#endif /* FT_HAVE_WCHAR */
|
|
|
|
|
|
|
|
assert(0);
|
|
|
|
return 0;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
FT_INTERNAL
|
|
|
|
size_t string_buffer_cod_width_capacity(const string_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
return string_buffer_width_capacity(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
FT_INTERNAL
|
|
|
|
size_t string_buffer_raw_capacity(const string_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
return buffer->data_sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
FT_INTERNAL
|
|
|
|
size_t ut8_width(const void *beg, const void *end)
|
|
|
|
{
|
|
|
|
size_t sz = (size_t)((const char *)end - (const char *)beg);
|
2019-08-26 11:33:17 +02:00
|
|
|
char *tmp = (char *)F_MALLOC(sizeof(char) * (sz + 1));
|
2019-08-14 21:01:57 +02:00
|
|
|
// @todo: add check to tmp
|
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
memcpy(tmp, beg, sz);
|
|
|
|
tmp[sz] = '\0';
|
|
|
|
size_t result = utf8len(tmp);
|
|
|
|
F_FREE(tmp);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2019-08-14 21:01:57 +02:00
|
|
|
size_t buffer_text_visible_width(const string_buffer_t *buffer)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
2018-01-21 09:19:18 +01:00
|
|
|
size_t max_length = 0;
|
2019-08-14 21:01:57 +02:00
|
|
|
if (buffer->type == CHAR_BUF) {
|
2019-01-01 17:22:39 +01:00
|
|
|
size_t n = 0;
|
2018-03-05 19:08:14 +01:00
|
|
|
while (1) {
|
|
|
|
const char *beg = NULL;
|
|
|
|
const char *end = NULL;
|
2018-03-17 19:53:38 +01:00
|
|
|
str_n_substring(buffer->str.cstr, '\n', n, &beg, &end);
|
2018-03-05 19:08:14 +01:00
|
|
|
if (beg == NULL || end == NULL)
|
|
|
|
return max_length;
|
|
|
|
|
2018-03-17 19:53:38 +01:00
|
|
|
max_length = MAX(max_length, (size_t)(end - beg));
|
2018-03-05 19:08:14 +01:00
|
|
|
++n;
|
|
|
|
}
|
2018-09-01 15:19:35 +02:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
2019-08-14 21:01:57 +02:00
|
|
|
} else if (buffer->type == W_CHAR_BUF) {
|
2019-01-01 17:22:39 +01:00
|
|
|
size_t n = 0;
|
2018-03-05 19:08:14 +01:00
|
|
|
while (1) {
|
|
|
|
const wchar_t *beg = NULL;
|
|
|
|
const wchar_t *end = NULL;
|
2018-03-17 19:53:38 +01:00
|
|
|
wstr_n_substring(buffer->str.wstr, L'\n', n, &beg, &end);
|
2018-03-05 19:08:14 +01:00
|
|
|
if (beg == NULL || end == NULL)
|
|
|
|
return max_length;
|
|
|
|
|
2019-01-01 17:22:39 +01:00
|
|
|
int line_width = mk_wcswidth(beg, (size_t)(end - beg));
|
2018-04-16 21:33:05 +02:00
|
|
|
if (line_width < 0) /* For safety */
|
|
|
|
line_width = 0;
|
|
|
|
max_length = MAX(max_length, (size_t)line_width);
|
2018-03-21 18:46:27 +01:00
|
|
|
|
2018-03-05 19:08:14 +01:00
|
|
|
++n;
|
|
|
|
}
|
2018-09-01 15:19:35 +02:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2019-08-14 21:01:57 +02:00
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
} else if (buffer->type == UTF8_BUF) {
|
|
|
|
size_t n = 0;
|
|
|
|
while (1) {
|
|
|
|
const void *beg = NULL;
|
|
|
|
const void *end = NULL;
|
|
|
|
utf8_n_substring(buffer->str.u8str, '\n', n, &beg, &end);
|
|
|
|
if (beg == NULL || end == NULL)
|
|
|
|
return max_length;
|
|
|
|
|
|
|
|
max_length = MAX(max_length, (size_t)ut8_width(beg, end));
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
#endif /* FT_HAVE_WCHAR */
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
2018-09-01 15:19:35 +02:00
|
|
|
|
|
|
|
return max_length; /* shouldn't be here */
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
static void
|
2019-08-25 08:33:17 +02:00
|
|
|
buffer_substring(const string_buffer_t *buffer, size_t buffer_row, const void **begin, const void **end, ptrdiff_t *str_it_width)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
2019-08-14 21:01:57 +02:00
|
|
|
switch (buffer->type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
str_n_substring(buffer->str.cstr, '\n', buffer_row, (const char **)begin, (const char **)end);
|
|
|
|
if ((*(const char **)begin) && (*(const char **)end))
|
|
|
|
*str_it_width = str_iter_width(*(const char **)begin, *(const char **)end);
|
2018-01-17 19:22:57 +01:00
|
|
|
break;
|
2019-08-14 21:01:57 +02:00
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
case W_CHAR_BUF:
|
|
|
|
wstr_n_substring(buffer->str.wstr, L'\n', buffer_row, (const wchar_t **)begin, (const wchar_t **)end);
|
|
|
|
if ((*(const wchar_t **)begin) && (*(const wchar_t **)end))
|
|
|
|
*str_it_width = wcs_iter_width(*(const wchar_t **)begin, *(const wchar_t **)end);
|
2018-01-17 19:22:57 +01:00
|
|
|
break;
|
2019-08-14 21:01:57 +02:00
|
|
|
#endif /* FT_HAVE_WCHAR */
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
2019-08-25 09:18:41 +02:00
|
|
|
utf8_n_substring(buffer->str.u8str, '\n', buffer_row, begin, end);
|
2019-08-14 21:01:57 +02:00
|
|
|
if ((*(const char **)begin) && (*(const char **)end))
|
|
|
|
*str_it_width = ut8_width(*begin, *end);
|
2018-01-17 19:22:57 +01:00
|
|
|
break;
|
2019-08-14 21:01:57 +02:00
|
|
|
#endif /* FT_HAVE_UTF8 */
|
2018-01-17 19:22:57 +01:00
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
}
|
2019-08-14 21:01:57 +02:00
|
|
|
}
|
2018-01-17 19:22:57 +01:00
|
|
|
|
2018-03-31 17:13:30 +02:00
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
static int
|
|
|
|
buffer_print_range(conv_context_t *cntx, const void *beg, const void *end)
|
|
|
|
{
|
|
|
|
size_t len;
|
|
|
|
switch (cntx->b_type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
len = (size_t)((const char *)end - (const char *)beg);
|
|
|
|
return ft_nprint(cntx, (const char *)beg, len);
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
case W_CHAR_BUF:
|
|
|
|
len = (size_t)((const wchar_t *)end - (const wchar_t *)beg);
|
|
|
|
return ft_nwprint(cntx, (const wchar_t *)beg, len);
|
|
|
|
#endif /* FT_HAVE_WCHAR */
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
|
|
|
return ft_nu8print(cntx, beg, end);
|
|
|
|
#endif /* FT_HAVE_UTF8 */
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
return -1;
|
|
|
|
}
|
2018-03-05 19:08:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2019-08-14 21:01:57 +02:00
|
|
|
int buffer_printf(string_buffer_t *buffer, size_t buffer_row, conv_context_t *cntx, size_t vis_width,
|
|
|
|
const char *content_style_tag, const char *reset_content_style_tag)
|
2018-03-05 19:08:14 +01:00
|
|
|
{
|
2019-08-14 21:01:57 +02:00
|
|
|
const context_t *context = cntx->cntx;
|
|
|
|
fort_table_properties_t *props = context->table_properties;
|
|
|
|
size_t row = context->row;
|
|
|
|
size_t column = context->column;
|
2018-11-10 07:58:21 +01:00
|
|
|
|
2018-03-17 19:53:38 +01:00
|
|
|
if (buffer == NULL || buffer->str.data == NULL
|
2019-08-14 21:01:57 +02:00
|
|
|
|| buffer_row >= buffer_text_visible_height(buffer)) {
|
2018-03-05 19:08:14 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
size_t content_width = buffer_text_visible_width(buffer);
|
|
|
|
if (vis_width < content_width)
|
2018-03-05 19:08:14 +01:00
|
|
|
return -1;
|
|
|
|
|
2018-04-17 19:14:50 +02:00
|
|
|
size_t left = 0;
|
|
|
|
size_t right = 0;
|
2019-08-14 21:01:57 +02:00
|
|
|
switch (get_cell_property_value_hierarcial(props, row, column, FT_CPROP_TEXT_ALIGN)) {
|
2018-04-01 12:27:02 +02:00
|
|
|
case FT_ALIGNED_LEFT:
|
2018-03-05 19:08:14 +01:00
|
|
|
left = 0;
|
2019-08-14 21:01:57 +02:00
|
|
|
right = (vis_width) - content_width;
|
2018-03-05 19:08:14 +01:00
|
|
|
break;
|
2018-04-01 12:27:02 +02:00
|
|
|
case FT_ALIGNED_CENTER:
|
2019-08-14 21:01:57 +02:00
|
|
|
left = ((vis_width) - content_width) / 2;
|
|
|
|
right = ((vis_width) - content_width) - left;
|
2018-03-05 19:08:14 +01:00
|
|
|
break;
|
2018-04-01 12:27:02 +02:00
|
|
|
case FT_ALIGNED_RIGHT:
|
2019-08-14 21:01:57 +02:00
|
|
|
left = (vis_width) - content_width;
|
2018-03-05 19:08:14 +01:00
|
|
|
right = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-01-01 17:55:00 +01:00
|
|
|
size_t written = 0;
|
2018-03-31 17:13:30 +02:00
|
|
|
int tmp = 0;
|
2018-07-29 10:24:05 +02:00
|
|
|
ptrdiff_t str_it_width = 0;
|
2019-08-14 21:01:57 +02:00
|
|
|
const void *beg = NULL;
|
|
|
|
const void *end = NULL;
|
2019-08-25 08:33:17 +02:00
|
|
|
buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width);
|
2018-01-21 09:19:18 +01:00
|
|
|
if (beg == NULL || end == NULL)
|
|
|
|
return -1;
|
2018-04-17 19:14:50 +02:00
|
|
|
if (str_it_width < 0 || content_width < (size_t)str_it_width)
|
2019-08-14 21:01:57 +02:00
|
|
|
return -1;
|
2018-04-17 19:14:50 +02:00
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
size_t padding = content_width - (size_t)str_it_width;
|
2018-11-10 07:58:21 +01:00
|
|
|
|
2019-08-14 21:01:57 +02:00
|
|
|
CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE));
|
|
|
|
CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, content_style_tag));
|
|
|
|
CHCK_RSLT_ADD_TO_WRITTEN(buffer_print_range(cntx, beg, end));
|
|
|
|
CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag));
|
|
|
|
CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, padding, FT_SPACE));
|
|
|
|
CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, right, FT_SPACE));
|
2019-01-01 17:55:00 +01:00
|
|
|
return (int)written;
|
2018-03-05 19:08:14 +01:00
|
|
|
|
2018-03-31 17:13:30 +02:00
|
|
|
clear:
|
|
|
|
return -1;
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
2018-01-17 19:34:15 +01:00
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
FT_INTERNAL
|
2019-08-14 21:01:57 +02:00
|
|
|
size_t string_buffer_width_capacity(const string_buffer_t *buffer)
|
2018-03-05 19:08:14 +01:00
|
|
|
{
|
|
|
|
assert(buffer);
|
2019-08-25 08:33:17 +02:00
|
|
|
switch (buffer->type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
return buffer->data_sz;
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
case W_CHAR_BUF:
|
|
|
|
return buffer->data_sz / sizeof(wchar_t);
|
|
|
|
#endif
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
|
|
|
return buffer->data_sz / 4;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
return 0;
|
|
|
|
}
|
2018-03-05 19:08:14 +01:00
|
|
|
}
|
|
|
|
|
2018-09-01 14:37:01 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
2018-03-05 19:08:14 +01:00
|
|
|
void *buffer_get_data(string_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
2018-03-17 19:53:38 +01:00
|
|
|
return buffer->str.data;
|
2018-03-05 19:08:14 +01:00
|
|
|
}
|
2019-08-25 09:01:47 +02:00
|
|
|
|
|
|
|
FT_INTERNAL
|
|
|
|
int buffer_check_align(string_buffer_t *buffer)
|
|
|
|
{
|
|
|
|
assert(buffer);
|
|
|
|
assert(buffer->str.data);
|
|
|
|
|
|
|
|
switch (buffer->type) {
|
|
|
|
case CHAR_BUF:
|
|
|
|
return 1;
|
|
|
|
#ifdef FT_HAVE_WCHAR
|
|
|
|
case W_CHAR_BUF:
|
2019-08-25 09:18:41 +02:00
|
|
|
return (((unsigned long)buffer->str.data) & (sizeof(wchar_t) - 1)) == 0;
|
2019-08-25 09:01:47 +02:00
|
|
|
#endif
|
|
|
|
#ifdef FT_HAVE_UTF8
|
|
|
|
case UTF8_BUF:
|
|
|
|
return 1;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|