libfort/src/string_buffer.c

500 lines
12 KiB
C
Raw Normal View History

2018-01-17 19:22:57 +01:00
#include "string_buffer.h"
#include "options.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>
2018-05-06 12:12:28 +02:00
#include <wchar.h>
2018-01-17 19:34:15 +01:00
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);
2018-03-21 18:46:27 +01:00
return mk_wcswidth(beg, (end - beg));
}
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);
if (buf->type == CharBuf) {
return strlen(buf->str.cstr);
2018-03-05 19:08:14 +01:00
} else {
return wcslen(buf->str.wstr);
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;
}
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;
}
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-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-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-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-01-21 09:19:18 +01:00
2018-09-01 14:37:01 +02:00
FT_INTERNAL
2018-03-31 12:33:37 +02:00
string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type type)
2018-01-17 19:22:57 +01:00
{
2018-03-05 19:08:14 +01:00
size_t sz = (number_of_chars) * (type == CharBuf ? sizeof(char) : sizeof(wchar_t));
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;
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
2018-05-04 20:25:29 +02:00
if (sz && type == CharBuf) {
result->str.cstr[0] = '\0';
} else if (sz && type == WCharBuf) {
result->str.wstr[0] = L'\0';
}
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;
F_FREE(buffer->str.data);
buffer->str.data = NULL;
2018-01-17 19:22:57 +01:00
F_FREE(buffer);
}
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
}
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);
size_t sz = strlen(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-05 19:08:14 +01:00
while (sz >= string_buffer_capacity(buffer)) {
int status = realloc_string_buffer_without_copy(buffer);
2018-05-05 17:38:45 +02:00
if (!FT_IS_SUCCESS(status)) {
2018-03-05 19:08:14 +01:00
return status;
}
}
F_FREE(buffer->str.data);
buffer->str.cstr = copy;
2018-03-05 19:08:14 +01:00
buffer->type = CharBuf;
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);
size_t sz = wcslen(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
while (sz >= string_buffer_capacity(buffer)) {
2018-01-17 19:22:57 +01:00
int status = realloc_string_buffer_without_copy(buffer);
2018-05-05 17:38:45 +02:00
if (!FT_IS_SUCCESS(status)) {
2018-01-17 19:22:57 +01:00
return status;
}
}
F_FREE(buffer->str.data);
buffer->str.wstr = copy;
2018-03-05 19:08:14 +01:00
buffer->type = WCharBuf;
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
2018-09-01 14:37:01 +02:00
FT_INTERNAL
2018-01-17 19:22:57 +01:00
size_t buffer_text_height(string_buffer_t *buffer)
{
if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) {
2018-01-17 19:22:57 +01:00
return 0;
}
2018-03-05 19:08:14 +01:00
if (buffer->type == CharBuf)
return 1 + strchr_count(buffer->str.cstr, '\n');
2018-03-05 19:08:14 +01:00
else
return 1 + wstrchr_count(buffer->str.wstr, L'\n');
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
size_t buffer_text_width(string_buffer_t *buffer)
{
2018-01-21 09:19:18 +01:00
size_t max_length = 0;
int n = 0;
2018-03-05 19:08:14 +01:00
if (buffer->type == CharBuf) {
while (1) {
const char *beg = NULL;
const char *end = NULL;
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;
max_length = MAX(max_length, (size_t)(end - beg));
2018-03-05 19:08:14 +01:00
++n;
}
} else {
while (1) {
const wchar_t *beg = NULL;
const wchar_t *end = NULL;
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;
2018-03-21 18:46:27 +01:00
int line_width = mk_wcswidth(beg, 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-01-17 19:22:57 +01:00
}
}
2018-09-01 14:37:01 +02:00
FT_INTERNAL
2018-03-29 20:25:04 +02:00
int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t buf_len, const context_t *context)
2018-01-17 19:22:57 +01:00
{
2018-03-05 19:08:14 +01:00
#define CHAR_TYPE char
#define NULL_CHAR '\0'
#define NEWLINE_CHAR '\n'
2018-05-02 20:16:41 +02:00
#define SPACE_CHAR " "
2018-03-05 19:08:14 +01:00
#define SNPRINTF_FMT_STR "%*s"
#define SNPRINTF snprintf
#define BUFFER_STR str.cstr
2018-05-02 20:16:41 +02:00
//#define SNPRINT_N_CHARS snprint_n_chars
#define SNPRINT_N_STRINGS snprint_n_strings
2018-03-05 19:08:14 +01:00
#define STR_N_SUBSTRING str_n_substring
2018-03-21 18:46:27 +01:00
#define STR_ITER_WIDTH str_iter_width
2018-03-05 19:08:14 +01:00
if (buffer == NULL || buffer->str.data == NULL
2018-03-31 12:33:37 +02:00
|| buffer_row >= buffer_text_height(buffer) || buf_len == 0) {
2018-01-17 19:22:57 +01:00
return -1;
}
size_t content_width = buffer_text_width(buffer);
if ((buf_len - 1) < content_width)
return -1;
2018-04-17 19:14:50 +02:00
size_t left = 0;
size_t right = 0;
2018-01-17 19:22:57 +01:00
2018-03-25 10:11:08 +02:00
switch (get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TEXT_ALIGN)) {
2018-04-01 12:27:02 +02:00
case FT_ALIGNED_LEFT:
2018-01-17 19:22:57 +01:00
left = 0;
right = (buf_len - 1) - content_width;
break;
2018-04-01 12:27:02 +02:00
case FT_ALIGNED_CENTER:
2018-01-17 19:22:57 +01:00
left = ((buf_len - 1) - content_width) / 2;
right = ((buf_len - 1) - content_width) - left;
break;
2018-04-01 12:27:02 +02:00
case FT_ALIGNED_RIGHT:
2018-01-17 19:22:57 +01:00
left = (buf_len - 1) - content_width;
right = 0;
break;
default:
assert(0);
break;
}
int 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;
2018-03-05 19:08:14 +01:00
const CHAR_TYPE *beg = NULL;
const CHAR_TYPE *end = NULL;
2018-03-31 17:13:30 +02:00
CHAR_TYPE old_value;
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, left, SPACE_CHAR));
2018-03-31 17:13:30 +02:00
2018-03-05 19:08:14 +01:00
STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end);
if (beg == NULL || end == NULL)
return -1;
2018-03-31 17:13:30 +02:00
old_value = *end;
2018-03-05 19:08:14 +01:00
*(CHAR_TYPE *)end = NULL_CHAR;
2018-07-29 10:24:05 +02:00
str_it_width = STR_ITER_WIDTH(beg, end);
2018-04-17 19:14:50 +02:00
if (str_it_width < 0 || content_width < (size_t)str_it_width)
return - 1;
2018-03-31 17:13:30 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg));
2018-03-05 19:08:14 +01:00
*(CHAR_TYPE *)end = old_value;
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR));
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, right, SPACE_CHAR));
2018-03-05 19:08:14 +01:00
return written;
2018-03-31 17:13:30 +02:00
clear:
return -1;
2018-03-05 19:08:14 +01:00
#undef CHAR_TYPE
#undef NULL_CHAR
#undef NEWLINE_CHAR
#undef SPACE_CHAR
#undef SNPRINTF_FMT_STR
#undef SNPRINTF
#undef BUFFER_STR
2018-05-02 20:16:41 +02:00
//#undef SNPRINT_N_CHARS
#undef SNPRINT_N_STRINGS
2018-03-05 19:08:14 +01:00
#undef STR_N_SUBSTRING
2018-03-21 18:46:27 +01:00
#undef STR_ITER_WIDTH
2018-03-05 19:08:14 +01:00
}
2018-09-01 14:37:01 +02:00
#ifdef FT_HAVE_WCHAR
FT_INTERNAL
2018-03-29 20:25:04 +02:00
int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t buf_len, const context_t *context)
2018-03-05 19:08:14 +01:00
{
#define CHAR_TYPE wchar_t
#define NULL_CHAR L'\0'
#define NEWLINE_CHAR L'\n'
2018-05-02 20:16:41 +02:00
#define SPACE_CHAR " "
2018-03-05 19:08:14 +01:00
#define SNPRINTF_FMT_STR L"%*ls"
#define SNPRINTF swprintf
#define BUFFER_STR str.wstr
2018-05-02 20:16:41 +02:00
//#define SNPRINT_N_CHARS wsnprint_n_chars
#define SNPRINT_N_STRINGS wsnprint_n_string
2018-03-05 19:08:14 +01:00
#define STR_N_SUBSTRING wstr_n_substring
2018-03-21 18:46:27 +01:00
#define STR_ITER_WIDTH wcs_iter_width
2018-03-05 19:08:14 +01:00
if (buffer == NULL || buffer->str.data == NULL
2018-03-31 12:33:37 +02:00
|| buffer_row >= buffer_text_height(buffer) || buf_len == 0) {
2018-03-05 19:08:14 +01:00
return -1;
}
size_t content_width = buffer_text_width(buffer);
if ((buf_len - 1) < content_width)
return -1;
2018-04-17 19:14:50 +02:00
size_t left = 0;
size_t right = 0;
2018-03-05 19:08:14 +01:00
2018-03-25 10:11:08 +02:00
switch (get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TEXT_ALIGN)) {
2018-04-01 12:27:02 +02:00
case FT_ALIGNED_LEFT:
2018-03-05 19:08:14 +01:00
left = 0;
right = (buf_len - 1) - content_width;
break;
2018-04-01 12:27:02 +02:00
case FT_ALIGNED_CENTER:
2018-03-05 19:08:14 +01:00
left = ((buf_len - 1) - content_width) / 2;
right = ((buf_len - 1) - content_width) - left;
break;
2018-04-01 12:27:02 +02:00
case FT_ALIGNED_RIGHT:
2018-03-05 19:08:14 +01:00
left = (buf_len - 1) - content_width;
right = 0;
break;
default:
assert(0);
break;
}
int 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;
2018-03-05 19:08:14 +01:00
const CHAR_TYPE *beg = NULL;
const CHAR_TYPE *end = NULL;
2018-03-31 17:13:30 +02:00
CHAR_TYPE old_value;
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, left, SPACE_CHAR));
2018-03-31 17:13:30 +02:00
2018-03-05 19:08:14 +01:00
STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end);
2018-01-21 09:19:18 +01:00
if (beg == NULL || end == NULL)
return -1;
2018-03-31 17:13:30 +02:00
old_value = *end;
2018-03-05 19:08:14 +01:00
*(CHAR_TYPE *)end = NULL_CHAR;
2018-01-21 09:19:18 +01:00
2018-07-29 10:24:05 +02:00
str_it_width = STR_ITER_WIDTH(beg, end);
2018-04-17 19:14:50 +02:00
if (str_it_width < 0 || content_width < (size_t)str_it_width)
return - 1;
2018-03-31 17:13:30 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg));
2018-03-05 19:08:14 +01:00
*(CHAR_TYPE *)end = old_value;
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR));
CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, right, SPACE_CHAR));
2018-01-17 19:22:57 +01:00
return written;
2018-03-05 19:08:14 +01:00
2018-03-31 17:13:30 +02:00
clear:
return -1;
2018-03-05 19:08:14 +01:00
#undef CHAR_TYPE
#undef NULL_CHAR
#undef NEWLINE_CHAR
#undef SPACE_CHAR
#undef SNPRINTF_FMT_STR
#undef SNPRINTF
#undef BUFFER_STR
2018-05-02 20:16:41 +02:00
//#undef SNPRINT_N_CHARS
#undef SNPRINT_N_STRINGS
2018-03-05 19:08:14 +01:00
#undef STR_N_SUBSTRING
2018-03-21 18:46:27 +01:00
#undef STR_ITER_WIDTH
2018-01-17 19:22:57 +01:00
}
2018-09-01 14:37:01 +02:00
#endif /* FT_HAVE_WCHAR */
2018-01-17 19:34:15 +01:00
2018-09-01 14:37:01 +02:00
FT_INTERNAL
2018-03-05 19:08:14 +01:00
size_t string_buffer_capacity(const string_buffer_t *buffer)
{
assert(buffer);
if (buffer->type == CharBuf)
return buffer->data_sz;
else
return buffer->data_sz / sizeof(wchar_t);
}
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);
return buffer->str.data;
2018-03-05 19:08:14 +01:00
}