From 3e961b053a67a959ecb6bc7da57310608628adb4 Mon Sep 17 00:00:00 2001 From: seleznevae Date: Wed, 14 Aug 2019 22:01:57 +0300 Subject: [PATCH] [C] Refactoring for utf-8 --- amalgamate.py | 1 + example/main.c | 59 + lib/fort.c | 2899 +++++++++++++++++++-------- lib/fort.h | 18 +- src/cell.c | 134 +- src/cell.h | 5 +- src/fort.h | 18 +- src/fort_impl.c | 366 ++-- src/fort_utils.c | 164 +- src/fort_utils.h | 57 +- src/row.c | 357 +--- src/row.h | 20 +- src/string_buffer.c | 434 ++-- src/string_buffer.h | 42 +- src/table.c | 5 + src/table.h | 7 + tests/bb_tests/test_table_basic.c | 306 +++ tests/main_test.c | 6 + tests/wb_tests/test_string_buffer.c | 372 +++- 19 files changed, 3532 insertions(+), 1738 deletions(-) diff --git a/amalgamate.py b/amalgamate.py index b6cf07e..e8e7c2f 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -99,6 +99,7 @@ def main(): "fort_utils.h", "vector.h", "wcwidth.h", + "utf8.h", "string_buffer.h", "properties.h", "cell.h", diff --git a/example/main.c b/example/main.c index 2ce29ae..0ba95fd 100644 --- a/example/main.c +++ b/example/main.c @@ -231,6 +231,65 @@ void colorfull_table(void) ft_destroy_table(table); #endif + + +#if defined(FT_HAVE_UTF8) + + table = ft_create_table(); + ft_set_border_style(table, FT_NICE_STYLE); + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + + /* Filling table with data */ + ft_u8write_ln(table, "Тест", "Итерации", "ms/op", "Тики", "Результат"); + ft_u8write_ln(table, "n-body", "1000", "1.6", "1,500,000", "✔"); + ft_add_separator(table); + ft_u8write_ln(table, "regex-redux", "1000", "0.8", "8,000,000"); + ft_u8write_ln(table, "", "2500", "3.9", "27,000,000", "✖"); + ft_u8write_ln(table, "", "10000", "12.5", "96,800,000"); + ft_add_separator(table); + ft_u8write_ln(table, "mandelbrot", "1000", "8.1", "89,000,000"); + ft_u8write_ln(table, "", "2500", "19.8", "320,000,000", "✔"); + ft_u8write_ln(table, "", "10000", "60.7", "987,000,000"); + ft_add_separator(table); + ft_set_cell_span(table, 8, 0, 4); + ft_u8write_ln(table, "Итог", "", "", "", "✖"); + + /* Setting text styles */ + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); + ft_set_cell_prop(table, 8, FT_ANY_COLUMN, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); + ft_set_cell_prop(table, FT_ANY_ROW, 0, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); + ft_set_cell_prop(table, FT_ANY_ROW, 4, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); + ft_set_cell_prop(table, FT_ANY_ROW, FT_ANY_COLUMN, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_ITALIC); + + /* Set alignment */ + ft_set_cell_prop(table, FT_ANY_ROW, 1, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_RIGHT); + ft_set_cell_prop(table, FT_ANY_ROW, 2, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_RIGHT); + ft_set_cell_prop(table, FT_ANY_ROW, 3, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_RIGHT); + ft_set_cell_prop(table, FT_ANY_ROW, 4, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_CENTER); + ft_set_cell_prop(table, 8, 0, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_CENTER); + + /* Set colors */ + ft_set_cell_prop(table, 1, 4, FT_CPROP_CONT_FG_COLOR, FT_COLOR_GREEN); + ft_set_cell_prop(table, 3, 4, FT_CPROP_CONT_FG_COLOR, FT_COLOR_RED); + ft_set_cell_prop(table, 6, 4, FT_CPROP_CONT_FG_COLOR, FT_COLOR_GREEN); + ft_set_cell_prop(table, 8, 4, FT_CPROP_CONT_FG_COLOR, FT_COLOR_RED); + ft_set_cell_prop(table, 3, 2, FT_CPROP_CONT_FG_COLOR, FT_COLOR_RED); + ft_set_cell_prop(table, 4, 3, FT_CPROP_CONT_BG_COLOR, FT_COLOR_LIGHT_RED); + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_CONT_FG_COLOR, FT_COLOR_LIGHT_BLUE); + + /* Move table to the center of the screen */ + ft_set_tbl_prop(table, FT_TPROP_TOP_MARGIN, 1); + ft_set_tbl_prop(table, FT_TPROP_LEFT_MARGIN, 10); + + const char *table_str = ft_to_u8string(table); + if (table_wstr) { + printf("Table:\n%s\n\n ", table_str); + } else { + printf("Table conversion failed !!!\n "); + } + + ft_destroy_table(table); +#endif } diff --git a/lib/fort.c b/lib/fort.c index c85ea17..4ab0391 100644 --- a/lib/fort.c +++ b/lib/fort.c @@ -72,12 +72,16 @@ extern char g_col_separator; #define F_REALLOC fort_realloc #define F_STRDUP fort_strdup #define F_WCSDUP fort_wcsdup +/* @todo: replace with custom impl !!!*/ +#define F_UTF8DUP utf8dup #define F_CREATE(type) ((type *)F_CALLOC(sizeof(type), 1)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define FT_NEWLINE "\n" +#define FT_SPACE " " enum PolicyOnNull { Create, @@ -90,6 +94,20 @@ enum F_BOOL { F_TRUE = 1 }; +enum str_buf_type { + CHAR_BUF, +#ifdef FT_HAVE_WCHAR + W_CHAR_BUF, +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + UTF8_BUF, +#endif /* FT_HAVE_WCHAR */ + TYPE_END +}; + + +typedef const char ** str_arr; + #define FT_STR_2_CAT_(arg1, arg2) \ arg1##arg2 @@ -154,6 +172,16 @@ enum request_geom_type { INTERN_REPR_GEOMETRY }; +struct conv_context { + char *buf_origin; + char *buf; + size_t raw_avail; + struct fort_context *cntx; + enum str_buf_type b_type; +}; +typedef struct conv_context conv_context_t; + + /***************************************************************************** * LIBFORT helpers *****************************************************************************/ @@ -181,18 +209,38 @@ size_t number_of_columns_in_format_wstring(const wchar_t *fmt); #endif FT_INTERNAL -int snprint_n_strings(char *buf, size_t length, size_t n, const char *str); +int print_n_strings(conv_context_t *cntx, size_t n, const char *str); + -#if defined(FT_HAVE_WCHAR) FT_INTERNAL -int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); -#endif +int ft_nprint(conv_context_t *cntx, const char *str, size_t strlen); +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +int ft_nwprint(conv_context_t *cntx, const wchar_t *str, size_t strlen); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +int ft_nu8print(conv_context_t *cntx, const void *beg, const void *end); +#endif /* FT_HAVE_UTF8 */ +/*#define PRINT_DEBUG_INFO fprintf(stderr, "error in %s(%s:%d)\n", __FUNCTION__, __FILE__, __LINE__);*/ +#define PRINT_DEBUG_INFO + +#define FT_CHECK(statement) \ + do { \ + tmp = statement; \ + if (tmp < 0) {\ + PRINT_DEBUG_INFO \ + goto clear; \ + } \ + } while(0) + #define CHCK_RSLT_ADD_TO_WRITTEN(statement) \ do { \ tmp = statement; \ if (tmp < 0) {\ + PRINT_DEBUG_INFO \ goto clear; \ } \ written += (size_t)tmp; \ @@ -202,6 +250,7 @@ int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); do { \ tmp = statement; \ if (tmp < 0) {\ + PRINT_DEBUG_INFO \ goto clear; \ } \ invisible_written += (size_t)tmp; \ @@ -302,6 +351,1306 @@ int mk_wcswidth(const wchar_t *pwcs, size_t n); ********************************************************/ +/******************************************************** + Begin of file "utf8.h" + ********************************************************/ + +// The latest version of this library is available on GitHub; +// https://github.com/sheredom/utf8.h + +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to + +#ifndef SHEREDOM_UTF8_H_INCLUDED +#define SHEREDOM_UTF8_H_INCLUDED + +#if defined(_MSC_VER) +#pragma warning(push) + +// disable 'bytes padding added after construct' warning +#pragma warning(disable : 4820) +#endif + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(_MSC_VER) +typedef __int32 utf8_int32_t; +#else +#include +typedef int32_t utf8_int32_t; +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wcast-qual" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__clang__) || defined(__GNUC__) +#define utf8_nonnull __attribute__((nonnull)) +#define utf8_pure __attribute__((pure)) +#define utf8_restrict __restrict__ +#define utf8_weak __attribute__((weak)) +#elif defined(_MSC_VER) +#define utf8_nonnull +#define utf8_pure +#define utf8_restrict __restrict +#define utf8_weak __inline +#else +#error Non clang, non gcc, non MSVC compiler found! +#endif + +#ifdef __cplusplus +#define utf8_null NULL +#else +#define utf8_null 0 +#endif + +// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 > +// src2 respectively, case insensitive. +utf8_nonnull utf8_pure utf8_weak int utf8casecmp(const void *src1, + const void *src2); + +// Append the utf8 string src onto the utf8 string dst. +utf8_nonnull utf8_weak void *utf8cat(void *utf8_restrict dst, + const void *utf8_restrict src); + +// Find the first match of the utf8 codepoint chr in the utf8 string src. +utf8_nonnull utf8_pure utf8_weak void *utf8chr(const void *src, + utf8_int32_t chr); + +// Return less than 0, 0, greater than 0 if src1 < src2, +// src1 == src2, src1 > src2 respectively. +utf8_nonnull utf8_pure utf8_weak int utf8cmp(const void *src1, + const void *src2); + +// Copy the utf8 string src onto the memory allocated in dst. +utf8_nonnull utf8_weak void *utf8cpy(void *utf8_restrict dst, + const void *utf8_restrict src); + +// Number of utf8 codepoints in the utf8 string src that consists entirely +// of utf8 codepoints not from the utf8 string reject. +utf8_nonnull utf8_pure utf8_weak size_t utf8cspn(const void *src, + const void *reject); + +// Duplicate the utf8 string src by getting its size, malloc'ing a new buffer +// copying over the data, and returning that. Or 0 if malloc failed. +utf8_nonnull utf8_weak void *utf8dup(const void *src); + +// Number of utf8 codepoints in the utf8 string str, +// excluding the null terminating byte. +utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str); + +// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 > +// src2 respectively, case insensitive. Checking at most n bytes of each utf8 +// string. +utf8_nonnull utf8_pure utf8_weak int utf8ncasecmp(const void *src1, + const void *src2, size_t n); + +// Append the utf8 string src onto the utf8 string dst, +// writing at most n+1 bytes. Can produce an invalid utf8 +// string if n falls partway through a utf8 codepoint. +utf8_nonnull utf8_weak void *utf8ncat(void *utf8_restrict dst, + const void *utf8_restrict src, size_t n); + +// Return less than 0, 0, greater than 0 if src1 < src2, +// src1 == src2, src1 > src2 respectively. Checking at most n +// bytes of each utf8 string. +utf8_nonnull utf8_pure utf8_weak int utf8ncmp(const void *src1, + const void *src2, size_t n); + +// Copy the utf8 string src onto the memory allocated in dst. +// Copies at most n bytes. If there is no terminating null byte in +// the first n bytes of src, the string placed into dst will not be +// null-terminated. If the size (in bytes) of src is less than n, +// extra null terminating bytes are appended to dst such that at +// total of n bytes are written. Can produce an invalid utf8 +// string if n falls partway through a utf8 codepoint. +utf8_nonnull utf8_weak void *utf8ncpy(void *utf8_restrict dst, + const void *utf8_restrict src, size_t n); + +// Similar to utf8dup, except that at most n bytes of src are copied. If src is +// longer than n, only n bytes are copied and a null byte is added. +// +// Returns a new string if successful, 0 otherwise +utf8_nonnull utf8_weak void *utf8ndup(const void *src, size_t n); + +// Locates the first occurence in the utf8 string str of any byte in the +// utf8 string accept, or 0 if no match was found. +utf8_nonnull utf8_pure utf8_weak void *utf8pbrk(const void *str, + const void *accept); + +// Find the last match of the utf8 codepoint chr in the utf8 string src. +utf8_nonnull utf8_pure utf8_weak void *utf8rchr(const void *src, int chr); + +// Number of bytes in the utf8 string str, +// including the null terminating byte. +utf8_nonnull utf8_pure utf8_weak size_t utf8size(const void *str); + +// Number of utf8 codepoints in the utf8 string src that consists entirely +// of utf8 codepoints from the utf8 string accept. +utf8_nonnull utf8_pure utf8_weak size_t utf8spn(const void *src, + const void *accept); + +// The position of the utf8 string needle in the utf8 string haystack. +utf8_nonnull utf8_pure utf8_weak void *utf8str(const void *haystack, + const void *needle); + +// The position of the utf8 string needle in the utf8 string haystack, case +// insensitive. +utf8_nonnull utf8_pure utf8_weak void *utf8casestr(const void *haystack, + const void *needle); + +// Return 0 on success, or the position of the invalid +// utf8 codepoint on failure. +utf8_nonnull utf8_pure utf8_weak void *utf8valid(const void *str); + +// Sets out_codepoint to the next utf8 codepoint in str, and returns the address +// of the utf8 codepoint after the current one in str. +utf8_nonnull utf8_weak void * +utf8codepoint(const void *utf8_restrict str, + utf8_int32_t *utf8_restrict out_codepoint); + +// Returns the size of the given codepoint in bytes. +utf8_weak size_t utf8codepointsize(utf8_int32_t chr); + +// Write a codepoint to the given string, and return the address to the next +// place after the written codepoint. Pass how many bytes left in the buffer to +// n. If there is not enough space for the codepoint, this function returns +// null. +utf8_nonnull utf8_weak void *utf8catcodepoint(void *utf8_restrict str, + utf8_int32_t chr, size_t n); + +// Returns 1 if the given character is lowercase, or 0 if it is not. +utf8_weak int utf8islower(utf8_int32_t chr); + +// Returns 1 if the given character is uppercase, or 0 if it is not. +utf8_weak int utf8isupper(utf8_int32_t chr); + +// Transform the given string into all lowercase codepoints. +utf8_nonnull utf8_weak void utf8lwr(void *utf8_restrict str); + +// Transform the given string into all uppercase codepoints. +utf8_nonnull utf8_weak void utf8upr(void *utf8_restrict str); + +// Make a codepoint lower case if possible. +utf8_weak utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp); + +// Make a codepoint upper case if possible. +utf8_weak utf8_int32_t utf8uprcodepoint(utf8_int32_t cp); + +#undef utf8_weak +#undef utf8_pure +#undef utf8_nonnull + +int utf8casecmp(const void *src1, const void *src2) +{ + utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; + + for (;;) { + src1 = utf8codepoint(src1, &src1_cp); + src2 = utf8codepoint(src2, &src2_cp); + + // Take a copy of src1 & src2 + src1_orig_cp = src1_cp; + src2_orig_cp = src2_cp; + + // Lower the srcs if required + src1_cp = utf8lwrcodepoint(src1_cp); + src2_cp = utf8lwrcodepoint(src2_cp); + + // Check if the lowered codepoints match + if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) { + return 0; + } else if (src1_cp == src2_cp) { + continue; + } + + // If they don't match, then we return which of the original's are less + if (src1_orig_cp < src2_orig_cp) { + return -1; + } else if (src1_orig_cp > src2_orig_cp) { + return 1; + } + } +} + +void *utf8cat(void *utf8_restrict dst, const void *utf8_restrict src) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + + // find the null terminating byte in dst + while ('\0' != *d) { + d++; + } + + // overwriting the null terminating byte in dst, append src byte-by-byte + while ('\0' != *s) { + *d++ = *s++; + } + + // write out a new null terminating byte into dst + *d = '\0'; + + return dst; +} + +void *utf8chr(const void *src, utf8_int32_t chr) +{ + char c[5] = {'\0', '\0', '\0', '\0', '\0'}; + + if (0 == chr) { + // being asked to return position of null terminating byte, so + // just run s to the end, and return! + const char *s = (const char *)src; + while ('\0' != *s) { + s++; + } + return (void *)s; + } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + c[0] = (char)chr; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + c[0] = 0xc0 | (char)(chr >> 6); + c[1] = 0x80 | (char)(chr & 0x3f); + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xe0 | (char)(chr >> 12); + c[1] = 0x80 | (char)((chr >> 6) & 0x3f); + c[2] = 0x80 | (char)(chr & 0x3f); + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xf0 | (char)(chr >> 18); + c[1] = 0x80 | (char)((chr >> 12) & 0x3f); + c[2] = 0x80 | (char)((chr >> 6) & 0x3f); + c[3] = 0x80 | (char)(chr & 0x3f); + } + + // we've made c into a 2 utf8 codepoint string, one for the chr we are + // seeking, another for the null terminating byte. Now use utf8str to + // search + return utf8str(src, c); +} + +int utf8cmp(const void *src1, const void *src2) +{ + const unsigned char *s1 = (const unsigned char *)src1; + const unsigned char *s2 = (const unsigned char *)src2; + + while (('\0' != *s1) || ('\0' != *s2)) { + if (*s1 < *s2) { + return -1; + } else if (*s1 > *s2) { + return 1; + } + + s1++; + s2++; + } + + // both utf8 strings matched + return 0; +} + +int utf8coll(const void *src1, const void *src2); + +void *utf8cpy(void *utf8_restrict dst, const void *utf8_restrict src) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + + // overwriting anything previously in dst, write byte-by-byte + // from src + while ('\0' != *s) { + *d++ = *s++; + } + + // append null terminating byte + *d = '\0'; + + return dst; +} + +size_t utf8cspn(const void *src, const void *reject) +{ + const char *s = (const char *)src; + size_t chars = 0; + + while ('\0' != *s) { + const char *r = (const char *)reject; + size_t offset = 0; + + while ('\0' != *r) { + // checking that if *r is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *r)) && (0 < offset)) { + return chars; + } else { + if (*r == s[offset]) { + // part of a utf8 codepoint matched, so move our checking + // onwards to the next byte + offset++; + r++; + } else { + // r could be in the middle of an unmatching utf8 code point, + // so we need to march it on to the next character beginning, + + do { + r++; + } while (0x80 == (0xc0 & *r)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // the current utf8 codepoint in src did not match reject, but src + // could have been partway through a utf8 codepoint, so we need to + // march it onto the next utf8 codepoint starting byte + do { + s++; + } while ((0x80 == (0xc0 & *s))); + chars++; + } + + return chars; +} + +size_t utf8size(const void *str); + +void *utf8dup(const void *src) +{ + const char *s = (const char *)src; + char *n = utf8_null; + + // figure out how many bytes (including the terminator) we need to copy first + size_t bytes = utf8size(src); + + n = (char *)malloc(bytes); + + if (utf8_null == n) { + // out of memory so we bail + return utf8_null; + } else { + bytes = 0; + + // copy src byte-by-byte into our new utf8 string + while ('\0' != s[bytes]) { + n[bytes] = s[bytes]; + bytes++; + } + + // append null terminating byte + n[bytes] = '\0'; + return n; + } +} + +void *utf8fry(const void *str); + +size_t utf8len(const void *str) +{ + const unsigned char *s = (const unsigned char *)str; + size_t length = 0; + + while ('\0' != *s) { + if (0xf0 == (0xf8 & *s)) { + // 4-byte utf8 code point (began with 0b11110xxx) + s += 4; + } else if (0xe0 == (0xf0 & *s)) { + // 3-byte utf8 code point (began with 0b1110xxxx) + s += 3; + } else if (0xc0 == (0xe0 & *s)) { + // 2-byte utf8 code point (began with 0b110xxxxx) + s += 2; + } else { // if (0x00 == (0x80 & *s)) { + // 1-byte ascii (began with 0b0xxxxxxx) + s += 1; + } + + // no matter the bytes we marched s forward by, it was + // only 1 utf8 codepoint + length++; + } + + return length; +} + +int utf8ncasecmp(const void *src1, const void *src2, size_t n) +{ + utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; + + do { + const unsigned char *const s1 = (const unsigned char *)src1; + const unsigned char *const s2 = (const unsigned char *)src2; + + // first check that we have enough bytes left in n to contain an entire + // codepoint + if (0 == n) { + return 0; + } + + if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) { + const utf8_int32_t c1 = (0xe0 & *s1); + const utf8_int32_t c2 = (0xe0 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) { + const utf8_int32_t c1 = (0xf0 & *s1); + const utf8_int32_t c2 = (0xf0 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) { + const utf8_int32_t c1 = (0xf8 & *s1); + const utf8_int32_t c2 = (0xf8 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + src1 = utf8codepoint(src1, &src1_cp); + src2 = utf8codepoint(src2, &src2_cp); + n -= utf8codepointsize(src1_cp); + + // Take a copy of src1 & src2 + src1_orig_cp = src1_cp; + src2_orig_cp = src2_cp; + + // Lower srcs if required + src1_cp = utf8lwrcodepoint(src1_cp); + src2_cp = utf8lwrcodepoint(src2_cp); + + // Check if the lowered codepoints match + if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) { + return 0; + } else if (src1_cp == src2_cp) { + continue; + } + + // If they don't match, then we return which of the original's are less + if (src1_orig_cp < src2_orig_cp) { + return -1; + } else if (src1_orig_cp > src2_orig_cp) { + return 1; + } + } while (0 < n); + + // both utf8 strings matched + return 0; +} + +void *utf8ncat(void *utf8_restrict dst, const void *utf8_restrict src, + size_t n) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + + // find the null terminating byte in dst + while ('\0' != *d) { + d++; + } + + // overwriting the null terminating byte in dst, append src byte-by-byte + // stopping if we run out of space + do { + *d++ = *s++; + } while (('\0' != *s) && (0 != --n)); + + // write out a new null terminating byte into dst + *d = '\0'; + + return dst; +} + +int utf8ncmp(const void *src1, const void *src2, size_t n) +{ + const unsigned char *s1 = (const unsigned char *)src1; + const unsigned char *s2 = (const unsigned char *)src2; + + while ((0 != n--) && (('\0' != *s1) || ('\0' != *s2))) { + if (*s1 < *s2) { + return -1; + } else if (*s1 > *s2) { + return 1; + } + + s1++; + s2++; + } + + // both utf8 strings matched + return 0; +} + +void *utf8ncpy(void *utf8_restrict dst, const void *utf8_restrict src, + size_t n) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + size_t index; + + // overwriting anything previously in dst, write byte-by-byte + // from src + for (index = 0; index < n; index++) { + d[index] = s[index]; + if ('\0' == s[index]) { + break; + } + } + + // append null terminating byte + for (; index < n; index++) { + d[index] = 0; + } + + return dst; +} + +void *utf8ndup(const void *src, size_t n) +{ + const char *s = (const char *)src; + char *c = utf8_null; + size_t bytes = 0; + + // Find the end of the string or stop when n is reached + while ('\0' != s[bytes] && bytes < n) { + bytes++; + } + + // In case bytes is actually less than n, we need to set it + // to be used later in the copy byte by byte. + n = bytes; + + c = (char *)malloc(bytes + 1); + if (utf8_null == c) { + // out of memory so we bail + return utf8_null; + } + + bytes = 0; + + // copy src byte-by-byte into our new utf8 string + while ('\0' != s[bytes] && bytes < n) { + c[bytes] = s[bytes]; + bytes++; + } + + // append null terminating byte + c[bytes] = '\0'; + return c; +} + +void *utf8rchr(const void *src, int chr) +{ + const char *s = (const char *)src; + const char *match = utf8_null; + char c[5] = {'\0', '\0', '\0', '\0', '\0'}; + + if (0 == chr) { + // being asked to return position of null terminating byte, so + // just run s to the end, and return! + while ('\0' != *s) { + s++; + } + return (void *)s; + } else if (0 == ((int)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + c[0] = (char)chr; + } else if (0 == ((int)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + c[0] = 0xc0 | (char)(chr >> 6); + c[1] = 0x80 | (char)(chr & 0x3f); + } else if (0 == ((int)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xe0 | (char)(chr >> 12); + c[1] = 0x80 | (char)((chr >> 6) & 0x3f); + c[2] = 0x80 | (char)(chr & 0x3f); + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xf0 | (char)(chr >> 18); + c[1] = 0x80 | (char)((chr >> 12) & 0x3f); + c[2] = 0x80 | (char)((chr >> 6) & 0x3f); + c[3] = 0x80 | (char)(chr & 0x3f); + } + + // we've created a 2 utf8 codepoint string in c that is + // the utf8 character asked for by chr, and a null + // terminating byte + + while ('\0' != *s) { + size_t offset = 0; + + while (s[offset] == c[offset]) { + offset++; + } + + if ('\0' == c[offset]) { + // we found a matching utf8 code point + match = s; + s += offset; + } else { + s += offset; + + // need to march s along to next utf8 codepoint start + // (the next byte that doesn't match 0b10xxxxxx) + if ('\0' != *s) { + do { + s++; + } while (0x80 == (0xc0 & *s)); + } + } + } + + // return the last match we found (or 0 if no match was found) + return (void *)match; +} + +void *utf8pbrk(const void *str, const void *accept) +{ + const char *s = (const char *)str; + + while ('\0' != *s) { + const char *a = (const char *)accept; + size_t offset = 0; + + while ('\0' != *a) { + // checking that if *a is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *a)) && (0 < offset)) { + return (void *)s; + } else { + if (*a == s[offset]) { + // part of a utf8 codepoint matched, so move our checking + // onwards to the next byte + offset++; + a++; + } else { + // r could be in the middle of an unmatching utf8 code point, + // so we need to march it on to the next character beginning, + + do { + a++; + } while (0x80 == (0xc0 & *a)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // we found a match on the last utf8 codepoint + if (0 < offset) { + return (void *)s; + } + + // the current utf8 codepoint in src did not match accept, but src + // could have been partway through a utf8 codepoint, so we need to + // march it onto the next utf8 codepoint starting byte + do { + s++; + } while ((0x80 == (0xc0 & *s))); + } + + return utf8_null; +} + +size_t utf8size(const void *str) +{ + const char *s = (const char *)str; + size_t size = 0; + while ('\0' != s[size]) { + size++; + } + + // we are including the null terminating byte in the size calculation + size++; + return size; +} + +size_t utf8spn(const void *src, const void *accept) +{ + const char *s = (const char *)src; + size_t chars = 0; + + while ('\0' != *s) { + const char *a = (const char *)accept; + size_t offset = 0; + + while ('\0' != *a) { + // checking that if *r is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *a)) && (0 < offset)) { + // found a match, so increment the number of utf8 codepoints + // that have matched and stop checking whether any other utf8 + // codepoints in a match + chars++; + s += offset; + break; + } else { + if (*a == s[offset]) { + offset++; + a++; + } else { + // a could be in the middle of an unmatching utf8 codepoint, + // so we need to march it on to the next character beginning, + do { + a++; + } while (0x80 == (0xc0 & *a)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // if a got to its terminating null byte, then we didn't find a match. + // Return the current number of matched utf8 codepoints + if ('\0' == *a) { + return chars; + } + } + + return chars; +} + +void *utf8str(const void *haystack, const void *needle) +{ + const char *h = (const char *)haystack; + utf8_int32_t throwaway_codepoint; + + // if needle has no utf8 codepoints before the null terminating + // byte then return haystack + if ('\0' == *((const char *)needle)) { + return (void *)haystack; + } + + while ('\0' != *h) { + const char *maybeMatch = h; + const char *n = (const char *)needle; + + while (*h == *n && (*h != '\0' && *n != '\0')) { + n++; + h++; + } + + if ('\0' == *n) { + // we found the whole utf8 string for needle in haystack at + // maybeMatch, so return it + return (void *)maybeMatch; + } else { + // h could be in the middle of an unmatching utf8 codepoint, + // so we need to march it on to the next character beginning + // starting from the current character + h = (const char *)utf8codepoint(maybeMatch, &throwaway_codepoint); + } + } + + // no match + return utf8_null; +} + +void *utf8casestr(const void *haystack, const void *needle) +{ + const void *h = haystack; + + // if needle has no utf8 codepoints before the null terminating + // byte then return haystack + if ('\0' == *((const char *)needle)) { + return (void *)haystack; + } + + for (;;) { + const void *maybeMatch = h; + const void *n = needle; + utf8_int32_t h_cp, n_cp; + + // Get the next code point and track it + const void *nextH = h = utf8codepoint(h, &h_cp); + n = utf8codepoint(n, &n_cp); + + while ((0 != h_cp) && (0 != n_cp)) { + h_cp = utf8lwrcodepoint(h_cp); + n_cp = utf8lwrcodepoint(n_cp); + + // if we find a mismatch, bail out! + if (h_cp != n_cp) { + break; + } + + h = utf8codepoint(h, &h_cp); + n = utf8codepoint(n, &n_cp); + } + + if (0 == n_cp) { + // we found the whole utf8 string for needle in haystack at + // maybeMatch, so return it + return (void *)maybeMatch; + } + + if (0 == h_cp) { + // no match + return utf8_null; + } + + // Roll back to the next code point in the haystack to test + h = nextH; + } +} + +void *utf8valid(const void *str) +{ + const char *s = (const char *)str; + + while ('\0' != *s) { + if (0xf0 == (0xf8 & *s)) { + // ensure each of the 3 following bytes in this 4-byte + // utf8 codepoint began with 0b10xxxxxx + if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2])) || + (0x80 != (0xc0 & s[3]))) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 4 bytes + if (0x80 == (0xc0 & s[4])) { + return (void *)s; + } + + // ensure that the top 5 bits of this 4-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if ((0 == (0x07 & s[0])) && (0 == (0x30 & s[1]))) { + return (void *)s; + } + + // 4-byte utf8 code point (began with 0b11110xxx) + s += 4; + } else if (0xe0 == (0xf0 & *s)) { + // ensure each of the 2 following bytes in this 3-byte + // utf8 codepoint began with 0b10xxxxxx + if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2]))) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 3 bytes + if (0x80 == (0xc0 & s[3])) { + return (void *)s; + } + + // ensure that the top 5 bits of this 3-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if ((0 == (0x0f & s[0])) && (0 == (0x20 & s[1]))) { + return (void *)s; + } + + // 3-byte utf8 code point (began with 0b1110xxxx) + s += 3; + } else if (0xc0 == (0xe0 & *s)) { + // ensure the 1 following byte in this 2-byte + // utf8 codepoint began with 0b10xxxxxx + if (0x80 != (0xc0 & s[1])) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 2 bytes + if (0x80 == (0xc0 & s[2])) { + return (void *)s; + } + + // ensure that the top 4 bits of this 2-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if (0 == (0x1e & s[0])) { + return (void *)s; + } + + // 2-byte utf8 code point (began with 0b110xxxxx) + s += 2; + } else if (0x00 == (0x80 & *s)) { + // 1-byte ascii (began with 0b0xxxxxxx) + s += 1; + } else { + // we have an invalid 0b1xxxxxxx utf8 code point entry + return (void *)s; + } + } + + return utf8_null; +} + +void *utf8codepoint(const void *utf8_restrict str, + utf8_int32_t *utf8_restrict out_codepoint) +{ + const char *s = (const char *)str; + + if (0xf0 == (0xf8 & s[0])) { + // 4 byte utf8 codepoint + *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | + ((0x3f & s[2]) << 6) | (0x3f & s[3]); + s += 4; + } else if (0xe0 == (0xf0 & s[0])) { + // 3 byte utf8 codepoint + *out_codepoint = + ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]); + s += 3; + } else if (0xc0 == (0xe0 & s[0])) { + // 2 byte utf8 codepoint + *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]); + s += 2; + } else { + // 1 byte utf8 codepoint otherwise + *out_codepoint = s[0]; + s += 1; + } + + return (void *)s; +} + +size_t utf8codepointsize(utf8_int32_t chr) +{ + if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + return 1; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + return 2; + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + return 3; + } else { // if (0 == ((int)0xffe00000 & chr)) { + return 4; + } +} + +void *utf8catcodepoint(void *utf8_restrict str, utf8_int32_t chr, size_t n) +{ + char *s = (char *)str; + + if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + if (n < 1) { + return utf8_null; + } + s[0] = (char)chr; + s += 1; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + if (n < 2) { + return utf8_null; + } + s[0] = 0xc0 | (char)(chr >> 6); + s[1] = 0x80 | (char)(chr & 0x3f); + s += 2; + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + if (n < 3) { + return utf8_null; + } + s[0] = 0xe0 | (char)(chr >> 12); + s[1] = 0x80 | (char)((chr >> 6) & 0x3f); + s[2] = 0x80 | (char)(chr & 0x3f); + s += 3; + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + if (n < 4) { + return utf8_null; + } + s[0] = 0xf0 | (char)(chr >> 18); + s[1] = 0x80 | (char)((chr >> 12) & 0x3f); + s[2] = 0x80 | (char)((chr >> 6) & 0x3f); + s[3] = 0x80 | (char)(chr & 0x3f); + s += 4; + } + + return s; +} + +int utf8islower(utf8_int32_t chr) { return chr != utf8uprcodepoint(chr); } + +int utf8isupper(utf8_int32_t chr) { return chr != utf8lwrcodepoint(chr); } + +void utf8lwr(void *utf8_restrict str) +{ + void *p, *pn; + utf8_int32_t cp; + + p = (char *)str; + pn = utf8codepoint(p, &cp); + + while (cp != 0) { + const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp); + const size_t size = utf8codepointsize(lwr_cp); + + if (lwr_cp != cp) { + utf8catcodepoint(p, lwr_cp, size); + } + + p = pn; + pn = utf8codepoint(p, &cp); + } +} + +void utf8upr(void *utf8_restrict str) +{ + void *p, *pn; + utf8_int32_t cp; + + p = (char *)str; + pn = utf8codepoint(p, &cp); + + while (cp != 0) { + const utf8_int32_t lwr_cp = utf8uprcodepoint(cp); + const size_t size = utf8codepointsize(lwr_cp); + + if (lwr_cp != cp) { + utf8catcodepoint(p, lwr_cp, size); + } + + p = pn; + pn = utf8codepoint(p, &cp); + } +} + +utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp) +{ + if (((0x0041 <= cp) && (0x005a >= cp)) || + ((0x00c0 <= cp) && (0x00d6 >= cp)) || + ((0x00d8 <= cp) && (0x00de >= cp)) || + ((0x0391 <= cp) && (0x03a1 >= cp)) || + ((0x03a3 <= cp) && (0x03ab >= cp))) { + cp += 32; + } else if (((0x0100 <= cp) && (0x012f >= cp)) || + ((0x0132 <= cp) && (0x0137 >= cp)) || + ((0x014a <= cp) && (0x0177 >= cp)) || + ((0x0182 <= cp) && (0x0185 >= cp)) || + ((0x01a0 <= cp) && (0x01a5 >= cp)) || + ((0x01de <= cp) && (0x01ef >= cp)) || + ((0x01f8 <= cp) && (0x021f >= cp)) || + ((0x0222 <= cp) && (0x0233 >= cp)) || + ((0x0246 <= cp) && (0x024f >= cp)) || + ((0x03d8 <= cp) && (0x03ef >= cp))) { + cp |= 0x1; + } else if (((0x0139 <= cp) && (0x0148 >= cp)) || + ((0x0179 <= cp) && (0x017e >= cp)) || + ((0x01af <= cp) && (0x01b0 >= cp)) || + ((0x01b3 <= cp) && (0x01b6 >= cp)) || + ((0x01cd <= cp) && (0x01dc >= cp))) { + cp += 1; + cp &= ~0x1; + } else { + switch (cp) { + default: break; + case 0x0178: cp = 0x00ff; break; + case 0x0243: cp = 0x0180; break; + case 0x018e: cp = 0x01dd; break; + case 0x023d: cp = 0x019a; break; + case 0x0220: cp = 0x019e; break; + case 0x01b7: cp = 0x0292; break; + case 0x01c4: cp = 0x01c6; break; + case 0x01c7: cp = 0x01c9; break; + case 0x01ca: cp = 0x01cc; break; + case 0x01f1: cp = 0x01f3; break; + case 0x01f7: cp = 0x01bf; break; + case 0x0187: cp = 0x0188; break; + case 0x018b: cp = 0x018c; break; + case 0x0191: cp = 0x0192; break; + case 0x0198: cp = 0x0199; break; + case 0x01a7: cp = 0x01a8; break; + case 0x01ac: cp = 0x01ad; break; + case 0x01af: cp = 0x01b0; break; + case 0x01b8: cp = 0x01b9; break; + case 0x01bc: cp = 0x01bd; break; + case 0x01f4: cp = 0x01f5; break; + case 0x023b: cp = 0x023c; break; + case 0x0241: cp = 0x0242; break; + case 0x03fd: cp = 0x037b; break; + case 0x03fe: cp = 0x037c; break; + case 0x03ff: cp = 0x037d; break; + case 0x037f: cp = 0x03f3; break; + case 0x0386: cp = 0x03ac; break; + case 0x0388: cp = 0x03ad; break; + case 0x0389: cp = 0x03ae; break; + case 0x038a: cp = 0x03af; break; + case 0x038c: cp = 0x03cc; break; + case 0x038e: cp = 0x03cd; break; + case 0x038f: cp = 0x03ce; break; + case 0x0370: cp = 0x0371; break; + case 0x0372: cp = 0x0373; break; + case 0x0376: cp = 0x0377; break; + case 0x03f4: cp = 0x03d1; break; + case 0x03cf: cp = 0x03d7; break; + case 0x03f9: cp = 0x03f2; break; + case 0x03f7: cp = 0x03f8; break; + case 0x03fa: cp = 0x03fb; break; + }; + } + + return cp; +} + +utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) +{ + if (((0x0061 <= cp) && (0x007a >= cp)) || + ((0x00e0 <= cp) && (0x00f6 >= cp)) || + ((0x00f8 <= cp) && (0x00fe >= cp)) || + ((0x03b1 <= cp) && (0x03c1 >= cp)) || + ((0x03c3 <= cp) && (0x03cb >= cp))) { + cp -= 32; + } else if (((0x0100 <= cp) && (0x012f >= cp)) || + ((0x0132 <= cp) && (0x0137 >= cp)) || + ((0x014a <= cp) && (0x0177 >= cp)) || + ((0x0182 <= cp) && (0x0185 >= cp)) || + ((0x01a0 <= cp) && (0x01a5 >= cp)) || + ((0x01de <= cp) && (0x01ef >= cp)) || + ((0x01f8 <= cp) && (0x021f >= cp)) || + ((0x0222 <= cp) && (0x0233 >= cp)) || + ((0x0246 <= cp) && (0x024f >= cp)) || + ((0x03d8 <= cp) && (0x03ef >= cp))) { + cp &= ~0x1; + } else if (((0x0139 <= cp) && (0x0148 >= cp)) || + ((0x0179 <= cp) && (0x017e >= cp)) || + ((0x01af <= cp) && (0x01b0 >= cp)) || + ((0x01b3 <= cp) && (0x01b6 >= cp)) || + ((0x01cd <= cp) && (0x01dc >= cp))) { + cp -= 1; + cp |= 0x1; + } else { + switch (cp) { + default: break; + case 0x00ff: cp = 0x0178; break; + case 0x0180: cp = 0x0243; break; + case 0x01dd: cp = 0x018e; break; + case 0x019a: cp = 0x023d; break; + case 0x019e: cp = 0x0220; break; + case 0x0292: cp = 0x01b7; break; + case 0x01c6: cp = 0x01c4; break; + case 0x01c9: cp = 0x01c7; break; + case 0x01cc: cp = 0x01ca; break; + case 0x01f3: cp = 0x01f1; break; + case 0x01bf: cp = 0x01f7; break; + case 0x0188: cp = 0x0187; break; + case 0x018c: cp = 0x018b; break; + case 0x0192: cp = 0x0191; break; + case 0x0199: cp = 0x0198; break; + case 0x01a8: cp = 0x01a7; break; + case 0x01ad: cp = 0x01ac; break; + case 0x01b0: cp = 0x01af; break; + case 0x01b9: cp = 0x01b8; break; + case 0x01bd: cp = 0x01bc; break; + case 0x01f5: cp = 0x01f4; break; + case 0x023c: cp = 0x023b; break; + case 0x0242: cp = 0x0241; break; + case 0x037b: cp = 0x03fd; break; + case 0x037c: cp = 0x03fe; break; + case 0x037d: cp = 0x03ff; break; + case 0x03f3: cp = 0x037f; break; + case 0x03ac: cp = 0x0386; break; + case 0x03ad: cp = 0x0388; break; + case 0x03ae: cp = 0x0389; break; + case 0x03af: cp = 0x038a; break; + case 0x03cc: cp = 0x038c; break; + case 0x03cd: cp = 0x038e; break; + case 0x03ce: cp = 0x038f; break; + case 0x0371: cp = 0x0370; break; + case 0x0373: cp = 0x0372; break; + case 0x0377: cp = 0x0376; break; + case 0x03d1: cp = 0x03f4; break; + case 0x03d7: cp = 0x03cf; break; + case 0x03f2: cp = 0x03f9; break; + case 0x03f8: cp = 0x03f7; break; + case 0x03fb: cp = 0x03fa; break; + }; + } + + return cp; +} + +#undef utf8_restrict +#undef utf8_null + +#ifdef __cplusplus +} // extern "C" +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // SHEREDOM_UTF8_H_INCLUDED + + +/******************************************************** + End of file "utf8.h" + ********************************************************/ + + /******************************************************** Begin of file "string_buffer.h" ********************************************************/ @@ -315,17 +1664,16 @@ int mk_wcswidth(const wchar_t *pwcs, size_t n); /***************************************************************************** * STRING BUFFER * ***************************************************************************/ -enum str_buf_type { - CharBuf, -#ifdef FT_HAVE_WCHAR - WCharBuf -#endif /* FT_HAVE_WCHAR */ -}; struct string_buffer { union { char *cstr; +#ifdef FT_HAVE_WCHAR wchar_t *wstr; +#endif +#ifdef FT_HAVE_UTF8 + void *u8str; +#endif void *data; } str; size_t data_sz; @@ -352,27 +1700,32 @@ FT_INTERNAL fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str); #endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 FT_INTERNAL -size_t buffer_text_height(const string_buffer_t *buffer); +fort_status_t fill_buffer_from_u8string(string_buffer_t *buffer, const void *str); +#endif /* FT_HAVE_UTF8 */ FT_INTERNAL -size_t string_buffer_capacity(const string_buffer_t *buffer); +size_t buffer_text_visible_width(const string_buffer_t *buffer); + +FT_INTERNAL +size_t buffer_text_visible_height(const string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_cod_width_capacity(const string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_raw_capacity(const string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_width_capacity(const string_buffer_t *buffer); FT_INTERNAL void *buffer_get_data(string_buffer_t *buffer); FT_INTERNAL -size_t buffer_text_width(const string_buffer_t *buffer); - -FT_INTERNAL -int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag); - -#ifdef FT_HAVE_WCHAR -FT_INTERNAL -int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag); -#endif /* FT_HAVE_WCHAR */ +int buffer_printf(string_buffer_t *buffer, size_t buffer_row, conv_context_t *cntx, size_t cod_width, + const char *content_style_tag, const char *reset_content_style_tag); #endif /* STRING_BUFFER_H */ @@ -631,15 +1984,12 @@ FT_INTERNAL enum CellType get_cell_type(const fort_cell_t *cell); FT_INTERNAL -int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context); +int cell_printf(fort_cell_t *cell, size_t row, conv_context_t *cntx, size_t cod_width); FT_INTERNAL fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str); #ifdef FT_HAVE_WCHAR -FT_INTERNAL -int cell_wprintf(fort_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context); - FT_INTERNAL fort_status_t fill_cell_from_wstring(fort_cell_t *cell, const wchar_t *str); #endif @@ -710,15 +2060,14 @@ FT_INTERNAL fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_span); FT_INTERNAL -int print_row_separator(char *buffer, size_t buffer_sz, +int print_row_separator(conv_context_t *cntx, const size_t *col_width_arr, size_t cols, const fort_row_t *upper_row, const fort_row_t *lower_row, - enum HorSeparatorPos separatorPos, const separator_t *sep, - const context_t *context); + enum HorSeparatorPos separatorPos, const separator_t *sep); FT_INTERNAL -int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context); +int snprintf_row(const fort_row_t *row, conv_context_t *cntx, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height); #ifdef FT_HAVE_WCHAR FT_INTERNAL @@ -726,17 +2075,6 @@ fort_row_t *create_row_from_wstring(const wchar_t *str); FT_INTERNAL fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args); - -FT_INTERNAL -int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, - const size_t *col_width_arr, size_t cols, - const fort_row_t *upper_row, const fort_row_t *lower_row, - enum HorSeparatorPos separatorPos, const separator_t *sep, - const context_t *context); - -FT_INTERNAL -int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context); #endif @@ -799,6 +2137,13 @@ fort_status_t table_rows_and_cols_geometry(const ft_table_t *table, FT_INTERNAL fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width); +/* + * Returns geometry in codepoints(characters) (include codepoints of invisible + * elements: e.g. styles tags). + */ +FT_INTERNAL +fort_status_t table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width); + #endif /* TABLE_H */ /******************************************************** @@ -826,7 +2171,7 @@ fort_cell_t *create_cell(void) fort_cell_t *cell = (fort_cell_t *)F_CALLOC(sizeof(fort_cell_t), 1); if (cell == NULL) return NULL; - cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); + cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CHAR_BUF); if (cell->str_buffer == NULL) { F_FREE(cell); return NULL; @@ -890,7 +2235,7 @@ size_t hint_width_cell(const fort_cell_t *cell, const context_t *context, enum r size_t cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_RIGHT_PADDING); size_t result = cell_padding_left + cell_padding_right; if (cell->str_buffer && cell->str_buffer->str.data) { - result += buffer_text_width(cell->str_buffer); + result += buffer_text_visible_width(cell->str_buffer); } result = MAX(result, (size_t)get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_MIN_WIDTH)); @@ -925,7 +2270,7 @@ size_t hint_height_cell(const fort_cell_t *cell, const context_t *context) size_t cell_empty_string_height = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_EMPTY_STR_HEIGHT); size_t result = cell_padding_top + cell_padding_bottom; if (cell->str_buffer && cell->str_buffer->str.data) { - size_t text_height = buffer_text_height(cell->str_buffer); + size_t text_height = buffer_text_visible_height(cell->str_buffer); result += text_height == 0 ? cell_empty_string_height : text_height; } return result; @@ -933,14 +2278,12 @@ size_t hint_height_cell(const fort_cell_t *cell, const context_t *context) FT_INTERNAL -int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context) +int cell_printf(fort_cell_t *cell, size_t row, conv_context_t *cntx, size_t vis_width) { - const char *space_char = " "; - int (*buffer_printf_)(string_buffer_t *, size_t, char *, size_t, const context_t *, const char *, const char *) = buffer_printf; - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; + const context_t *context = cntx->cntx; + size_t buf_len = vis_width; - if (cell == NULL || buf_len == 0 - || (buf_len <= hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { + if (cell == NULL || (vis_width < hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { return -1; } @@ -985,32 +2328,35 @@ int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const #define TOTAL_WRITTEN (written + invisible_written) #define RIGHT (cell_padding_right + extra_right) -#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, cell_style_tag)) -#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_cell_style_tag)) -#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, content_style_tag)) -#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_content_style_tag)) +#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, cell_style_tag)) +#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_cell_style_tag)) +#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, content_style_tag)) +#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag)) if (row >= hint_height_cell(cell, context) || row < cell_padding_top - || row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { + || row >= (cell_padding_top + buffer_text_visible_height(cell->str_buffer))) { WRITE_CELL_STYLE_TAG; WRITE_CONTENT_STYLE_TAG; WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len, buf_len - 1 - TOTAL_WRITTEN - R3, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, buf_len - TOTAL_WRITTEN - R3, FT_SPACE)); WRITE_RESET_CELL_STYLE_TAG; + return (int)TOTAL_WRITTEN; } WRITE_CELL_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, L2, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, L2, FT_SPACE)); if (cell->str_buffer) { - CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, context, content_style_tag, reset_content_style_tag)); +// CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf2_(cell->str_buffer, row - cell_padding_top, cntx, buf_len - TOTAL_WRITTEN - R2 - R3, content_style_tag, reset_content_style_tag)); + CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf(cell->str_buffer, row - cell_padding_top, cntx, vis_width - L2 - R2 , content_style_tag, reset_content_style_tag)); } else { WRITE_CONTENT_STYLE_TAG; WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, space_char)); +// CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings2_(cntx, buf_len - TOTAL_WRITTEN - R2 - R3, FT_SPACE)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, vis_width - L2 - R2, FT_SPACE)); } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, R2, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, R2, FT_SPACE)); WRITE_RESET_CELL_STYLE_TAG; return (int)TOTAL_WRITTEN; @@ -1025,101 +2371,6 @@ clear: #undef RIGHT } -#ifdef FT_HAVE_WCHAR -FT_INTERNAL -int cell_wprintf(fort_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context) -{ - const char *space_char = " "; - int (*buffer_printf_)(string_buffer_t *, size_t, wchar_t *, size_t, const context_t *, const char *, const char *) = buffer_wprintf; - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - - if (cell == NULL || buf_len == 0 - || (buf_len <= hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { - return -1; - } - - unsigned int cell_padding_top = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_TOP_PADDING); - unsigned int cell_padding_left = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_LEFT_PADDING); - unsigned int cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_RIGHT_PADDING); - - size_t written = 0; - size_t invisible_written = 0; - int tmp = 0; - - /* todo: Dirty hack with changing buf_len! need refactoring. */ - /* Also maybe it is better to move all struff with colors to buffers? */ - char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_style_tag_for_cell(context->table_properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(cell_style_tag); - - char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_reset_style_tag_for_cell(context->table_properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(reset_cell_style_tag); - - char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_style_tag_for_content(context->table_properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(content_style_tag); - - char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_reset_style_tag_for_content(context->table_properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(reset_content_style_tag); - - /* CELL_STYLE_T LEFT_PADDING CONTENT_STYLE_T CONTENT RESET_CONTENT_STYLE_T RIGHT_PADDING RESET_CELL_STYLE_T - * | | | | | | | | - * L1 R1 - * L2 R2 - * L3 R3 - */ - - size_t L2 = cell_padding_left; - - size_t R2 = cell_padding_right; - size_t R3 = strlen(reset_cell_style_tag); - -#define TOTAL_WRITTEN (written + invisible_written) -#define RIGHT (right + extra_right) - -#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, cell_style_tag)) -#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_cell_style_tag)) -#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, content_style_tag)) -#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_content_style_tag)) - - if (row >= hint_height_cell(cell, context) - || row < cell_padding_top - || row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { - WRITE_CELL_STYLE_TAG; - WRITE_CONTENT_STYLE_TAG; - WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len, buf_len - 1 - TOTAL_WRITTEN - R3, space_char)); - WRITE_RESET_CELL_STYLE_TAG; - return (int)TOTAL_WRITTEN; - } - - WRITE_CELL_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, L2, space_char)); - if (cell->str_buffer) { - CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, context, content_style_tag, reset_content_style_tag)); - } else { - WRITE_CONTENT_STYLE_TAG; - WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, space_char)); - } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, R2, space_char)); - WRITE_RESET_CELL_STYLE_TAG; - - return (int)TOTAL_WRITTEN; - -clear: - return -1; -#undef WRITE_CELL_STYLE_TAG -#undef WRITE_RESET_CELL_STYLE_TAG -#undef WRITE_CONTENT_STYLE_TAG -#undef WRITE_RESET_CONTENT_STYLE_TAG -#undef TOTAL_WRITTEN -#undef RIGHT -} -#endif - FT_INTERNAL fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str) { @@ -1505,6 +2756,23 @@ static int ft_write_impl(ft_table_t *table, const char *cell_content) return status; } +#ifdef FT_HAVE_UTF8 +static int ft_u8write_impl(ft_table_t *table, const void *cell_content) +{ + assert(table); + string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table); + if (str_buffer == NULL) + return FT_ERROR; + + int status = fill_buffer_from_u8string(str_buffer, cell_content); + if (FT_IS_SUCCESS(status)) { + table->cur_col++; + } + return status; +} +#endif /* FT_HAVE_UTF8 */ + + #ifdef FT_HAVE_WCHAR @@ -1573,6 +2841,9 @@ int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...) return status; } + + + #ifdef FT_HAVE_WCHAR int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...) @@ -1738,209 +3009,111 @@ int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_ } #endif +static +const char * empty_str_arr[] = {"", (const char *)L""}; +static +const char *ft_to_string_impl(const ft_table_t *table, enum str_buf_type b_type) +{ + assert(table); + + const char *result = NULL; + + /* Determing size of table string representation */ + size_t cod_height = 0; + size_t cod_width = 0; + int status = table_internal_codepoints_geometry(table, &cod_height, &cod_width); + if (FT_IS_ERROR(status)) { + return NULL; + } + size_t n_codepoints = cod_height * cod_width + 1; + + /* Allocate string buffer for string representation */ + if (table->conv_buffer == NULL) { + ((ft_table_t *)table)->conv_buffer = create_string_buffer(n_codepoints, b_type); + if (table->conv_buffer == NULL) + return NULL; + } + while (string_buffer_cod_width_capacity(table->conv_buffer) < n_codepoints) { + if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { + return NULL; + } + } + char *buffer = (char *)buffer_get_data(table->conv_buffer); + + size_t cols = 0; + size_t rows = 0; + size_t *col_vis_width_arr = NULL; + size_t *row_vis_height_arr = NULL; + status = table_rows_and_cols_geometry(table, &col_vis_width_arr, &cols, &row_vis_height_arr, &rows, VISIBLE_GEOMETRY); + if (FT_IS_ERROR(status)) + return NULL; + + if (rows == 0) { + result = empty_str_arr[b_type]; + goto clear; + } + + int tmp = 0; + size_t i = 0; + context_t context; + context.table_properties = (table->properties ? table->properties : &g_table_properties); + fort_row_t *prev_row = NULL; + fort_row_t *cur_row = NULL; + separator_t *cur_sep = NULL; + size_t sep_size = vector_size(table->separators); + + conv_context_t cntx; + cntx.buf_origin = buffer; + cntx.buf = buffer; + cntx.raw_avail = string_buffer_raw_capacity(table->conv_buffer); + cntx.cntx = &context; + cntx.b_type = b_type; + + /* Print top margin */ + for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { + FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE)); + FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE)); + } + + for (i = 0; i < rows; ++i) { + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + cur_row = *(fort_row_t **)vector_at(table->rows, i); + enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; + context.row = i; + FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep)); + FT_CHECK(snprintf_row(cur_row, &cntx, col_vis_width_arr, cols, row_vis_height_arr[i])); + prev_row = cur_row; + } + cur_row = NULL; + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + context.row = i; + FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep)); + + /* Print bottom margin */ + for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { + FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE)); + FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE)); + } + + result = buffer; + +clear: + F_FREE(col_vis_width_arr); + F_FREE(row_vis_height_arr); + return result; +} const char *ft_to_string(const ft_table_t *table) { - typedef char char_type; - const enum str_buf_type buf_type = CharBuf; - const char *space_char = " "; - const char *new_line_char = "\n"; -#define EMPTY_STRING "" - int (*snprintf_row_)(const fort_row_t *, char *, size_t, size_t *, size_t, size_t, const context_t *) = snprintf_row; - int (*print_row_separator_)(char *, size_t, - const size_t *, size_t, - const fort_row_t *, const fort_row_t *, - enum HorSeparatorPos, const separator_t *, - const context_t *) = print_row_separator; - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; - assert(table); - - /* Determing size of table string representation */ - size_t height = 0; - size_t width = 0; - int status = table_geometry(table, &height, &width); - if (FT_IS_ERROR(status)) { - return NULL; - } - size_t sz = height * width + 1; - - /* Allocate string buffer for string representation */ - if (table->conv_buffer == NULL) { - ((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); - if (table->conv_buffer == NULL) - return NULL; - } - while (string_buffer_capacity(table->conv_buffer) < sz) { - if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { - return NULL; - } - } - char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); - - - size_t cols = 0; - size_t rows = 0; - size_t *col_width_arr = NULL; - size_t *row_height_arr = NULL; - status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, VISIBLE_GEOMETRY); - if (FT_IS_ERROR(status)) - return NULL; - - if (rows == 0) - return EMPTY_STRING; - - size_t written = 0; - int tmp = 0; - size_t i = 0; - context_t context; - context.table_properties = (table->properties ? table->properties : &g_table_properties); - fort_row_t *prev_row = NULL; - fort_row_t *cur_row = NULL; - separator_t *cur_sep = NULL; - size_t sep_size = vector_size(table->separators); - - /* Print top margin */ - for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - for (i = 0; i < rows; ++i) { - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - cur_row = *(fort_row_t **)vector_at(table->rows, i); - enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); - CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); - prev_row = cur_row; - } - cur_row = NULL; - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); - - /* Print bottom margin */ - for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return buffer; - -clear: - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return NULL; -#undef EMPTY_STRING + return ft_to_string_impl(table, CHAR_BUF); } - #ifdef FT_HAVE_WCHAR - const wchar_t *ft_to_wstring(const ft_table_t *table) { - typedef wchar_t char_type; - const enum str_buf_type buf_type = WCharBuf; - const char *space_char = " "; - const char *new_line_char = "\n"; -#define EMPTY_STRING L"" - int (*snprintf_row_)(const fort_row_t *, wchar_t *, size_t, size_t *, size_t, size_t, const context_t *) = wsnprintf_row; - int (*print_row_separator_)(wchar_t *, size_t, - const size_t *, size_t, - const fort_row_t *, const fort_row_t *, - enum HorSeparatorPos, const separator_t *, - const context_t *) = wprint_row_separator; - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - - - assert(table); - - /* Determing size of table string representation */ - size_t height = 0; - size_t width = 0; - int status = table_geometry(table, &height, &width); - if (FT_IS_ERROR(status)) { - return NULL; - } - size_t sz = height * width + 1; - - /* Allocate string buffer for string representation */ - if (table->conv_buffer == NULL) { - ((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); - if (table->conv_buffer == NULL) - return NULL; - } - while (string_buffer_capacity(table->conv_buffer) < sz) { - if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { - return NULL; - } - } - char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); - - - size_t cols = 0; - size_t rows = 0; - size_t *col_width_arr = NULL; - size_t *row_height_arr = NULL; - status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, VISIBLE_GEOMETRY); - - if (rows == 0) - return EMPTY_STRING; - - if (FT_IS_ERROR(status)) - return NULL; - - size_t written = 0; - int tmp = 0; - size_t i = 0; - context_t context; - context.table_properties = (table->properties ? table->properties : &g_table_properties); - fort_row_t *prev_row = NULL; - fort_row_t *cur_row = NULL; - separator_t *cur_sep = NULL; - size_t sep_size = vector_size(table->separators); - - /* Print top margin */ - for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - for (i = 0; i < rows; ++i) { - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - cur_row = *(fort_row_t **)vector_at(table->rows, i); - enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); - CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); - prev_row = cur_row; - } - cur_row = NULL; - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); - - /* Print bottom margin */ - for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return buffer; - -clear: - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return NULL; -#undef EMPTY_STRING + return (const wchar_t *)ft_to_string_impl(table, W_CHAR_BUF); } - #endif @@ -2161,6 +3334,64 @@ int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span) return row_set_cell_span(row_p, col, hor_span); } +#ifdef FT_HAVE_UTF8 + +int ft_nu8write(ft_table_t *table, size_t n, const void *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_u8write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const void *cell = va_arg(va, const void *); + status = ft_u8write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} + +int ft_nu8write_ln(ft_table_t *table, size_t n, const void *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_u8write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const void *cell = va_arg(va, const void *); + status = ft_u8write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} + +const void *ft_to_u8string(const ft_table_t *table) +{ + return (const void *)ft_to_string_impl(table, UTF8_BUF); +} +#endif /* FT_HAVE_UTF8 */ + /******************************************************** End of file "fort_impl.c" ********************************************************/ @@ -2174,6 +3405,9 @@ int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span) #ifdef FT_HAVE_WCHAR #include #endif +#if defined(FT_HAVE_UTF8) +/* #include "utf8.h" */ /* Commented by amalgamation script */ +#endif char g_col_separator = FORT_DEFAULT_COL_SEPARATOR; @@ -2341,9 +3575,26 @@ size_t number_of_columns_in_format_wstring(const wchar_t *fmt) } #endif - +#if defined(FT_HAVE_UTF8) FT_INTERNAL -int snprint_n_strings(char *buf, size_t length, size_t n, const char *str) +size_t number_of_columns_in_format_u8string(const void *fmt) +{ + size_t separator_counter = 0; + const char *pos = fmt; + while (1) { + pos = utf8chr(pos, g_col_separator); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} +#endif + +static +int snprint_n_strings_impl(char *buf, size_t length, size_t n, const char *str) { size_t str_len = strlen(str); if (length <= n * str_len) @@ -2372,17 +3623,123 @@ int snprint_n_strings(char *buf, size_t length, size_t n, const char *str) return (int)(n * str_len); } +static +int snprint_n_strings(conv_context_t *cntx, size_t n, const char *str) +{ + int w = snprint_n_strings_impl(cntx->buf, cntx->raw_avail, n, str); + if (w >= 0) { + cntx->buf += w; + cntx->raw_avail -= w; + } + return w; +} +#if defined(FT_HAVE_WCHAR) +static +int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); +#endif + +#if defined(FT_HAVE_UTF8) +static +int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str); +#endif + + +FT_INTERNAL +int print_n_strings(conv_context_t *cntx, size_t n, const char *str) +{ + int cod_w; + int raw_written; + + switch (cntx->b_type) { + case CHAR_BUF: + return snprint_n_strings(cntx, n, str); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + cod_w = wsnprint_n_string((wchar_t *)cntx->buf, cntx->raw_avail, n, str); + if (cod_w < 0) + return cod_w; + raw_written = sizeof(wchar_t) * cod_w; + + cntx->buf += raw_written; + cntx->raw_avail -= raw_written; + return cod_w; +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + /* Everying is very strange and differs with W_CHAR_BUF */ + raw_written = u8nprint_n_strings(cntx->buf, cntx->raw_avail, n, str); + if (raw_written < 0) { + fprintf(stderr, " raw_written = %d\n", raw_written); + return raw_written; + } + + cntx->buf += raw_written; + cntx->raw_avail -= raw_written; + return utf8len(str) * n; +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + return -1; + } +} + +FT_INTERNAL +int ft_nprint(conv_context_t *cntx, const char *str, size_t strlen) +{ + if (cntx->raw_avail + 1/* for 0 */ < strlen) + return -1; + + memcpy(cntx->buf, str, strlen); + cntx->buf += strlen; + cntx->raw_avail -= strlen; + *cntx->buf = '\0'; /* Do we need this ? */ + return strlen; +} + +#ifdef FT_HAVE_WCHAR +int ft_nwprint(conv_context_t *cntx, const wchar_t *str, size_t strlen) +{ + if (cntx->raw_avail + 1/* for 0 */ < strlen) + return -1; + + size_t raw_len = strlen * sizeof(wchar_t); + + memcpy(cntx->buf, str, raw_len); + cntx->buf += raw_len; + cntx->raw_avail -= raw_len; + *(wchar_t *)cntx->buf = L'\0'; /* Do we need this ? */ + return strlen; +} +#endif /* FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +int ft_nu8print(conv_context_t *cntx, const void *beg, const void *end) +{ + const char *bc = beg; + const char *ec = end; + size_t raw_len = ec - bc; + if (cntx->raw_avail + 1 < raw_len) + return -1; + + memcpy(cntx->buf, beg, raw_len); + cntx->buf += raw_len; + cntx->raw_avail -= raw_len; + *(char *)cntx->buf = '\0'; /* Do we need this ? */ + return raw_len; /* what return here ? */ +} +#endif /* FT_HAVE_UTF8 */ #if defined(FT_HAVE_WCHAR) #define WCS_SIZE 64 -FT_INTERNAL +static int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) { size_t str_len = strlen(str); - /* note: baybe it's, better to return -1 in case of multibyte character + /* note: maybe it's, better to return -1 in case of multibyte character * strings (not sure this case is done correctly). */ if (str_len > 1) { @@ -2442,6 +3799,36 @@ int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) } #endif + +#if defined(FT_HAVE_UTF8) +static +int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str) +{ + size_t str_size = utf8size(str) - 1; /* str_size - raw size in bytes, excluding \0 */ + if (length <= n * str_size) + return -1; + + if (n == 0) + return 0; + + /* To ensure valid return value it is safely not print such big strings */ + if (n * str_size > INT_MAX) + return -1; + + if (str_size == 0) + return 0; + + size_t i = n; + while (i) { + memcpy(buf, str, str_size); + buf = (char *)buf + str_size; + --i; + } + *(char *)buf = '\0'; + return (int)(n * str_size); +} +#endif + /******************************************************** End of file "fort_utils.c" ********************************************************/ @@ -3706,22 +5093,20 @@ fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_ return FT_SUCCESS; } - FT_INTERNAL -int print_row_separator(char *buffer, size_t buffer_sz, +int print_row_separator_impl(conv_context_t *cntx, const size_t *col_width_arr, size_t cols, const fort_row_t *upper_row, const fort_row_t *lower_row, enum HorSeparatorPos separatorPos, - const separator_t *sep, const context_t *context) + const separator_t *sep) { - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; - - assert(buffer); - assert(context); + assert(cntx); const char *space_char = " "; int status = -1; + const context_t *context = cntx->cntx; + /* Get cell types * * Regions above top row and below bottom row areconsidered full of virtual @@ -3859,31 +5244,31 @@ int print_row_separator(char *buffer, size_t buffer_sz, } /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.left_margin, space_char)); for (i = 0; i < cols; ++i) { if (i == 0) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L)); } else { if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) && (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV)); } else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *II)); } else if (top_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IT)); } else { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IB)); } } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[i], *I)); } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R)); /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.right_margin, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, "\n")); status = (int)written; @@ -3892,195 +5277,16 @@ clear: return status; } - -#ifdef FT_HAVE_WCHAR FT_INTERNAL -int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, - const size_t *col_width_arr, size_t cols, - const fort_row_t *upper_row, const fort_row_t *lower_row, - enum HorSeparatorPos separatorPos, const separator_t *sep, - const context_t *context) +int print_row_separator(conv_context_t *cntx, + const size_t *col_width_arr, size_t cols, + const fort_row_t *upper_row, const fort_row_t *lower_row, + enum HorSeparatorPos separatorPos, const separator_t *sep) { - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - - assert(buffer); - assert(context); - - const char *space_char = " "; - int status = -1; - - /* Get cell types - * - * Regions above top row and below bottom row areconsidered full of virtual - * GroupSlaveCell cells - */ - enum CellType *top_row_types = (enum CellType *)F_MALLOC(sizeof(enum CellType) * cols * 2); - if (top_row_types == NULL) { - return FT_MEMORY_ERROR; - } - enum CellType *bottom_row_types = top_row_types + cols; - if (upper_row) { - get_row_cell_types(upper_row, top_row_types, cols); - } else { - size_t i = 0; - for (i = 0; i < cols; ++i) - top_row_types[i] = GroupSlaveCell; - } - if (lower_row) { - get_row_cell_types(lower_row, bottom_row_types, cols); - } else { - size_t i = 0; - for (i = 0; i < cols; ++i) - bottom_row_types[i] = GroupSlaveCell; - } - - - size_t written = 0; - int tmp = 0; - - enum ft_row_type lower_row_type = FT_ROW_COMMON; - if (lower_row != NULL) { - lower_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); - } - enum ft_row_type upper_row_type = FT_ROW_COMMON; - if (upper_row != NULL) { - upper_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row - 1, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); - } - - /* Row separator anatomy - * - * | C11 | C12 C13 | C14 C15 | - * L I I I IV I I IT I I I IB I I II I I R - * | C21 | C22 | C23 C24 C25 | - */ - const char **L = NULL; - const char **I = NULL; - const char **IV = NULL; - const char **R = NULL; - const char **IT = NULL; - const char **IB = NULL; - const char **II = NULL; - - - typedef const char *(*border_chars_point_t)[BorderItemPosSize]; - const char *(*border_chars)[BorderItemPosSize] = NULL; - border_chars = (border_chars_point_t)&context->table_properties->border_style.border_chars; - if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) { - border_chars = (border_chars_point_t)&context->table_properties->border_style.header_border_chars; - } - - if (sep && sep->enabled) { - L = &(context->table_properties->border_style.separator_chars[LH_sip]); - I = &(context->table_properties->border_style.separator_chars[IH_sip]); - IV = &(context->table_properties->border_style.separator_chars[II_sip]); - R = &(context->table_properties->border_style.separator_chars[RH_sip]); - - IT = &(context->table_properties->border_style.separator_chars[TI_sip]); - IB = &(context->table_properties->border_style.separator_chars[BI_sip]); - II = &(context->table_properties->border_style.separator_chars[IH_sip]); - - if (lower_row == NULL) { - L = &(*border_chars)[BL_bip]; - R = &(*border_chars)[BR_bip]; - } else if (upper_row == NULL) { - L = &(*border_chars)[TL_bip]; - R = &(*border_chars)[TR_bip]; - } - } else { - switch (separatorPos) { - case TopSeparator: - L = &(*border_chars)[TL_bip]; - I = &(*border_chars)[TT_bip]; - IV = &(*border_chars)[TV_bip]; - R = &(*border_chars)[TR_bip]; - - IT = &(*border_chars)[TV_bip]; - IB = &(*border_chars)[TV_bip]; - II = &(*border_chars)[TT_bip]; - break; - case InsideSeparator: - L = &(*border_chars)[LH_bip]; - I = &(*border_chars)[IH_bip]; - IV = &(*border_chars)[II_bip]; - R = &(*border_chars)[RH_bip]; - - /* - IT = &(*border_chars)[TV_bip]; - IB = &(*border_chars)[BV_bip]; - */ - IT = &(*border_chars)[TI_bip]; - IB = &(*border_chars)[BI_bip]; - II = &(*border_chars)[IH_bip]; - break; - case BottomSeparator: - L = &(*border_chars)[BL_bip]; - I = &(*border_chars)[BB_bip]; - IV = &(*border_chars)[BV_bip]; - R = &(*border_chars)[BR_bip]; - - IT = &(*border_chars)[BV_bip]; - IB = &(*border_chars)[BV_bip]; - II = &(*border_chars)[BB_bip]; - break; - default: - break; - } - } - - size_t i = 0; - - /* If all chars are not printable, skip line separator */ - /* todo: add processing for wchar_t */ - /* - if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) { - status = 0; - goto clear; - } - */ - if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L))) - && (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I))) - && (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV))) - && (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) { - status = 0; - goto clear; - } - - /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); - - for (i = 0; i < cols; ++i) { - if (i == 0) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); - } else { - if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) - && (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); - } else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); - } else if (top_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); - } else { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); - } - } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); - } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); - - /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); - - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); - - status = (int)written; - -clear: - F_FREE(top_row_types); - return status; + return print_row_separator_impl(cntx, col_width_arr, cols, upper_row, lower_row, + separatorPos, sep); } -#endif - FT_INTERNAL fort_row_t *create_row_from_string(const char *str) { @@ -4265,7 +5471,7 @@ fort_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args) #define CREATE_ROW_FROM_STRING create_row_from_string #define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_string #define FILL_CELL_FROM_STRING fill_cell_from_string -#define STR_BUF_TYPE CharBuf +#define STR_BUF_TYPE CHAR_BUF string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, STR_BUF_TYPE); if (buffer == NULL) @@ -4277,14 +5483,14 @@ fort_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args) while (1) { va_list va; va_copy(va, *va_args); - int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); + int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_width_capacity(buffer), fmt, va); va_end(va); /* If error encountered */ if (virtual_sz < 0) goto clear; /* Successful write */ - if ((size_t)virtual_sz < string_buffer_capacity(buffer)) + if ((size_t)virtual_sz < string_buffer_width_capacity(buffer)) break; /* Otherwise buffer was too small, so incr. buffer size ant try again. */ @@ -4351,7 +5557,7 @@ fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args) #define CREATE_ROW_FROM_STRING create_row_from_wstring #define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_wstring #define FILL_CELL_FROM_STRING fill_cell_from_wstring -#define STR_BUF_TYPE WCharBuf +#define STR_BUF_TYPE W_CHAR_BUF string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, STR_BUF_TYPE); if (buffer == NULL) @@ -4363,14 +5569,14 @@ fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args) while (1) { va_list va; va_copy(va, *va_args); - int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); + int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_width_capacity(buffer), fmt, va); va_end(va); /* If error encountered */ if (virtual_sz < 0) goto clear; /* Successful write */ - if ((size_t)virtual_sz < string_buffer_capacity(buffer)) + if ((size_t)virtual_sz < string_buffer_width_capacity(buffer)) break; /* Otherwise buffer was too small, so incr. buffer size ant try again. */ @@ -4431,12 +5637,10 @@ clear: FT_INTERNAL -int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context) +int snprintf_row(const fort_row_t *row, conv_context_t *cntx, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height) { - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; - int (*cell_printf_)(fort_cell_t *, size_t, char *, size_t, const context_t *) = cell_printf; - + const context_t *context = cntx->cntx; assert(context); const char *space_char = " "; const char *new_line_char = "\n"; @@ -4468,47 +5672,47 @@ int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col size_t i = 0; for (i = 0; i < row_height; ++i) { /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.left_margin, space_char)); /* Print left table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L)); size_t j = 0; while (j < col_width_arr_sz) { if (j < cols_in_row) { ((context_t *)context)->column = j; fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j); - size_t cell_width = 0; + size_t cell_vis_width = 0; size_t group_slave_sz = group_cell_number(row, j); - cell_width = col_width_arr[j]; + cell_vis_width = col_width_arr[j]; size_t slave_j = 0; size_t master_j = j; for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { - cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; + cell_vis_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; ++j; } - CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); + CHCK_RSLT_ADD_TO_WRITTEN(cell_printf(cell, i, cntx, cell_vis_width)); } else { /* Print empty cell */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[j], space_char)); } /* Print boundary between cells */ if (j < col_width_arr_sz - 1) - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV)); ++j; } /* Print right table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R)); /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.right_margin, space_char)); /* Print new line character */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, new_line_char)); } return (int)written; @@ -4516,98 +5720,6 @@ clear: return -1; } - -#ifdef FT_HAVE_WCHAR -FT_INTERNAL -int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context) -{ - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - int (*cell_printf_)(fort_cell_t *, size_t, wchar_t *, size_t, const context_t *) = cell_wprintf; - - assert(context); - const char *space_char = " "; - const char *new_line_char = "\n"; - - if (row == NULL) - return -1; - - size_t cols_in_row = columns_in_row(row); - if (cols_in_row > col_width_arr_sz) - return -1; - - /* Row separator anatomy - * - * L data IV data IV data R - */ - - typedef const char *(*border_chars_point_t)[BorderItemPosSize]; - enum ft_row_type row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); - const char *(*bord_chars)[BorderItemPosSize] = (row_type == FT_ROW_HEADER) - ? (border_chars_point_t)(&context->table_properties->border_style.header_border_chars) - : (border_chars_point_t)(&context->table_properties->border_style.border_chars); - const char **L = &(*bord_chars)[LL_bip]; - const char **IV = &(*bord_chars)[IV_bip]; - const char **R = &(*bord_chars)[RR_bip]; - - - size_t written = 0; - int tmp = 0; - size_t i = 0; - for (i = 0; i < row_height; ++i) { - /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); - - /* Print left table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); - size_t j = 0; - while (j < col_width_arr_sz) { - if (j < cols_in_row) { - ((context_t *)context)->column = j; - fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j); - size_t cell_width = 0; - - size_t group_slave_sz = group_cell_number(row, j); - cell_width = col_width_arr[j]; - size_t slave_j = 0; - size_t master_j = j; - for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { - cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; - ++j; - } - - CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); - } else { - /* Print empty cell */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); - } - - /* Print boundary between cells */ - if (j < col_width_arr_sz - 1) - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); - - ++j; - } - - /* Print right table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); - - /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); - - /* Print new line character */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); - } - return (int)written; - -clear: - return -1; -} - -#endif - - - /******************************************************** End of file "row.c" ********************************************************/ @@ -4622,8 +5734,12 @@ clear: /* #include "wcwidth.h" */ /* Commented by amalgamation script */ #include #include +#ifdef FT_HAVE_WCHAR #include - +#endif +#if defined(FT_HAVE_UTF8) +/* #include "utf8.h" */ /* Commented by amalgamation script */ +#endif static ptrdiff_t str_iter_width(const char *beg, const char *end) { @@ -4640,15 +5756,34 @@ static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end) } #endif /* FT_HAVE_WCHAR */ +//#ifdef FT_HAVE_UTF8 +//static ptrdiff_t u8_iter_width(const void *beg, const void *end) +//{ +// assert(end >= beg); +// return ut8_width(beg, end); +//} +//#endif /* FT_HAVE_UTF8 */ + static size_t buf_str_len(const string_buffer_t *buf) { assert(buf); - if (buf->type == CharBuf) { - return strlen(buf->str.cstr); - } else { - return wcslen(buf->str.wstr); + + 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 } + + assert(0); + return 0; } @@ -4668,7 +5803,7 @@ size_t strchr_count(const char *str, char ch) return count; } - +#ifdef FT_HAVE_WCHAR FT_INTERNAL size_t wstrchr_count(const wchar_t *str, wchar_t ch) { @@ -4684,6 +5819,34 @@ size_t wstrchr_count(const wchar_t *str, wchar_t ch) } return count; } +#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 */ FT_INTERNAL @@ -4731,6 +5894,29 @@ const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, si } #endif /* FT_HAVE_WCHAR */ +#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 + FT_INTERNAL void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end) @@ -4775,11 +5961,34 @@ void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const } #endif /* FT_HAVE_WCHAR */ +#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) +{ + const char *beg = utf8_n_substring_beg(str, ch_separator, n); + if (beg == NULL) { + *begin = NULL; + *end = NULL; + return; + } + + const char *en = utf8chr(beg, ch_separator); + if (en == NULL) { + en = (const char *)str + strlen(str); + } + + *begin = beg; + *end = en; + return; +} +#endif /* FT_HAVE_UTF8 */ + + FT_INTERNAL string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type type) { - size_t sz = (number_of_chars) * (type == CharBuf ? sizeof(char) : sizeof(wchar_t)); + size_t sz = (number_of_chars) * (type == CHAR_BUF ? sizeof(char) : sizeof(wchar_t)); string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t)); if (result == NULL) return NULL; @@ -4791,10 +6000,10 @@ string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type result->data_sz = sz; result->type = type; - if (sz && type == CharBuf) { + if (sz && type == CHAR_BUF) { result->str.cstr[0] = '\0'; #ifdef FT_HAVE_WCHAR - } else if (sz && type == WCharBuf) { + } else if (sz && type == W_CHAR_BUF) { result->str.wstr[0] = L'\0'; #endif /* FT_HAVE_WCHAR */ } @@ -4821,14 +6030,14 @@ string_buffer_t *copy_string_buffer(const string_buffer_t *buffer) if (result == NULL) return NULL; switch (buffer->type) { - case CharBuf: + case CHAR_BUF: if (FT_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) { destroy_string_buffer(result); return NULL; } break; #ifdef FT_HAVE_WCHAR - case WCharBuf: + case W_CHAR_BUF: if (FT_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) { destroy_string_buffer(result); return NULL; @@ -4869,7 +6078,7 @@ fort_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str) F_FREE(buffer->str.data); buffer->str.cstr = copy; - buffer->type = CharBuf; + buffer->type = CHAR_BUF; return FT_SUCCESS; } @@ -4888,31 +6097,86 @@ fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *s F_FREE(buffer->str.data); buffer->str.wstr = copy; - buffer->type = WCharBuf; + buffer->type = W_CHAR_BUF; return FT_SUCCESS; } #endif /* FT_HAVE_WCHAR */ +#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 */ FT_INTERNAL -size_t buffer_text_height(const string_buffer_t *buffer) +size_t buffer_text_visible_height(const string_buffer_t *buffer) { if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) { return 0; } - if (buffer->type == CharBuf) + if (buffer->type == CHAR_BUF) return 1 + strchr_count(buffer->str.cstr, '\n'); - else +#ifdef FT_HAVE_WCHAR + else if (buffer->type == W_CHAR_BUF) return 1 + wstrchr_count(buffer->str.wstr, L'\n'); +#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; } +FT_INTERNAL +size_t string_buffer_cod_width_capacity(const string_buffer_t *buffer) +{ + return string_buffer_width_capacity(buffer); +} FT_INTERNAL -size_t buffer_text_width(const string_buffer_t *buffer) +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); + char *tmp = F_MALLOC(sizeof(char) * (sz + 1)); + // @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 */ + +FT_INTERNAL +size_t buffer_text_visible_width(const string_buffer_t *buffer) { size_t max_length = 0; - if (buffer->type == CharBuf) { + if (buffer->type == CHAR_BUF) { size_t n = 0; while (1) { const char *beg = NULL; @@ -4925,7 +6189,7 @@ size_t buffer_text_width(const string_buffer_t *buffer) ++n; } #ifdef FT_HAVE_WCHAR - } else { + } else if (buffer->type == W_CHAR_BUF) { size_t n = 0; while (1) { const wchar_t *beg = NULL; @@ -4942,149 +6206,110 @@ size_t buffer_text_width(const string_buffer_t *buffer) ++n; } #endif /* FT_HAVE_WCHAR */ +#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 */ } return max_length; /* shouldn't be here */ } -FT_INTERNAL -int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag) +static void +buffer_substring(const string_buffer_t *buffer, size_t buffer_row, void **begin, void **end, ptrdiff_t *str_it_width) { -#define CHAR_TYPE char -#define NULL_CHAR '\0' -#define NEWLINE_CHAR '\n' -#define SPACE_CHAR " " -#define SNPRINTF_FMT_STR "%*s" -#define SNPRINTF snprintf -#define BUFFER_STR str.cstr -#define SNPRINT_N_STRINGS snprint_n_strings -#define STR_N_SUBSTRING str_n_substring -#define STR_ITER_WIDTH str_iter_width - - size_t buf_len = total_buf_len - strlen(content_style_tag) - strlen(reset_content_style_tag); - - if (buffer == NULL || buffer->str.data == NULL - || buffer_row >= buffer_text_height(buffer) || buf_len == 0) { - return -1; - } - - size_t content_width = buffer_text_width(buffer); - if ((buf_len - 1) < content_width) - return -1; - - size_t left = 0; - size_t right = 0; - - switch (get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_TEXT_ALIGN)) { - case FT_ALIGNED_LEFT: - left = 0; - right = (buf_len - 1) - content_width; + 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); break; - case FT_ALIGNED_CENTER: - left = ((buf_len - 1) - content_width) / 2; - right = ((buf_len - 1) - content_width) - left; - break; - case FT_ALIGNED_RIGHT: - left = (buf_len - 1) - content_width; - right = 0; - break; - default: - assert(0); - break; - } - - int set_old_value = 0; - size_t written = 0; - int tmp = 0; - ptrdiff_t str_it_width = 0; - const CHAR_TYPE *beg = NULL; - const CHAR_TYPE *end = NULL; - CHAR_TYPE old_value = (CHAR_TYPE)0; - - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, left, SPACE_CHAR)); - - STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); - if (beg == NULL || end == NULL) - return -1; - old_value = *end; - *(CHAR_TYPE *)end = NULL_CHAR; - set_old_value = 1; - - str_it_width = STR_ITER_WIDTH(beg, end); - if (str_it_width < 0 || content_width < (size_t)str_it_width) - goto clear; - - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, content_style_tag)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, total_buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, reset_content_style_tag)); - - *(CHAR_TYPE *)end = old_value; - set_old_value = 0; - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, right, SPACE_CHAR)); - return (int)written; - -clear: - if (set_old_value) - *(CHAR_TYPE *)end = old_value; - return -1; - -#undef CHAR_TYPE -#undef NULL_CHAR -#undef NEWLINE_CHAR -#undef SPACE_CHAR -#undef SNPRINTF_FMT_STR -#undef SNPRINTF -#undef BUFFER_STR -#undef SNPRINT_N_STRINGS -#undef STR_N_SUBSTRING -#undef STR_ITER_WIDTH -} - - #ifdef FT_HAVE_WCHAR -FT_INTERNAL -int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag) -{ -#define CHAR_TYPE wchar_t -#define NULL_CHAR L'\0' -#define NEWLINE_CHAR L'\n' -#define SPACE_CHAR " " -#define SNPRINTF_FMT_STR L"%*ls" -#define SNPRINTF swprintf -#define BUFFER_STR str.wstr -#define SNPRINT_N_STRINGS wsnprint_n_string -#define STR_N_SUBSTRING wstr_n_substring -#define STR_ITER_WIDTH wcs_iter_width + 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); + break; +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + utf8_n_substring(buffer->str.wstr, '\n', buffer_row, begin, end); + if ((*(const char **)begin) && (*(const char **)end)) + *str_it_width = ut8_width(*begin, *end); + break; +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + } +} - size_t buf_len = total_buf_len - strlen(content_style_tag) - strlen(reset_content_style_tag); + +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; + } +} + + +FT_INTERNAL +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) +{ + const context_t *context = cntx->cntx; + fort_table_properties_t *props = context->table_properties; + size_t row = context->row; + size_t column = context->column; if (buffer == NULL || buffer->str.data == NULL - || buffer_row >= buffer_text_height(buffer) || buf_len == 0) { + || buffer_row >= buffer_text_visible_height(buffer)) { return -1; } - size_t content_width = buffer_text_width(buffer); - if ((buf_len - 1) < content_width) + size_t content_width = buffer_text_visible_width(buffer); + if (vis_width < content_width) return -1; size_t left = 0; size_t right = 0; - - switch (get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_TEXT_ALIGN)) { + switch (get_cell_property_value_hierarcial(props, row, column, FT_CPROP_TEXT_ALIGN)) { case FT_ALIGNED_LEFT: left = 0; - right = (buf_len - 1) - content_width; + right = (vis_width) - content_width; break; case FT_ALIGNED_CENTER: - left = ((buf_len - 1) - content_width) / 2; - right = ((buf_len - 1) - content_width) - left; + left = ((vis_width) - content_width) / 2; + right = ((vis_width) - content_width) - left; break; case FT_ALIGNED_RIGHT: - left = (buf_len - 1) - content_width; + left = (vis_width) - content_width; right = 0; break; default: @@ -5092,64 +6317,41 @@ int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, siz break; } - int set_old_value = 0; size_t written = 0; int tmp = 0; ptrdiff_t str_it_width = 0; - const CHAR_TYPE *beg = NULL; - const CHAR_TYPE *end = NULL; - CHAR_TYPE old_value = (CHAR_TYPE)0; - - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, left, SPACE_CHAR)); - - STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); + const void *beg = NULL; + const void *end = NULL; + buffer_substring(buffer, buffer_row, (void **)&beg, (void **)&end, &str_it_width); if (beg == NULL || end == NULL) return -1; - old_value = *end; - *(CHAR_TYPE *)end = NULL_CHAR; - set_old_value = 1; - - str_it_width = STR_ITER_WIDTH(beg, end); if (str_it_width < 0 || content_width < (size_t)str_it_width) - goto clear; + return -1; - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, content_style_tag)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, total_buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, reset_content_style_tag)); + size_t padding = content_width - (size_t)str_it_width; - *(CHAR_TYPE *)end = old_value; - set_old_value = 0; - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, right, SPACE_CHAR)); + 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)); return (int)written; clear: - if (set_old_value) - *(CHAR_TYPE *)end = old_value; return -1; - -#undef CHAR_TYPE -#undef NULL_CHAR -#undef NEWLINE_CHAR -#undef SPACE_CHAR -#undef SNPRINTF_FMT_STR -#undef SNPRINTF -#undef BUFFER_STR -#undef SNPRINT_N_STRINGS -#undef STR_N_SUBSTRING -#undef STR_ITER_WIDTH } -#endif /* FT_HAVE_WCHAR */ - FT_INTERNAL -size_t string_buffer_capacity(const string_buffer_t *buffer) +size_t string_buffer_width_capacity(const string_buffer_t *buffer) { assert(buffer); - if (buffer->type == CharBuf) + if (buffer->type == CHAR_BUF) return buffer->data_sz; - else + else if (buffer->type == W_CHAR_BUF) return buffer->data_sz / sizeof(wchar_t); + else if (buffer->type == UTF8_BUF) + return buffer->data_sz / 4; } @@ -5465,6 +6667,11 @@ fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *wi } +FT_INTERNAL +fort_status_t table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width) +{ + return table_geometry(table, height, width); +} /******************************************************** End of file "table.c" diff --git a/lib/fort.h b/lib/fort.h index 345b110..9fff313 100644 --- a/lib/fort.h +++ b/lib/fort.h @@ -63,6 +63,10 @@ SOFTWARE. #define FT_HAVE_WCHAR #endif +#define FT_CONGIG_HAVE_UTF8 +#if defined(FT_CONGIG_HAVE_UTF8) +#define FT_HAVE_UTF8 +#endif /***************************************************************************** * RETURN CODES @@ -483,8 +487,6 @@ int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...) - - /** * Write strings from the array to the table. * @@ -899,6 +901,18 @@ const wchar_t *ft_to_wstring(const ft_table_t *table); +#ifdef FT_HAVE_UTF8 +#define ft_u8write(table, ...)\ + (ft_nu8write(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +#define ft_u8write_ln(table, ...)\ + (ft_nu8write_ln(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +int ft_nu8write(ft_table_t *table, size_t n, const void *cell_content, ...); +int ft_nu8write_ln(ft_table_t *table, size_t n, const void *cell_content, ...); +const void *ft_to_u8string(const ft_table_t *table); + +#endif /* FT_HAVE_UTF8 */ + + FT_END_DECLS #endif /* LIBFORT_H */ diff --git a/src/cell.c b/src/cell.c index aeacd64..f03b386 100644 --- a/src/cell.c +++ b/src/cell.c @@ -14,7 +14,7 @@ fort_cell_t *create_cell(void) fort_cell_t *cell = (fort_cell_t *)F_CALLOC(sizeof(fort_cell_t), 1); if (cell == NULL) return NULL; - cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); + cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CHAR_BUF); if (cell->str_buffer == NULL) { F_FREE(cell); return NULL; @@ -78,7 +78,7 @@ size_t hint_width_cell(const fort_cell_t *cell, const context_t *context, enum r size_t cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_RIGHT_PADDING); size_t result = cell_padding_left + cell_padding_right; if (cell->str_buffer && cell->str_buffer->str.data) { - result += buffer_text_width(cell->str_buffer); + result += buffer_text_visible_width(cell->str_buffer); } result = MAX(result, (size_t)get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_MIN_WIDTH)); @@ -113,7 +113,7 @@ size_t hint_height_cell(const fort_cell_t *cell, const context_t *context) size_t cell_empty_string_height = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_EMPTY_STR_HEIGHT); size_t result = cell_padding_top + cell_padding_bottom; if (cell->str_buffer && cell->str_buffer->str.data) { - size_t text_height = buffer_text_height(cell->str_buffer); + size_t text_height = buffer_text_visible_height(cell->str_buffer); result += text_height == 0 ? cell_empty_string_height : text_height; } return result; @@ -121,14 +121,12 @@ size_t hint_height_cell(const fort_cell_t *cell, const context_t *context) FT_INTERNAL -int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context) +int cell_printf(fort_cell_t *cell, size_t row, conv_context_t *cntx, size_t vis_width) { - const char *space_char = " "; - int (*buffer_printf_)(string_buffer_t *, size_t, char *, size_t, const context_t *, const char *, const char *) = buffer_printf; - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; + const context_t *context = cntx->cntx; + size_t buf_len = vis_width; - if (cell == NULL || buf_len == 0 - || (buf_len <= hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { + if (cell == NULL || (vis_width < hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { return -1; } @@ -173,32 +171,35 @@ int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const #define TOTAL_WRITTEN (written + invisible_written) #define RIGHT (cell_padding_right + extra_right) -#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, cell_style_tag)) -#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_cell_style_tag)) -#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, content_style_tag)) -#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_content_style_tag)) +#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, cell_style_tag)) +#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_cell_style_tag)) +#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, content_style_tag)) +#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag)) if (row >= hint_height_cell(cell, context) || row < cell_padding_top - || row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { + || row >= (cell_padding_top + buffer_text_visible_height(cell->str_buffer))) { WRITE_CELL_STYLE_TAG; WRITE_CONTENT_STYLE_TAG; WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len, buf_len - 1 - TOTAL_WRITTEN - R3, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, buf_len - TOTAL_WRITTEN - R3, FT_SPACE)); WRITE_RESET_CELL_STYLE_TAG; + return (int)TOTAL_WRITTEN; } WRITE_CELL_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, L2, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, L2, FT_SPACE)); if (cell->str_buffer) { - CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, context, content_style_tag, reset_content_style_tag)); +// CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf2_(cell->str_buffer, row - cell_padding_top, cntx, buf_len - TOTAL_WRITTEN - R2 - R3, content_style_tag, reset_content_style_tag)); + CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf(cell->str_buffer, row - cell_padding_top, cntx, vis_width - L2 - R2 , content_style_tag, reset_content_style_tag)); } else { WRITE_CONTENT_STYLE_TAG; WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, space_char)); +// CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings2_(cntx, buf_len - TOTAL_WRITTEN - R2 - R3, FT_SPACE)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, vis_width - L2 - R2, FT_SPACE)); } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, R2, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, R2, FT_SPACE)); WRITE_RESET_CELL_STYLE_TAG; return (int)TOTAL_WRITTEN; @@ -213,101 +214,6 @@ clear: #undef RIGHT } -#ifdef FT_HAVE_WCHAR -FT_INTERNAL -int cell_wprintf(fort_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context) -{ - const char *space_char = " "; - int (*buffer_printf_)(string_buffer_t *, size_t, wchar_t *, size_t, const context_t *, const char *, const char *) = buffer_wprintf; - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - - if (cell == NULL || buf_len == 0 - || (buf_len <= hint_width_cell(cell, context, VISIBLE_GEOMETRY))) { - return -1; - } - - unsigned int cell_padding_top = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_TOP_PADDING); - unsigned int cell_padding_left = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_LEFT_PADDING); - unsigned int cell_padding_right = get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_RIGHT_PADDING); - - size_t written = 0; - size_t invisible_written = 0; - int tmp = 0; - - /* todo: Dirty hack with changing buf_len! need refactoring. */ - /* Also maybe it is better to move all struff with colors to buffers? */ - char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_style_tag_for_cell(context->table_properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(cell_style_tag); - - char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_reset_style_tag_for_cell(context->table_properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(reset_cell_style_tag); - - char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_style_tag_for_content(context->table_properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(content_style_tag); - - char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; - get_reset_style_tag_for_content(context->table_properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); - buf_len += strlen(reset_content_style_tag); - - /* CELL_STYLE_T LEFT_PADDING CONTENT_STYLE_T CONTENT RESET_CONTENT_STYLE_T RIGHT_PADDING RESET_CELL_STYLE_T - * | | | | | | | | - * L1 R1 - * L2 R2 - * L3 R3 - */ - - size_t L2 = cell_padding_left; - - size_t R2 = cell_padding_right; - size_t R3 = strlen(reset_cell_style_tag); - -#define TOTAL_WRITTEN (written + invisible_written) -#define RIGHT (right + extra_right) - -#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, cell_style_tag)) -#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_cell_style_tag)) -#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, content_style_tag)) -#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, 1, reset_content_style_tag)) - - if (row >= hint_height_cell(cell, context) - || row < cell_padding_top - || row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { - WRITE_CELL_STYLE_TAG; - WRITE_CONTENT_STYLE_TAG; - WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len, buf_len - 1 - TOTAL_WRITTEN - R3, space_char)); - WRITE_RESET_CELL_STYLE_TAG; - return (int)TOTAL_WRITTEN; - } - - WRITE_CELL_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, L2, space_char)); - if (cell->str_buffer) { - CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, context, content_style_tag, reset_content_style_tag)); - } else { - WRITE_CONTENT_STYLE_TAG; - WRITE_RESET_CONTENT_STYLE_TAG; - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN - R2 - R3, space_char)); - } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + TOTAL_WRITTEN, buf_len - TOTAL_WRITTEN, R2, space_char)); - WRITE_RESET_CELL_STYLE_TAG; - - return (int)TOTAL_WRITTEN; - -clear: - return -1; -#undef WRITE_CELL_STYLE_TAG -#undef WRITE_RESET_CELL_STYLE_TAG -#undef WRITE_CONTENT_STYLE_TAG -#undef WRITE_RESET_CONTENT_STYLE_TAG -#undef TOTAL_WRITTEN -#undef RIGHT -} -#endif - FT_INTERNAL fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str) { diff --git a/src/cell.h b/src/cell.h index 7daa3bb..8a9befa 100644 --- a/src/cell.h +++ b/src/cell.h @@ -25,15 +25,12 @@ FT_INTERNAL enum CellType get_cell_type(const fort_cell_t *cell); FT_INTERNAL -int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context); +int cell_printf(fort_cell_t *cell, size_t row, conv_context_t *cntx, size_t cod_width); FT_INTERNAL fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str); #ifdef FT_HAVE_WCHAR -FT_INTERNAL -int cell_wprintf(fort_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context); - FT_INTERNAL fort_status_t fill_cell_from_wstring(fort_cell_t *cell, const wchar_t *str); #endif diff --git a/src/fort.h b/src/fort.h index 345b110..9fff313 100644 --- a/src/fort.h +++ b/src/fort.h @@ -63,6 +63,10 @@ SOFTWARE. #define FT_HAVE_WCHAR #endif +#define FT_CONGIG_HAVE_UTF8 +#if defined(FT_CONGIG_HAVE_UTF8) +#define FT_HAVE_UTF8 +#endif /***************************************************************************** * RETURN CODES @@ -483,8 +487,6 @@ int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...) - - /** * Write strings from the array to the table. * @@ -899,6 +901,18 @@ const wchar_t *ft_to_wstring(const ft_table_t *table); +#ifdef FT_HAVE_UTF8 +#define ft_u8write(table, ...)\ + (ft_nu8write(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +#define ft_u8write_ln(table, ...)\ + (ft_nu8write_ln(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +int ft_nu8write(ft_table_t *table, size_t n, const void *cell_content, ...); +int ft_nu8write_ln(ft_table_t *table, size_t n, const void *cell_content, ...); +const void *ft_to_u8string(const ft_table_t *table); + +#endif /* FT_HAVE_UTF8 */ + + FT_END_DECLS #endif /* LIBFORT_H */ diff --git a/src/fort_impl.c b/src/fort_impl.c index dbc5255..a931686 100644 --- a/src/fort_impl.c +++ b/src/fort_impl.c @@ -344,6 +344,23 @@ static int ft_write_impl(ft_table_t *table, const char *cell_content) return status; } +#ifdef FT_HAVE_UTF8 +static int ft_u8write_impl(ft_table_t *table, const void *cell_content) +{ + assert(table); + string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table); + if (str_buffer == NULL) + return FT_ERROR; + + int status = fill_buffer_from_u8string(str_buffer, cell_content); + if (FT_IS_SUCCESS(status)) { + table->cur_col++; + } + return status; +} +#endif /* FT_HAVE_UTF8 */ + + #ifdef FT_HAVE_WCHAR @@ -412,6 +429,9 @@ int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...) return status; } + + + #ifdef FT_HAVE_WCHAR int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...) @@ -577,209 +597,111 @@ int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_ } #endif +static +const char * empty_str_arr[] = {"", (const char *)L"", ""}; +static +const char *ft_to_string_impl(const ft_table_t *table, enum str_buf_type b_type) +{ + assert(table); + + const char *result = NULL; + + /* Determing size of table string representation */ + size_t cod_height = 0; + size_t cod_width = 0; + int status = table_internal_codepoints_geometry(table, &cod_height, &cod_width); + if (FT_IS_ERROR(status)) { + return NULL; + } + size_t n_codepoints = cod_height * cod_width + 1; + + /* Allocate string buffer for string representation */ + if (table->conv_buffer == NULL) { + ((ft_table_t *)table)->conv_buffer = create_string_buffer(n_codepoints, b_type); + if (table->conv_buffer == NULL) + return NULL; + } + while (string_buffer_cod_width_capacity(table->conv_buffer) < n_codepoints) { + if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { + return NULL; + } + } + char *buffer = (char *)buffer_get_data(table->conv_buffer); + + size_t cols = 0; + size_t rows = 0; + size_t *col_vis_width_arr = NULL; + size_t *row_vis_height_arr = NULL; + status = table_rows_and_cols_geometry(table, &col_vis_width_arr, &cols, &row_vis_height_arr, &rows, VISIBLE_GEOMETRY); + if (FT_IS_ERROR(status)) + return NULL; + + if (rows == 0) { + result = empty_str_arr[b_type]; + goto clear; + } + + int tmp = 0; + size_t i = 0; + context_t context; + context.table_properties = (table->properties ? table->properties : &g_table_properties); + fort_row_t *prev_row = NULL; + fort_row_t *cur_row = NULL; + separator_t *cur_sep = NULL; + size_t sep_size = vector_size(table->separators); + + conv_context_t cntx; + cntx.buf_origin = buffer; + cntx.buf = buffer; + cntx.raw_avail = string_buffer_raw_capacity(table->conv_buffer); + cntx.cntx = &context; + cntx.b_type = b_type; + + /* Print top margin */ + for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { + FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE)); + FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE)); + } + + for (i = 0; i < rows; ++i) { + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + cur_row = *(fort_row_t **)vector_at(table->rows, i); + enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; + context.row = i; + FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep)); + FT_CHECK(snprintf_row(cur_row, &cntx, col_vis_width_arr, cols, row_vis_height_arr[i])); + prev_row = cur_row; + } + cur_row = NULL; + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + context.row = i; + FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep)); + + /* Print bottom margin */ + for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { + FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE)); + FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE)); + } + + result = buffer; + +clear: + F_FREE(col_vis_width_arr); + F_FREE(row_vis_height_arr); + return result; +} const char *ft_to_string(const ft_table_t *table) { - typedef char char_type; - const enum str_buf_type buf_type = CharBuf; - const char *space_char = " "; - const char *new_line_char = "\n"; -#define EMPTY_STRING "" - int (*snprintf_row_)(const fort_row_t *, char *, size_t, size_t *, size_t, size_t, const context_t *) = snprintf_row; - int (*print_row_separator_)(char *, size_t, - const size_t *, size_t, - const fort_row_t *, const fort_row_t *, - enum HorSeparatorPos, const separator_t *, - const context_t *) = print_row_separator; - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; - assert(table); - - /* Determing size of table string representation */ - size_t height = 0; - size_t width = 0; - int status = table_geometry(table, &height, &width); - if (FT_IS_ERROR(status)) { - return NULL; - } - size_t sz = height * width + 1; - - /* Allocate string buffer for string representation */ - if (table->conv_buffer == NULL) { - ((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); - if (table->conv_buffer == NULL) - return NULL; - } - while (string_buffer_capacity(table->conv_buffer) < sz) { - if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { - return NULL; - } - } - char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); - - - size_t cols = 0; - size_t rows = 0; - size_t *col_width_arr = NULL; - size_t *row_height_arr = NULL; - status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, VISIBLE_GEOMETRY); - if (FT_IS_ERROR(status)) - return NULL; - - if (rows == 0) - return EMPTY_STRING; - - size_t written = 0; - int tmp = 0; - size_t i = 0; - context_t context; - context.table_properties = (table->properties ? table->properties : &g_table_properties); - fort_row_t *prev_row = NULL; - fort_row_t *cur_row = NULL; - separator_t *cur_sep = NULL; - size_t sep_size = vector_size(table->separators); - - /* Print top margin */ - for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - for (i = 0; i < rows; ++i) { - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - cur_row = *(fort_row_t **)vector_at(table->rows, i); - enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); - CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); - prev_row = cur_row; - } - cur_row = NULL; - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); - - /* Print bottom margin */ - for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return buffer; - -clear: - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return NULL; -#undef EMPTY_STRING + return ft_to_string_impl(table, CHAR_BUF); } - #ifdef FT_HAVE_WCHAR - const wchar_t *ft_to_wstring(const ft_table_t *table) { - typedef wchar_t char_type; - const enum str_buf_type buf_type = WCharBuf; - const char *space_char = " "; - const char *new_line_char = "\n"; -#define EMPTY_STRING L"" - int (*snprintf_row_)(const fort_row_t *, wchar_t *, size_t, size_t *, size_t, size_t, const context_t *) = wsnprintf_row; - int (*print_row_separator_)(wchar_t *, size_t, - const size_t *, size_t, - const fort_row_t *, const fort_row_t *, - enum HorSeparatorPos, const separator_t *, - const context_t *) = wprint_row_separator; - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - - - assert(table); - - /* Determing size of table string representation */ - size_t height = 0; - size_t width = 0; - int status = table_geometry(table, &height, &width); - if (FT_IS_ERROR(status)) { - return NULL; - } - size_t sz = height * width + 1; - - /* Allocate string buffer for string representation */ - if (table->conv_buffer == NULL) { - ((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); - if (table->conv_buffer == NULL) - return NULL; - } - while (string_buffer_capacity(table->conv_buffer) < sz) { - if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { - return NULL; - } - } - char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); - - - size_t cols = 0; - size_t rows = 0; - size_t *col_width_arr = NULL; - size_t *row_height_arr = NULL; - status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, VISIBLE_GEOMETRY); - - if (rows == 0) - return EMPTY_STRING; - - if (FT_IS_ERROR(status)) - return NULL; - - size_t written = 0; - int tmp = 0; - size_t i = 0; - context_t context; - context.table_properties = (table->properties ? table->properties : &g_table_properties); - fort_row_t *prev_row = NULL; - fort_row_t *cur_row = NULL; - separator_t *cur_sep = NULL; - size_t sep_size = vector_size(table->separators); - - /* Print top margin */ - for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - for (i = 0; i < rows; ++i) { - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - cur_row = *(fort_row_t **)vector_at(table->rows, i); - enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); - CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); - prev_row = cur_row; - } - cur_row = NULL; - cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; - context.row = i; - CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); - - /* Print bottom margin */ - for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); - } - - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return buffer; - -clear: - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return NULL; -#undef EMPTY_STRING + return (const wchar_t *)ft_to_string_impl(table, W_CHAR_BUF); } - #endif @@ -999,3 +921,61 @@ int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span) return row_set_cell_span(row_p, col, hor_span); } + +#ifdef FT_HAVE_UTF8 + +int ft_nu8write(ft_table_t *table, size_t n, const void *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_u8write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const void *cell = va_arg(va, const void *); + status = ft_u8write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} + +int ft_nu8write_ln(ft_table_t *table, size_t n, const void *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_u8write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const void *cell = va_arg(va, const void *); + status = ft_u8write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} + +const void *ft_to_u8string(const ft_table_t *table) +{ + return (const void *)ft_to_string_impl(table, UTF8_BUF); +} +#endif /* FT_HAVE_UTF8 */ diff --git a/src/fort_utils.c b/src/fort_utils.c index f1f69ef..aa90f4c 100644 --- a/src/fort_utils.c +++ b/src/fort_utils.c @@ -2,6 +2,9 @@ #ifdef FT_HAVE_WCHAR #include #endif +#if defined(FT_HAVE_UTF8) +#include "utf8.h" +#endif char g_col_separator = FORT_DEFAULT_COL_SEPARATOR; @@ -169,9 +172,26 @@ size_t number_of_columns_in_format_wstring(const wchar_t *fmt) } #endif - +#if defined(FT_HAVE_UTF8) FT_INTERNAL -int snprint_n_strings(char *buf, size_t length, size_t n, const char *str) +size_t number_of_columns_in_format_u8string(const void *fmt) +{ + size_t separator_counter = 0; + const char *pos = fmt; + while (1) { + pos = utf8chr(pos, g_col_separator); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} +#endif + +static +int snprint_n_strings_impl(char *buf, size_t length, size_t n, const char *str) { size_t str_len = strlen(str); if (length <= n * str_len) @@ -200,17 +220,123 @@ int snprint_n_strings(char *buf, size_t length, size_t n, const char *str) return (int)(n * str_len); } +static +int snprint_n_strings(conv_context_t *cntx, size_t n, const char *str) +{ + int w = snprint_n_strings_impl(cntx->buf, cntx->raw_avail, n, str); + if (w >= 0) { + cntx->buf += w; + cntx->raw_avail -= w; + } + return w; +} +#if defined(FT_HAVE_WCHAR) +static +int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); +#endif + +#if defined(FT_HAVE_UTF8) +static +int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str); +#endif + + +FT_INTERNAL +int print_n_strings(conv_context_t *cntx, size_t n, const char *str) +{ + int cod_w; + int raw_written; + + switch (cntx->b_type) { + case CHAR_BUF: + return snprint_n_strings(cntx, n, str); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + cod_w = wsnprint_n_string((wchar_t *)cntx->buf, cntx->raw_avail, n, str); + if (cod_w < 0) + return cod_w; + raw_written = sizeof(wchar_t) * cod_w; + + cntx->buf += raw_written; + cntx->raw_avail -= raw_written; + return cod_w; +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + /* Everying is very strange and differs with W_CHAR_BUF */ + raw_written = u8nprint_n_strings(cntx->buf, cntx->raw_avail, n, str); + if (raw_written < 0) { + fprintf(stderr, " raw_written = %d\n", raw_written); + return raw_written; + } + + cntx->buf += raw_written; + cntx->raw_avail -= raw_written; + return utf8len(str) * n; +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + return -1; + } +} + +FT_INTERNAL +int ft_nprint(conv_context_t *cntx, const char *str, size_t strlen) +{ + if (cntx->raw_avail + 1/* for 0 */ < strlen) + return -1; + + memcpy(cntx->buf, str, strlen); + cntx->buf += strlen; + cntx->raw_avail -= strlen; + *cntx->buf = '\0'; /* Do we need this ? */ + return strlen; +} + +#ifdef FT_HAVE_WCHAR +int ft_nwprint(conv_context_t *cntx, const wchar_t *str, size_t strlen) +{ + if (cntx->raw_avail + 1/* for 0 */ < strlen) + return -1; + + size_t raw_len = strlen * sizeof(wchar_t); + + memcpy(cntx->buf, str, raw_len); + cntx->buf += raw_len; + cntx->raw_avail -= raw_len; + *(wchar_t *)cntx->buf = L'\0'; /* Do we need this ? */ + return strlen; +} +#endif /* FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +int ft_nu8print(conv_context_t *cntx, const void *beg, const void *end) +{ + const char *bc = beg; + const char *ec = end; + size_t raw_len = ec - bc; + if (cntx->raw_avail + 1 < raw_len) + return -1; + + memcpy(cntx->buf, beg, raw_len); + cntx->buf += raw_len; + cntx->raw_avail -= raw_len; + *(char *)cntx->buf = '\0'; /* Do we need this ? */ + return raw_len; /* what return here ? */ +} +#endif /* FT_HAVE_UTF8 */ #if defined(FT_HAVE_WCHAR) #define WCS_SIZE 64 -FT_INTERNAL +static int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) { size_t str_len = strlen(str); - /* note: baybe it's, better to return -1 in case of multibyte character + /* note: maybe it's, better to return -1 in case of multibyte character * strings (not sure this case is done correctly). */ if (str_len > 1) { @@ -269,3 +395,33 @@ int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) return (int)(n * str_len); } #endif + + +#if defined(FT_HAVE_UTF8) +static +int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str) +{ + size_t str_size = utf8size(str) - 1; /* str_size - raw size in bytes, excluding \0 */ + if (length <= n * str_size) + return -1; + + if (n == 0) + return 0; + + /* To ensure valid return value it is safely not print such big strings */ + if (n * str_size > INT_MAX) + return -1; + + if (str_size == 0) + return 0; + + size_t i = n; + while (i) { + memcpy(buf, str, str_size); + buf = (char *)buf + str_size; + --i; + } + *(char *)buf = '\0'; + return (int)(n * str_size); +} +#endif diff --git a/src/fort_utils.h b/src/fort_utils.h index d3afb62..33d9721 100644 --- a/src/fort_utils.h +++ b/src/fort_utils.h @@ -35,12 +35,16 @@ extern char g_col_separator; #define F_REALLOC fort_realloc #define F_STRDUP fort_strdup #define F_WCSDUP fort_wcsdup +/* @todo: replace with custom impl !!!*/ +#define F_UTF8DUP utf8dup #define F_CREATE(type) ((type *)F_CALLOC(sizeof(type), 1)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define FT_NEWLINE "\n" +#define FT_SPACE " " enum PolicyOnNull { Create, @@ -53,6 +57,20 @@ enum F_BOOL { F_TRUE = 1 }; +enum str_buf_type { + CHAR_BUF, +#ifdef FT_HAVE_WCHAR + W_CHAR_BUF, +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + UTF8_BUF, +#endif /* FT_HAVE_WCHAR */ + TYPE_END +}; + + +typedef const char ** str_arr; + #define FT_STR_2_CAT_(arg1, arg2) \ arg1##arg2 @@ -117,6 +135,16 @@ enum request_geom_type { INTERN_REPR_GEOMETRY }; +struct conv_context { + char *buf_origin; + char *buf; + size_t raw_avail; + struct fort_context *cntx; + enum str_buf_type b_type; +}; +typedef struct conv_context conv_context_t; + + /***************************************************************************** * LIBFORT helpers *****************************************************************************/ @@ -144,18 +172,38 @@ size_t number_of_columns_in_format_wstring(const wchar_t *fmt); #endif FT_INTERNAL -int snprint_n_strings(char *buf, size_t length, size_t n, const char *str); +int print_n_strings(conv_context_t *cntx, size_t n, const char *str); + -#if defined(FT_HAVE_WCHAR) FT_INTERNAL -int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); -#endif +int ft_nprint(conv_context_t *cntx, const char *str, size_t strlen); +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +int ft_nwprint(conv_context_t *cntx, const wchar_t *str, size_t strlen); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +int ft_nu8print(conv_context_t *cntx, const void *beg, const void *end); +#endif /* FT_HAVE_UTF8 */ +/*#define PRINT_DEBUG_INFO fprintf(stderr, "error in %s(%s:%d)\n", __FUNCTION__, __FILE__, __LINE__);*/ +#define PRINT_DEBUG_INFO + +#define FT_CHECK(statement) \ + do { \ + tmp = statement; \ + if (tmp < 0) {\ + PRINT_DEBUG_INFO \ + goto clear; \ + } \ + } while(0) + #define CHCK_RSLT_ADD_TO_WRITTEN(statement) \ do { \ tmp = statement; \ if (tmp < 0) {\ + PRINT_DEBUG_INFO \ goto clear; \ } \ written += (size_t)tmp; \ @@ -165,6 +213,7 @@ int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); do { \ tmp = statement; \ if (tmp < 0) {\ + PRINT_DEBUG_INFO \ goto clear; \ } \ invisible_written += (size_t)tmp; \ diff --git a/src/row.c b/src/row.c index 649b341..f625f68 100644 --- a/src/row.c +++ b/src/row.c @@ -225,22 +225,20 @@ fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_ return FT_SUCCESS; } - FT_INTERNAL -int print_row_separator(char *buffer, size_t buffer_sz, +int print_row_separator_impl(conv_context_t *cntx, const size_t *col_width_arr, size_t cols, const fort_row_t *upper_row, const fort_row_t *lower_row, enum HorSeparatorPos separatorPos, - const separator_t *sep, const context_t *context) + const separator_t *sep) { - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; - - assert(buffer); - assert(context); + assert(cntx); const char *space_char = " "; int status = -1; + const context_t *context = cntx->cntx; + /* Get cell types * * Regions above top row and below bottom row areconsidered full of virtual @@ -378,31 +376,31 @@ int print_row_separator(char *buffer, size_t buffer_sz, } /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.left_margin, space_char)); for (i = 0; i < cols; ++i) { if (i == 0) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L)); } else { if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) && (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV)); } else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *II)); } else if (top_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IT)); } else { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IB)); } } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[i], *I)); } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R)); /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.right_margin, space_char)); - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, "\n")); status = (int)written; @@ -411,195 +409,16 @@ clear: return status; } - -#ifdef FT_HAVE_WCHAR FT_INTERNAL -int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, - const size_t *col_width_arr, size_t cols, - const fort_row_t *upper_row, const fort_row_t *lower_row, - enum HorSeparatorPos separatorPos, const separator_t *sep, - const context_t *context) +int print_row_separator(conv_context_t *cntx, + const size_t *col_width_arr, size_t cols, + const fort_row_t *upper_row, const fort_row_t *lower_row, + enum HorSeparatorPos separatorPos, const separator_t *sep) { - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - - assert(buffer); - assert(context); - - const char *space_char = " "; - int status = -1; - - /* Get cell types - * - * Regions above top row and below bottom row areconsidered full of virtual - * GroupSlaveCell cells - */ - enum CellType *top_row_types = (enum CellType *)F_MALLOC(sizeof(enum CellType) * cols * 2); - if (top_row_types == NULL) { - return FT_MEMORY_ERROR; - } - enum CellType *bottom_row_types = top_row_types + cols; - if (upper_row) { - get_row_cell_types(upper_row, top_row_types, cols); - } else { - size_t i = 0; - for (i = 0; i < cols; ++i) - top_row_types[i] = GroupSlaveCell; - } - if (lower_row) { - get_row_cell_types(lower_row, bottom_row_types, cols); - } else { - size_t i = 0; - for (i = 0; i < cols; ++i) - bottom_row_types[i] = GroupSlaveCell; - } - - - size_t written = 0; - int tmp = 0; - - enum ft_row_type lower_row_type = FT_ROW_COMMON; - if (lower_row != NULL) { - lower_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); - } - enum ft_row_type upper_row_type = FT_ROW_COMMON; - if (upper_row != NULL) { - upper_row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row - 1, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); - } - - /* Row separator anatomy - * - * | C11 | C12 C13 | C14 C15 | - * L I I I IV I I IT I I I IB I I II I I R - * | C21 | C22 | C23 C24 C25 | - */ - const char **L = NULL; - const char **I = NULL; - const char **IV = NULL; - const char **R = NULL; - const char **IT = NULL; - const char **IB = NULL; - const char **II = NULL; - - - typedef const char *(*border_chars_point_t)[BorderItemPosSize]; - const char *(*border_chars)[BorderItemPosSize] = NULL; - border_chars = (border_chars_point_t)&context->table_properties->border_style.border_chars; - if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) { - border_chars = (border_chars_point_t)&context->table_properties->border_style.header_border_chars; - } - - if (sep && sep->enabled) { - L = &(context->table_properties->border_style.separator_chars[LH_sip]); - I = &(context->table_properties->border_style.separator_chars[IH_sip]); - IV = &(context->table_properties->border_style.separator_chars[II_sip]); - R = &(context->table_properties->border_style.separator_chars[RH_sip]); - - IT = &(context->table_properties->border_style.separator_chars[TI_sip]); - IB = &(context->table_properties->border_style.separator_chars[BI_sip]); - II = &(context->table_properties->border_style.separator_chars[IH_sip]); - - if (lower_row == NULL) { - L = &(*border_chars)[BL_bip]; - R = &(*border_chars)[BR_bip]; - } else if (upper_row == NULL) { - L = &(*border_chars)[TL_bip]; - R = &(*border_chars)[TR_bip]; - } - } else { - switch (separatorPos) { - case TopSeparator: - L = &(*border_chars)[TL_bip]; - I = &(*border_chars)[TT_bip]; - IV = &(*border_chars)[TV_bip]; - R = &(*border_chars)[TR_bip]; - - IT = &(*border_chars)[TV_bip]; - IB = &(*border_chars)[TV_bip]; - II = &(*border_chars)[TT_bip]; - break; - case InsideSeparator: - L = &(*border_chars)[LH_bip]; - I = &(*border_chars)[IH_bip]; - IV = &(*border_chars)[II_bip]; - R = &(*border_chars)[RH_bip]; - - /* - IT = &(*border_chars)[TV_bip]; - IB = &(*border_chars)[BV_bip]; - */ - IT = &(*border_chars)[TI_bip]; - IB = &(*border_chars)[BI_bip]; - II = &(*border_chars)[IH_bip]; - break; - case BottomSeparator: - L = &(*border_chars)[BL_bip]; - I = &(*border_chars)[BB_bip]; - IV = &(*border_chars)[BV_bip]; - R = &(*border_chars)[BR_bip]; - - IT = &(*border_chars)[BV_bip]; - IB = &(*border_chars)[BV_bip]; - II = &(*border_chars)[BB_bip]; - break; - default: - break; - } - } - - size_t i = 0; - - /* If all chars are not printable, skip line separator */ - /* todo: add processing for wchar_t */ - /* - if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) { - status = 0; - goto clear; - } - */ - if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L))) - && (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I))) - && (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV))) - && (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) { - status = 0; - goto clear; - } - - /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); - - for (i = 0; i < cols; ++i) { - if (i == 0) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); - } else { - if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) - && (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); - } else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); - } else if (top_row_types[i] == GroupSlaveCell) { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); - } else { - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); - } - } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); - } - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); - - /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); - - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); - - status = (int)written; - -clear: - F_FREE(top_row_types); - return status; + return print_row_separator_impl(cntx, col_width_arr, cols, upper_row, lower_row, + separatorPos, sep); } -#endif - FT_INTERNAL fort_row_t *create_row_from_string(const char *str) { @@ -784,7 +603,7 @@ fort_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args) #define CREATE_ROW_FROM_STRING create_row_from_string #define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_string #define FILL_CELL_FROM_STRING fill_cell_from_string -#define STR_BUF_TYPE CharBuf +#define STR_BUF_TYPE CHAR_BUF string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, STR_BUF_TYPE); if (buffer == NULL) @@ -796,14 +615,14 @@ fort_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args) while (1) { va_list va; va_copy(va, *va_args); - int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); + int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_width_capacity(buffer), fmt, va); va_end(va); /* If error encountered */ if (virtual_sz < 0) goto clear; /* Successful write */ - if ((size_t)virtual_sz < string_buffer_capacity(buffer)) + if ((size_t)virtual_sz < string_buffer_width_capacity(buffer)) break; /* Otherwise buffer was too small, so incr. buffer size ant try again. */ @@ -870,7 +689,7 @@ fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args) #define CREATE_ROW_FROM_STRING create_row_from_wstring #define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_wstring #define FILL_CELL_FROM_STRING fill_cell_from_wstring -#define STR_BUF_TYPE WCharBuf +#define STR_BUF_TYPE W_CHAR_BUF string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, STR_BUF_TYPE); if (buffer == NULL) @@ -882,14 +701,14 @@ fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args) while (1) { va_list va; va_copy(va, *va_args); - int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); + int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_width_capacity(buffer), fmt, va); va_end(va); /* If error encountered */ if (virtual_sz < 0) goto clear; /* Successful write */ - if ((size_t)virtual_sz < string_buffer_capacity(buffer)) + if ((size_t)virtual_sz < string_buffer_width_capacity(buffer)) break; /* Otherwise buffer was too small, so incr. buffer size ant try again. */ @@ -950,12 +769,10 @@ clear: FT_INTERNAL -int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context) +int snprintf_row(const fort_row_t *row, conv_context_t *cntx, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height) { - int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; - int (*cell_printf_)(fort_cell_t *, size_t, char *, size_t, const context_t *) = cell_printf; - + const context_t *context = cntx->cntx; assert(context); const char *space_char = " "; const char *new_line_char = "\n"; @@ -987,142 +804,50 @@ int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col size_t i = 0; for (i = 0; i < row_height; ++i) { /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.left_margin, space_char)); /* Print left table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L)); size_t j = 0; while (j < col_width_arr_sz) { if (j < cols_in_row) { ((context_t *)context)->column = j; fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j); - size_t cell_width = 0; + size_t cell_vis_width = 0; size_t group_slave_sz = group_cell_number(row, j); - cell_width = col_width_arr[j]; + cell_vis_width = col_width_arr[j]; size_t slave_j = 0; size_t master_j = j; for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { - cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; + cell_vis_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; ++j; } - CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); + CHCK_RSLT_ADD_TO_WRITTEN(cell_printf(cell, i, cntx, cell_vis_width)); } else { /* Print empty cell */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[j], space_char)); } /* Print boundary between cells */ if (j < col_width_arr_sz - 1) - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV)); ++j; } /* Print right table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R)); /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, context->table_properties->entire_table_properties.right_margin, space_char)); /* Print new line character */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, new_line_char)); } return (int)written; clear: return -1; } - - -#ifdef FT_HAVE_WCHAR -FT_INTERNAL -int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context) -{ - int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; - int (*cell_printf_)(fort_cell_t *, size_t, wchar_t *, size_t, const context_t *) = cell_wprintf; - - assert(context); - const char *space_char = " "; - const char *new_line_char = "\n"; - - if (row == NULL) - return -1; - - size_t cols_in_row = columns_in_row(row); - if (cols_in_row > col_width_arr_sz) - return -1; - - /* Row separator anatomy - * - * L data IV data IV data R - */ - - typedef const char *(*border_chars_point_t)[BorderItemPosSize]; - enum ft_row_type row_type = (enum ft_row_type)get_cell_property_value_hierarcial(context->table_properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); - const char *(*bord_chars)[BorderItemPosSize] = (row_type == FT_ROW_HEADER) - ? (border_chars_point_t)(&context->table_properties->border_style.header_border_chars) - : (border_chars_point_t)(&context->table_properties->border_style.border_chars); - const char **L = &(*bord_chars)[LL_bip]; - const char **IV = &(*bord_chars)[IV_bip]; - const char **R = &(*bord_chars)[RR_bip]; - - - size_t written = 0; - int tmp = 0; - size_t i = 0; - for (i = 0; i < row_height; ++i) { - /* Print left margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.left_margin, space_char)); - - /* Print left table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); - size_t j = 0; - while (j < col_width_arr_sz) { - if (j < cols_in_row) { - ((context_t *)context)->column = j; - fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j); - size_t cell_width = 0; - - size_t group_slave_sz = group_cell_number(row, j); - cell_width = col_width_arr[j]; - size_t slave_j = 0; - size_t master_j = j; - for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { - cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; - ++j; - } - - CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); - } else { - /* Print empty cell */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); - } - - /* Print boundary between cells */ - if (j < col_width_arr_sz - 1) - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); - - ++j; - } - - /* Print right table boundary */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); - - /* Print right margin */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_properties->entire_table_properties.right_margin, space_char)); - - /* Print new line character */ - CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); - } - return (int)written; - -clear: - return -1; -} - -#endif - - diff --git a/src/row.h b/src/row.h index 1f5e630..680f401 100644 --- a/src/row.h +++ b/src/row.h @@ -50,15 +50,14 @@ FT_INTERNAL fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_span); FT_INTERNAL -int print_row_separator(char *buffer, size_t buffer_sz, +int print_row_separator(conv_context_t *cntx, const size_t *col_width_arr, size_t cols, const fort_row_t *upper_row, const fort_row_t *lower_row, - enum HorSeparatorPos separatorPos, const separator_t *sep, - const context_t *context); + enum HorSeparatorPos separatorPos, const separator_t *sep); FT_INTERNAL -int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context); +int snprintf_row(const fort_row_t *row, conv_context_t *cntx, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height); #ifdef FT_HAVE_WCHAR FT_INTERNAL @@ -66,17 +65,6 @@ fort_row_t *create_row_from_wstring(const wchar_t *str); FT_INTERNAL fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args); - -FT_INTERNAL -int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, - const size_t *col_width_arr, size_t cols, - const fort_row_t *upper_row, const fort_row_t *lower_row, - enum HorSeparatorPos separatorPos, const separator_t *sep, - const context_t *context); - -FT_INTERNAL -int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, - size_t row_height, const context_t *context); #endif diff --git a/src/string_buffer.c b/src/string_buffer.c index 6eb0503..011a9c0 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -3,8 +3,12 @@ #include "wcwidth.h" #include #include +#ifdef FT_HAVE_WCHAR #include - +#endif +#if defined(FT_HAVE_UTF8) +#include "utf8.h" +#endif static ptrdiff_t str_iter_width(const char *beg, const char *end) { @@ -25,11 +29,22 @@ static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end) static size_t buf_str_len(const string_buffer_t *buf) { assert(buf); - if (buf->type == CharBuf) { - return strlen(buf->str.cstr); - } else { - return wcslen(buf->str.wstr); + + 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 } + + assert(0); + return 0; } @@ -49,7 +64,7 @@ size_t strchr_count(const char *str, char ch) return count; } - +#ifdef FT_HAVE_WCHAR FT_INTERNAL size_t wstrchr_count(const wchar_t *str, wchar_t ch) { @@ -65,6 +80,34 @@ size_t wstrchr_count(const wchar_t *str, wchar_t ch) } return count; } +#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 */ FT_INTERNAL @@ -112,6 +155,29 @@ const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, si } #endif /* FT_HAVE_WCHAR */ +#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 + FT_INTERNAL void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end) @@ -156,11 +222,34 @@ void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const } #endif /* FT_HAVE_WCHAR */ +#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) +{ + const char *beg = utf8_n_substring_beg(str, ch_separator, n); + if (beg == NULL) { + *begin = NULL; + *end = NULL; + return; + } + + const char *en = utf8chr(beg, ch_separator); + if (en == NULL) { + en = (const char *)str + strlen(str); + } + + *begin = beg; + *end = en; + return; +} +#endif /* FT_HAVE_UTF8 */ + + FT_INTERNAL string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type type) { - size_t sz = (number_of_chars) * (type == CharBuf ? sizeof(char) : sizeof(wchar_t)); + size_t sz = (number_of_chars) * (type == CHAR_BUF ? sizeof(char) : sizeof(wchar_t)); string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t)); if (result == NULL) return NULL; @@ -172,10 +261,10 @@ string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type result->data_sz = sz; result->type = type; - if (sz && type == CharBuf) { + if (sz && type == CHAR_BUF) { result->str.cstr[0] = '\0'; #ifdef FT_HAVE_WCHAR - } else if (sz && type == WCharBuf) { + } else if (sz && type == W_CHAR_BUF) { result->str.wstr[0] = L'\0'; #endif /* FT_HAVE_WCHAR */ } @@ -202,14 +291,14 @@ string_buffer_t *copy_string_buffer(const string_buffer_t *buffer) if (result == NULL) return NULL; switch (buffer->type) { - case CharBuf: + case CHAR_BUF: if (FT_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) { destroy_string_buffer(result); return NULL; } break; #ifdef FT_HAVE_WCHAR - case WCharBuf: + case W_CHAR_BUF: if (FT_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) { destroy_string_buffer(result); return NULL; @@ -250,7 +339,7 @@ fort_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str) F_FREE(buffer->str.data); buffer->str.cstr = copy; - buffer->type = CharBuf; + buffer->type = CHAR_BUF; return FT_SUCCESS; } @@ -269,31 +358,86 @@ fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *s F_FREE(buffer->str.data); buffer->str.wstr = copy; - buffer->type = WCharBuf; + buffer->type = W_CHAR_BUF; return FT_SUCCESS; } #endif /* FT_HAVE_WCHAR */ +#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 */ FT_INTERNAL -size_t buffer_text_height(const string_buffer_t *buffer) +size_t buffer_text_visible_height(const string_buffer_t *buffer) { if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) { return 0; } - if (buffer->type == CharBuf) + if (buffer->type == CHAR_BUF) return 1 + strchr_count(buffer->str.cstr, '\n'); - else +#ifdef FT_HAVE_WCHAR + else if (buffer->type == W_CHAR_BUF) return 1 + wstrchr_count(buffer->str.wstr, L'\n'); +#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; } +FT_INTERNAL +size_t string_buffer_cod_width_capacity(const string_buffer_t *buffer) +{ + return string_buffer_width_capacity(buffer); +} FT_INTERNAL -size_t buffer_text_width(const string_buffer_t *buffer) +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); + char *tmp = F_MALLOC(sizeof(char) * (sz + 1)); + // @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 */ + +FT_INTERNAL +size_t buffer_text_visible_width(const string_buffer_t *buffer) { size_t max_length = 0; - if (buffer->type == CharBuf) { + if (buffer->type == CHAR_BUF) { size_t n = 0; while (1) { const char *beg = NULL; @@ -306,7 +450,7 @@ size_t buffer_text_width(const string_buffer_t *buffer) ++n; } #ifdef FT_HAVE_WCHAR - } else { + } else if (buffer->type == W_CHAR_BUF) { size_t n = 0; while (1) { const wchar_t *beg = NULL; @@ -323,149 +467,110 @@ size_t buffer_text_width(const string_buffer_t *buffer) ++n; } #endif /* FT_HAVE_WCHAR */ +#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 */ } return max_length; /* shouldn't be here */ } -FT_INTERNAL -int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag) +static void +buffer_substring(const string_buffer_t *buffer, size_t buffer_row, void **begin, void **end, ptrdiff_t *str_it_width) { -#define CHAR_TYPE char -#define NULL_CHAR '\0' -#define NEWLINE_CHAR '\n' -#define SPACE_CHAR " " -#define SNPRINTF_FMT_STR "%*s" -#define SNPRINTF snprintf -#define BUFFER_STR str.cstr -#define SNPRINT_N_STRINGS snprint_n_strings -#define STR_N_SUBSTRING str_n_substring -#define STR_ITER_WIDTH str_iter_width - - size_t buf_len = total_buf_len - strlen(content_style_tag) - strlen(reset_content_style_tag); - - if (buffer == NULL || buffer->str.data == NULL - || buffer_row >= buffer_text_height(buffer) || buf_len == 0) { - return -1; - } - - size_t content_width = buffer_text_width(buffer); - if ((buf_len - 1) < content_width) - return -1; - - size_t left = 0; - size_t right = 0; - - switch (get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_TEXT_ALIGN)) { - case FT_ALIGNED_LEFT: - left = 0; - right = (buf_len - 1) - content_width; + 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); break; - case FT_ALIGNED_CENTER: - left = ((buf_len - 1) - content_width) / 2; - right = ((buf_len - 1) - content_width) - left; - break; - case FT_ALIGNED_RIGHT: - left = (buf_len - 1) - content_width; - right = 0; - break; - default: - assert(0); - break; - } - - int set_old_value = 0; - size_t written = 0; - int tmp = 0; - ptrdiff_t str_it_width = 0; - const CHAR_TYPE *beg = NULL; - const CHAR_TYPE *end = NULL; - CHAR_TYPE old_value = (CHAR_TYPE)0; - - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, left, SPACE_CHAR)); - - STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); - if (beg == NULL || end == NULL) - return -1; - old_value = *end; - *(CHAR_TYPE *)end = NULL_CHAR; - set_old_value = 1; - - str_it_width = STR_ITER_WIDTH(beg, end); - if (str_it_width < 0 || content_width < (size_t)str_it_width) - goto clear; - - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, content_style_tag)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, total_buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, reset_content_style_tag)); - - *(CHAR_TYPE *)end = old_value; - set_old_value = 0; - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, right, SPACE_CHAR)); - return (int)written; - -clear: - if (set_old_value) - *(CHAR_TYPE *)end = old_value; - return -1; - -#undef CHAR_TYPE -#undef NULL_CHAR -#undef NEWLINE_CHAR -#undef SPACE_CHAR -#undef SNPRINTF_FMT_STR -#undef SNPRINTF -#undef BUFFER_STR -#undef SNPRINT_N_STRINGS -#undef STR_N_SUBSTRING -#undef STR_ITER_WIDTH -} - - #ifdef FT_HAVE_WCHAR -FT_INTERNAL -int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag) -{ -#define CHAR_TYPE wchar_t -#define NULL_CHAR L'\0' -#define NEWLINE_CHAR L'\n' -#define SPACE_CHAR " " -#define SNPRINTF_FMT_STR L"%*ls" -#define SNPRINTF swprintf -#define BUFFER_STR str.wstr -#define SNPRINT_N_STRINGS wsnprint_n_string -#define STR_N_SUBSTRING wstr_n_substring -#define STR_ITER_WIDTH wcs_iter_width + 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); + break; +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + utf8_n_substring(buffer->str.wstr, '\n', buffer_row, begin, end); + if ((*(const char **)begin) && (*(const char **)end)) + *str_it_width = ut8_width(*begin, *end); + break; +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + } +} - size_t buf_len = total_buf_len - strlen(content_style_tag) - strlen(reset_content_style_tag); + +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; + } +} + + +FT_INTERNAL +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) +{ + const context_t *context = cntx->cntx; + fort_table_properties_t *props = context->table_properties; + size_t row = context->row; + size_t column = context->column; if (buffer == NULL || buffer->str.data == NULL - || buffer_row >= buffer_text_height(buffer) || buf_len == 0) { + || buffer_row >= buffer_text_visible_height(buffer)) { return -1; } - size_t content_width = buffer_text_width(buffer); - if ((buf_len - 1) < content_width) + size_t content_width = buffer_text_visible_width(buffer); + if (vis_width < content_width) return -1; size_t left = 0; size_t right = 0; - - switch (get_cell_property_value_hierarcial(context->table_properties, context->row, context->column, FT_CPROP_TEXT_ALIGN)) { + switch (get_cell_property_value_hierarcial(props, row, column, FT_CPROP_TEXT_ALIGN)) { case FT_ALIGNED_LEFT: left = 0; - right = (buf_len - 1) - content_width; + right = (vis_width) - content_width; break; case FT_ALIGNED_CENTER: - left = ((buf_len - 1) - content_width) / 2; - right = ((buf_len - 1) - content_width) - left; + left = ((vis_width) - content_width) / 2; + right = ((vis_width) - content_width) - left; break; case FT_ALIGNED_RIGHT: - left = (buf_len - 1) - content_width; + left = (vis_width) - content_width; right = 0; break; default: @@ -473,64 +578,41 @@ int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, siz break; } - int set_old_value = 0; size_t written = 0; int tmp = 0; ptrdiff_t str_it_width = 0; - const CHAR_TYPE *beg = NULL; - const CHAR_TYPE *end = NULL; - CHAR_TYPE old_value = (CHAR_TYPE)0; - - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, left, SPACE_CHAR)); - - STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); + const void *beg = NULL; + const void *end = NULL; + buffer_substring(buffer, buffer_row, (void **)&beg, (void **)&end, &str_it_width); if (beg == NULL || end == NULL) return -1; - old_value = *end; - *(CHAR_TYPE *)end = NULL_CHAR; - set_old_value = 1; - - str_it_width = STR_ITER_WIDTH(beg, end); if (str_it_width < 0 || content_width < (size_t)str_it_width) - goto clear; + return -1; - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, content_style_tag)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, total_buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, 1, reset_content_style_tag)); + size_t padding = content_width - (size_t)str_it_width; - *(CHAR_TYPE *)end = old_value; - set_old_value = 0; - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); - CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, total_buf_len - written, right, SPACE_CHAR)); + 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)); return (int)written; clear: - if (set_old_value) - *(CHAR_TYPE *)end = old_value; return -1; - -#undef CHAR_TYPE -#undef NULL_CHAR -#undef NEWLINE_CHAR -#undef SPACE_CHAR -#undef SNPRINTF_FMT_STR -#undef SNPRINTF -#undef BUFFER_STR -#undef SNPRINT_N_STRINGS -#undef STR_N_SUBSTRING -#undef STR_ITER_WIDTH } -#endif /* FT_HAVE_WCHAR */ - FT_INTERNAL -size_t string_buffer_capacity(const string_buffer_t *buffer) +size_t string_buffer_width_capacity(const string_buffer_t *buffer) { assert(buffer); - if (buffer->type == CharBuf) + if (buffer->type == CHAR_BUF) return buffer->data_sz; - else + else if (buffer->type == W_CHAR_BUF) return buffer->data_sz / sizeof(wchar_t); + else if (buffer->type == UTF8_BUF) + return buffer->data_sz / 4; } diff --git a/src/string_buffer.h b/src/string_buffer.h index 764fd60..b55887b 100644 --- a/src/string_buffer.h +++ b/src/string_buffer.h @@ -7,17 +7,16 @@ /***************************************************************************** * STRING BUFFER * ***************************************************************************/ -enum str_buf_type { - CharBuf, -#ifdef FT_HAVE_WCHAR - WCharBuf -#endif /* FT_HAVE_WCHAR */ -}; struct string_buffer { union { char *cstr; +#ifdef FT_HAVE_WCHAR wchar_t *wstr; +#endif +#ifdef FT_HAVE_UTF8 + void *u8str; +#endif void *data; } str; size_t data_sz; @@ -44,26 +43,31 @@ FT_INTERNAL fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str); #endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 FT_INTERNAL -size_t buffer_text_height(const string_buffer_t *buffer); +fort_status_t fill_buffer_from_u8string(string_buffer_t *buffer, const void *str); +#endif /* FT_HAVE_UTF8 */ FT_INTERNAL -size_t string_buffer_capacity(const string_buffer_t *buffer); +size_t buffer_text_visible_width(const string_buffer_t *buffer); + +FT_INTERNAL +size_t buffer_text_visible_height(const string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_cod_width_capacity(const string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_raw_capacity(const string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_width_capacity(const string_buffer_t *buffer); FT_INTERNAL void *buffer_get_data(string_buffer_t *buffer); FT_INTERNAL -size_t buffer_text_width(const string_buffer_t *buffer); - -FT_INTERNAL -int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag); - -#ifdef FT_HAVE_WCHAR -FT_INTERNAL -int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t total_buf_len, - const context_t *context, const char *content_style_tag, const char *reset_content_style_tag); -#endif /* FT_HAVE_WCHAR */ +int buffer_printf(string_buffer_t *buffer, size_t buffer_row, conv_context_t *cntx, size_t cod_width, + const char *content_style_tag, const char *reset_content_style_tag); #endif /* STRING_BUFFER_H */ diff --git a/src/table.c b/src/table.c index 6e5d9ad..9519e11 100644 --- a/src/table.c +++ b/src/table.c @@ -294,3 +294,8 @@ fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *wi } +FT_INTERNAL +fort_status_t table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width) +{ + return table_geometry(table, height, width); +} diff --git a/src/table.h b/src/table.h index 0af96aa..f1c71f7 100644 --- a/src/table.h +++ b/src/table.h @@ -46,4 +46,11 @@ fort_status_t table_rows_and_cols_geometry(const ft_table_t *table, FT_INTERNAL fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width); +/* + * Returns geometry in codepoints(characters) (include codepoints of invisible + * elements: e.g. styles tags). + */ +FT_INTERNAL +fort_status_t table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width); + #endif /* TABLE_H */ diff --git a/tests/bb_tests/test_table_basic.c b/tests/bb_tests/test_table_basic.c index 00fd173..48bd7eb 100644 --- a/tests/bb_tests/test_table_basic.c +++ b/tests/bb_tests/test_table_basic.c @@ -125,12 +125,55 @@ void test_bug_fixes(void) ft_destroy_table(table); } #endif + +#ifdef FT_HAVE_UTF8 + SCENARIO("Issue 11 - https://github.com/seleznevae/libfort/issues/11 (utf-8 case)") { + ft_table_t *table = ft_create_table(); + ft_set_border_style(table, FT_PLAIN_STYLE); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + ft_u8write_ln(table, "1", "2"); + ft_u8write_ln(table, "3", "4"); + + const char *table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "-------\n" + " 1 2 \n" + "-------\n" + " 3 4 \n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* FT_HAVE_UTF8 */ } void test_table_basic(void) { ft_table_t *table = NULL; + WHEN("Empty table") { + table = ft_create_table(); + assert_true(table != NULL); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = ""; + assert_str_equal(table_str, table_str_etalon); +#ifdef FT_HAVE_WCHAR + const wchar_t *table_wstr = ft_to_wstring(table); + assert_true(table_wstr != NULL); + const wchar_t *table_wstr_etalon = L""; + assert_wcs_equal(table_wstr, table_wstr_etalon); +#endif +#ifdef FT_HAVE_UTF8 + table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + assert_str_equal(table_str, table_str_etalon); +#endif /* FT_HAVE_UTF8 */ + ft_destroy_table(table); + } + WHEN("All columns are equal and not empty") { table = ft_create_table(); assert_true(table != NULL); @@ -193,6 +236,39 @@ void test_table_basic(void) } #endif +#ifdef FT_HAVE_UTF8 + WHEN("All columns are equal and not empty (utf8 strings)") { + table = ft_create_table(); + assert_true(table != NULL); + assert_true(set_test_props_for_table(table) == FT_SUCCESS); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + assert_true(ft_u8write_ln(table, "3", "c", "234", "3.140000") == FT_SUCCESS); + assert_true(ft_u8write_ln(table, "3", "c", "234", "3.140000") == FT_SUCCESS); + assert_true(ft_u8write_ln(table, "3", "c", "234", "3.140000") == FT_SUCCESS); + + const char *table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + + const char *table_str_etalon = + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | |\n" + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | |\n" + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | |\n" + "+---+---+-----+----------+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* FT_HAVE_UTF8 */ + WHEN("All columns are not equal and not empty") { table = ft_create_table(); assert_true(table != NULL); @@ -255,6 +331,38 @@ void test_table_basic(void) } #endif +#ifdef FT_HAVE_UTF8 + WHEN("All columns are not equal and not empty") { + table = ft_create_table(); + assert_true(table != NULL); + assert_true(set_test_props_for_table(table) == FT_SUCCESS); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + assert_true(ft_u8write_ln(table, "3", "c", "234", "3.140000") == FT_SUCCESS); + assert_true(ft_u8write_ln(table, "c", "234", "3.140000", "3") == FT_SUCCESS); + assert_true(ft_u8write_ln(table, "234", "3.140000", "3", "c") == FT_SUCCESS); + + const char *table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+-----+----------+----------+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | |\n" + "+-----+----------+----------+----------+\n" + "| | | | |\n" + "| c | 234 | 3.140000 | 3 |\n" + "| | | | |\n" + "+-----+----------+----------+----------+\n" + "| | | | |\n" + "| 234 | 3.140000 | 3 | c |\n" + "| | | | |\n" + "+-----+----------+----------+----------+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* FT_HAVE_UTF8 */ + WHEN("All columns are not equal and some cells are empty") { table = ft_create_table(); assert_true(table != NULL); @@ -317,6 +425,38 @@ void test_table_basic(void) } #endif +#ifdef FT_HAVE_UTF8 + WHEN("All columns are not equal and some cells are empty (utf-8 strings)") { + table = ft_create_table(); + assert_true(table != NULL); + assert_true(set_test_props_for_table(table) == FT_SUCCESS); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + assert_true(ft_u8write_ln(table, "", "", "234", "3.140000") == FT_SUCCESS); + assert_true(ft_u8write_ln(table, "c", "234", "3.140000", "") == FT_SUCCESS); + assert_true(ft_u8write_ln(table, "234", "3.140000", "", "") == FT_SUCCESS); + + const char *table_str = ft_to_u8string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+-----+----------+----------+----------+\n" + "| | | | |\n" + "| | | 234 | 3.140000 |\n" + "| | | | |\n" + "+-----+----------+----------+----------+\n" + "| | | | |\n" + "| c | 234 | 3.140000 | |\n" + "| | | | |\n" + "+-----+----------+----------+----------+\n" + "| | | | |\n" + "| 234 | 3.140000 | | |\n" + "| | | | |\n" + "+-----+----------+----------+----------+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } +#endif /* FT_HAVE_UTF8 */ + WHEN("All cells are empty") { table = ft_create_table(); assert_true(table != NULL); @@ -378,6 +518,40 @@ void test_table_basic(void) ft_destroy_table(table); } #endif + + WHEN("Multiline conten") { + table = ft_create_table(); + assert_true(table != NULL); + assert_true(set_test_props_for_table(table) == FT_SUCCESS); + + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + assert_true(ft_write_ln(table, "3", "c", "234\n2", "3.140000") == FT_SUCCESS); + assert_true(ft_write_ln(table, "3", "c", "234", "3.140000\n123") == FT_SUCCESS); + assert_true(ft_write_ln(table, "3", "c", "234", "x") == FT_SUCCESS); + + ft_set_cell_prop(table, FT_ANY_ROW, 3, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_RIGHT); + + const char *table_str = ft_to_string(table); + assert_true(table_str != NULL); + const char *table_str_etalon = + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | 2 | |\n" + "| | | | |\n" + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | 123 |\n" /* todo: Fix strange alignment for multiline cells */ + "| | | | |\n" + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | x |\n" + "| | | | |\n" + "+---+---+-----+----------+\n"; + assert_str_equal(table_str, table_str_etalon); + ft_destroy_table(table); + } } @@ -419,6 +593,138 @@ void test_wcs_table_boundaries(void) } #endif +#ifdef FT_HAVE_UTF8 +void test_utf8_table(void) +{ + ft_table_t *table = NULL; + +#define TEST_UTF8_SIMPLE(content) \ + { \ + table = ft_create_table(); \ + assert_true(table != NULL); \ + assert(ft_set_border_style(table, FT_EMPTY_STYLE) == 0); \ + assert_true(ft_u8write_ln(table, content) == FT_SUCCESS); \ + const char *table_str = ft_to_u8string(table); \ + assert_true(table_str != NULL); \ + char table_str_etalon[1024] = {'\0'}; \ + snprintf(table_str_etalon, 1024," %s \n", content); \ + assert_str_equal(table_str, table_str_etalon); \ + ft_destroy_table(table); \ + } + TEST_UTF8_SIMPLE(""); + TEST_UTF8_SIMPLE("1"); + TEST_UTF8_SIMPLE("foo"); + TEST_UTF8_SIMPLE("1234567890"); + TEST_UTF8_SIMPLE("Xylophmsik"); + TEST_UTF8_SIMPLE("ψημένηζειθ"); + TEST_UTF8_SIMPLE("D’ḟuascail"); + TEST_UTF8_SIMPLE("Pójdźżełąć"); + TEST_UTF8_SIMPLE("«braçõeshá"); + TEST_UTF8_SIMPLE("французких"); + TEST_UTF8_SIMPLE("Benjamínúñ"); + TEST_UTF8_SIMPLE("görmüştüçğ"); + TEST_UTF8_SIMPLE("視野無限廣窗外有藍天"); + TEST_UTF8_SIMPLE("いろはにほへとちりぬ"); + TEST_UTF8_SIMPLE("𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼"); +#undef TEST_UTF8_SIMPLE + +#define TEST_UTF8_SIMPLE_STYLE(content) \ + { \ + table = ft_create_table(); \ + assert_true(table != NULL); \ + assert(ft_set_border_style(table, FT_EMPTY_STYLE) == 0); \ + assert_true(ft_u8write_ln(table, content) == FT_SUCCESS); \ + assert(ft_set_cell_prop(table, 0, 0, FT_CPROP_CONT_FG_COLOR, FT_COLOR_YELLOW) == FT_SUCCESS); \ + assert(ft_set_cell_prop(table, 0, 0, FT_CPROP_CELL_BG_COLOR, FT_COLOR_RED) == FT_SUCCESS); \ + assert(ft_set_cell_prop(table, 0, 0, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_UNDERLINED) == FT_SUCCESS); \ + const char *table_str = ft_to_u8string(table); \ + assert_true(table_str != NULL); \ + char table_str_etalon[1024] = {'\0'}; \ + snprintf(table_str_etalon, 1024, \ + "\033[41m \033[4m\033[33m%s\033[0m\033[41m \033[0m\n", content); \ + assert_str_equal(table_str, table_str_etalon); \ + ft_destroy_table(table); \ + } + TEST_UTF8_SIMPLE_STYLE("1234567890"); + TEST_UTF8_SIMPLE_STYLE("Xylophmsik"); + TEST_UTF8_SIMPLE_STYLE("ψημένηζειθ"); + TEST_UTF8_SIMPLE_STYLE("D’ḟuascail"); + TEST_UTF8_SIMPLE_STYLE("Pójdźżełąć"); + TEST_UTF8_SIMPLE_STYLE("«braçõeshá"); + TEST_UTF8_SIMPLE_STYLE("французких"); + TEST_UTF8_SIMPLE_STYLE("Benjamínúñ"); + TEST_UTF8_SIMPLE_STYLE("görmüştüçğ"); + TEST_UTF8_SIMPLE_STYLE("視野無限廣窗外有藍天"); + TEST_UTF8_SIMPLE_STYLE("いろはにほへとちりぬ"); + TEST_UTF8_SIMPLE_STYLE("𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼"); +#undef TEST_UTF8_SIMPLE_STYLE + +#define TEST_UTF8_BORDER(content) \ + { \ + table = ft_create_table(); \ + assert_true(table != NULL); \ + assert(ft_set_border_style(table, FT_BASIC_STYLE) == 0); \ + assert_true(ft_u8write_ln(table, content) == FT_SUCCESS); \ + const char *table_str = ft_to_u8string(table); \ + assert_true(table_str != NULL); \ + char table_str_etalon[1024] = {'\0'}; \ + snprintf(table_str_etalon, 1024, \ + "+------------+\n" \ + "| %s |\n" \ + "+------------+\n", content); \ + assert_str_equal(table_str, table_str_etalon); \ + ft_destroy_table(table); \ + } + + TEST_UTF8_BORDER("1234567890"); + TEST_UTF8_BORDER("Xylophmsik"); + TEST_UTF8_BORDER("ψημένηζειθ"); + TEST_UTF8_BORDER("D’ḟuascail"); + TEST_UTF8_BORDER("Pójdźżełąć"); + TEST_UTF8_BORDER("«braçõeshá"); + TEST_UTF8_BORDER("французких"); + TEST_UTF8_BORDER("Benjamínúñ"); + TEST_UTF8_BORDER("görmüştüçğ"); + TEST_UTF8_BORDER("視野無限廣窗外有藍天"); + TEST_UTF8_BORDER("いろはにほへとちりぬ"); + TEST_UTF8_BORDER("𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼"); +#undef TEST_UTF8_BORDER + +#define TEST_UTF8_STYLE(content) \ + { \ + table = ft_create_table(); \ + assert_true(table != NULL); \ + assert(ft_set_border_style(table, FT_BASIC_STYLE) == 0); \ + assert_true(ft_u8write_ln(table, content) == FT_SUCCESS); \ + assert(ft_set_cell_prop(table, 0, 0, FT_CPROP_CONT_FG_COLOR, FT_COLOR_YELLOW) == FT_SUCCESS); \ + assert(ft_set_cell_prop(table, 0, 0, FT_CPROP_CELL_BG_COLOR, FT_COLOR_RED) == FT_SUCCESS); \ + assert(ft_set_cell_prop(table, 0, 0, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_UNDERLINED) == FT_SUCCESS); \ + const char *table_str = ft_to_u8string(table); \ + assert_true(table_str != NULL); \ + char table_str_etalon[1024] = {'\0'}; \ + snprintf(table_str_etalon, 1024, \ + "+------------+\n" \ + "|\033[41m \033[4m\033[33m%s\033[0m\033[41m \033[0m|\n" \ + "+------------+\n", content); \ + assert_str_equal(table_str, table_str_etalon); \ + ft_destroy_table(table); \ + } + TEST_UTF8_STYLE("1234567890"); + TEST_UTF8_STYLE("Xylophmsik"); + TEST_UTF8_STYLE("ψημένηζειθ"); + TEST_UTF8_STYLE("D’ḟuascail"); + TEST_UTF8_STYLE("Pójdźżełąć"); + TEST_UTF8_STYLE("«braçõeshá"); + TEST_UTF8_STYLE("французких"); + TEST_UTF8_STYLE("Benjamínúñ"); + TEST_UTF8_STYLE("görmüştüçğ"); + TEST_UTF8_STYLE("視野無限廣窗外有藍天"); + TEST_UTF8_STYLE("いろはにほへとちりぬ"); + TEST_UTF8_STYLE("𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼"); +#undef TEST_UTF8_STYLE + +} +#endif /* FT_HAVE_UTF8 */ void test_table_write(void) { diff --git a/tests/main_test.c b/tests/main_test.c index a59c96e..b4ae459 100644 --- a/tests/main_test.c +++ b/tests/main_test.c @@ -22,6 +22,9 @@ void test_table_cell_properties(void); void test_table_text_styles(void); void test_table_tbl_properties(void); void test_memory_errors(void); +#ifdef FT_HAVE_UTF8 +void test_utf8_table(void); +#endif #ifdef FORT_WB_TESTING_ENABLED @@ -41,6 +44,9 @@ struct test_case bb_test_suite [] = { {"test_table_basic", test_table_basic}, #ifdef FT_HAVE_WCHAR {"test_wcs_table_boundaries", test_wcs_table_boundaries}, +#endif +#ifdef FT_HAVE_UTF8 + {"test_utf8_table", test_utf8_table}, #endif {"test_table_write", test_table_write}, {"test_table_changing_cell", test_table_changing_cell}, diff --git a/tests/wb_tests/test_string_buffer.c b/tests/wb_tests/test_string_buffer.c index 792f808..83b6aa8 100644 --- a/tests/wb_tests/test_string_buffer.c +++ b/tests/wb_tests/test_string_buffer.c @@ -2,39 +2,53 @@ #include "string_buffer.h" #include "wcwidth.h" #include - +#if defined(FT_HAVE_UTF8) +#include "utf8.h" +#endif size_t strchr_count(const char *str, char ch); size_t wstrchr_count(const wchar_t *str, wchar_t ch); +#if defined(FT_HAVE_UTF8) +size_t utf8chr_count(const void *str, utf8_int32_t ch); +#endif + const char *str_n_substring_beg(const char *str, char ch_separator, size_t n); const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, size_t n); +#ifdef FT_HAVE_UTF8 +const void *utf8_n_substring_beg(const void *str, utf8_int32_t ch_separator, size_t n); +#endif fort_status_t str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end); void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const wchar_t **begin, const wchar_t **end); +#ifdef FT_HAVE_UTF8 +void utf8_n_substring(const void *str, utf8_int32_t ch_separator, size_t n, const void **begin, const void **end); +#endif -//size_t buffer_text_width(string_buffer_t *buffer); void test_strchr_count(void); void test_str_n_substring(void); -void test_buffer_text_width(void); -void test_buffer_text_height(void); +void test_buffer_text_visible_width(void); +void test_buffer_text_visible_height(void); #if defined(FT_HAVE_WCHAR) void test_wchar_basics(void); #endif +void test_print_n_strings(void); + void test_string_buffer(void) { test_strchr_count(); test_str_n_substring(); - test_buffer_text_width(); - test_buffer_text_height(); + test_buffer_text_visible_width(); + test_buffer_text_visible_height(); #if defined(FT_HAVE_WCHAR) test_wchar_basics(); #endif + test_print_n_strings(); } @@ -70,8 +84,60 @@ void test_strchr_count(void) assert_true(wstrchr_count(L"\n123123\n123123\n\n\n123", L'\n') == 5); assert_true(wstrchr_count(L"1\xffffy23123\xffffy123123\xffffy\xffffy\xffffy123", L'\xffff') == 5); + + +#ifdef FT_HAVE_UTF8 + assert_true(utf8chr_count(NULL, '\n') == 0); + assert_true(utf8chr_count("", '\n') == 0); + assert_true(utf8chr_count("asbd", '\n') == 0); + + assert_true(utf8chr_count("Chinese L視野無限廣,窗外有藍天", '\n') == 0); + assert_true(utf8chr_count("Hindi ऋषियों को सताने वाले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।", '\n') == 0); + assert_true(utf8chr_count("Portuguese Luís argüia à Júlia que «brações, fé, chá, óxido, pôr, zângão» eram palavras do português.", '\n') == 0); + assert_true(utf8chr_count("Russian В чащах юга жил бы цитрус? Да, но фальшивый экземпляръ!", '\n') == 0); + assert_true(utf8chr_count("Spanish La niña, viéndose atrapada en el áspero baúl índigo y sintiendo asfixia, lloró de vergüenza; mientras que la frustrada madre llamaba a su hija diciendo: ¿Dónde estás Waleska?", '\n') == 0); + + assert_true(utf8chr_count("asbd\n", '\n') == 1); + assert_true(utf8chr_count("\nasbd", '\n') == 1); + assert_true(utf8chr_count("a\nsbd", '\n') == 1); + assert_true(utf8chr_count("Chinese L視野無限\n廣,窗外有藍天", '\n') == 1); + assert_true(utf8chr_count("Hindi ऋषियों को सताने वा\nले दुष्ट राक्षसों के राजा रावण का सर्वनाश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।", '\n') == 1); + assert_true(utf8chr_count("Portuguese Luís argüi\na à Júlia que «brações, fé, chá, óxido, pôr, zângão» eram palavras do português.", '\n') == 1); + assert_true(utf8chr_count("Russian В чащах \nюга жил бы цитрус? Да, но фальшивый экземпляръ!", '\n') == 1); + assert_true(utf8chr_count("Spanish La niña, vié\nndose atrapada en el áspero baúl índigo y sintiendo asfixia, lloró de vergüenza; mientras que la frustrada madre llamaba a su hija diciendo: ¿Dónde estás Waleska?", '\n') == 1); + + assert_true(utf8chr_count("\n12\n123", '\n') == 2); + assert_true(utf8chr_count("\n12\n123\n", '\n') == 3); + assert_true(utf8chr_count("\n\n\n", '\n') == 3); + assert_true(utf8chr_count("\n123123\n123123\n\n\n123", '\n') == 5); + assert_true(utf8chr_count("Chinese L視野無限\n廣,窗外有\n藍天", '\n') == 2); + assert_true(utf8chr_count("Hindi ऋषियों को सताने वा\nले दुष्ट राक्षसों के राजा\n रावण का सर्वना\nश करने वाले विष्णुवतार भगवान श्रीराम, अयोध्या के महाराज दशरथ के बड़े सपुत्र थे।", '\n') == 3); + assert_true(utf8chr_count("Portuguese Luís argüi\na à Júlia que «brações, fé, chá, óxido, \npôr, zângão» eram pal\navras do portu\nguês.", '\n') == 4); + assert_true(utf8chr_count("Russian В чащах \nюга жил бы ц\nитрус? Да, но фальшивый экземпляръ!", '\n') == 2); + assert_true(utf8chr_count("Spanish La niña, vié\nndose atrapada en el \n\n\náspero baúl índigo y \nsintiendo asfixia, lloró de vergüenza; mientras que la frustrada madre llamaba a su hija diciendo: ¿Dónde estás Waleska?", '\n') == 5); + + assert_true(utf8chr_count("1a23123a123123aaa123", 'a') == 5); +#endif } +void assert_str_equal_strong(const char *str1, const char *str2, + const char *file, + int line, + const char *function) +{ + if (!str1 && !str2) + return; + + if ((str1 && !str2) || (!str1 && str2)) { + fprintf(stderr, "%s:%d(%s):Abort! Not equals strings:\n", file, line, function); + exit(EXIT_FAILURE); + } else if (strcmp(str1, str2) != 0) { + fprintf(stderr, "%s:%d(%s):Abort! Not equals strings:\n", file, line, function); + exit(EXIT_FAILURE); + } +} + +#define ASSERT_STR_EQUAL_STRONG(str1, str2) assert_str_equal_strong(str1, str2, __FILE__,__LINE__, __func__) void test_str_n_substring(void) { @@ -86,6 +152,13 @@ void test_str_n_substring(void) assert_true(wstr_n_substring_beg(empty_wstr, L'\n', 1) == NULL); assert_true(wstr_n_substring_beg(empty_wstr, L'\n', 2) == NULL); #endif +#ifdef FT_HAVE_UTF8 + const char *utf8_empty_str = ""; + assert_true(utf8_n_substring_beg(utf8_empty_str, '\n', 0) == utf8_empty_str); + assert_true(utf8_n_substring_beg(utf8_empty_str, '\n', 1) == NULL); + assert_true(utf8_n_substring_beg(utf8_empty_str, '\n', 2) == NULL); +#endif + const char *str = "123\n5678\n9"; assert_true(str_n_substring_beg(NULL, '\n', 0) == NULL); @@ -106,6 +179,16 @@ void test_str_n_substring(void) assert_true(wstr_n_substring_beg(wstr, L'\n', 2) == wstr + 9); assert_true(wstr_n_substring_beg(wstr, L'\n', 3) == NULL); #endif +#ifdef FT_HAVE_UTF8 + const char *utf8_str = "123\n5678\n9"; + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(NULL, '\n', 0), NULL); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str, '\n', 0), utf8_str); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str, '1', 0), utf8_str); + + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str, '\n', 1), "5678\n9"); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str, '\n', 2), "9"); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str, '\n', 3), NULL); +#endif const char *str2 = "\n123\n56\n\n9\n"; assert_true(str_n_substring_beg(str2, '\n', 0) == str2); @@ -126,6 +209,16 @@ void test_str_n_substring(void) assert_true(wstr_n_substring_beg(wstr2, L'\xff0f', 5) == wstr2 + 11); assert_true(wstr_n_substring_beg(wstr2, L'\xff0f', 6) == NULL); #endif +#ifdef FT_HAVE_UTF8 + const char *utf8_str2 = "\n123\n56\n\n9\n"; + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 0), utf8_str2); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 1), "123\n56\n\n9\n"); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 2), "56\n\n9\n"); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 3), "\n9\n"); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 4), "9\n"); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 5), ""); + ASSERT_STR_EQUAL_STRONG(utf8_n_substring_beg(utf8_str2, '\n', 6), NULL); +#endif const char *beg = NULL; const char *end = NULL; @@ -146,6 +239,16 @@ void test_str_n_substring(void) wstr_n_substring(empty_wstr, L'\n', 2, &wbeg, &wend); assert_true(wbeg == NULL && wend == NULL); #endif +#ifdef FT_HAVE_UTF8 + const void *utf8_beg = NULL; + const void *utf8_end = NULL; + utf8_n_substring(utf8_empty_str, '\n', 0, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_empty_str && utf8_end == utf8_empty_str + strlen(utf8_empty_str)); + utf8_n_substring(utf8_empty_str, '\n', 1, &utf8_beg, &utf8_end); + assert_true(utf8_beg == NULL && utf8_end == NULL); + utf8_n_substring(utf8_empty_str, '\n', 2, &utf8_beg, &utf8_end); + assert_true(utf8_beg == NULL && utf8_end == NULL); +#endif str_n_substring(NULL, '\n', 0, &beg, &end); @@ -163,6 +266,14 @@ void test_str_n_substring(void) wstr_n_substring(wstr, L'2', 0, &wbeg, &wend); assert_true(wbeg == wstr && wend == wstr + 1); #endif +#ifdef FT_HAVE_UTF8 + utf8_n_substring(NULL, '\n', 0, &utf8_beg, &utf8_end); + assert_true(utf8_beg == NULL && utf8_end == NULL); + utf8_n_substring(utf8_str, '\n', 0, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str && utf8_end == utf8_str + 3); + utf8_n_substring(utf8_str, '2', 0, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str && utf8_end == utf8_str + 1); +#endif str_n_substring(str, '\n', 1, &beg, &end); assert_true(beg == str + 4 && end == str + 8); @@ -179,6 +290,14 @@ void test_str_n_substring(void) wstr_n_substring(wstr, L'\n', 3, &wbeg, &wend); assert_true(wbeg == NULL && wend == NULL); #endif +#ifdef FT_HAVE_UTF8 + utf8_n_substring(utf8_str, '\n', 1, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str + 4 && utf8_end == utf8_str + 8); + utf8_n_substring(utf8_str, '\n', 2, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str + 9 && utf8_end == utf8_str + strlen(utf8_str)); + utf8_n_substring(utf8_str, '\n', 3, &utf8_beg, &utf8_end); + assert_true(utf8_beg == NULL && end == NULL); +#endif str_n_substring(str2, '\n', 0, &beg, &end); assert_true(beg == str2 && end == str2); @@ -211,113 +330,226 @@ void test_str_n_substring(void) wstr_n_substring(wstr2, L'\xff0f', 6, &wbeg, &wend); assert_true(wbeg == NULL && wend == NULL); #endif +#if defined(FT_HAVE_WCHAR) + utf8_n_substring(utf8_str2, '\n', 0, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str2 && utf8_end == utf8_str2); + utf8_n_substring(utf8_str2, '\n', 1, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str2 + 1 && utf8_end == utf8_str2 + 4); + utf8_n_substring(utf8_str2, '\n', 2, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str2 + 5 && utf8_end == utf8_str2 + 7); + utf8_n_substring(utf8_str2, '\n', 3, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str2 + 8 && utf8_end == utf8_str2 + 8); + utf8_n_substring(utf8_str2, '\n', 4, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str2 + 9 && utf8_end == utf8_str2 + 10); + utf8_n_substring(utf8_str2, '\n', 5, &utf8_beg, &utf8_end); + assert_true(utf8_beg == utf8_str2 + 11 && utf8_end == utf8_str2 + 11); + utf8_n_substring(utf8_str2, '\n', 6, &utf8_beg, &utf8_end); + assert_true(utf8_beg == NULL && utf8_end == NULL); +#endif } -void test_buffer_text_width(void) +void test_buffer_text_visible_width(void) { - string_buffer_t *buffer = create_string_buffer(200, CharBuf); - buffer->type = CharBuf; + string_buffer_t *buffer = create_string_buffer(200, CHAR_BUF); + buffer->type = CHAR_BUF; char *old_value = buffer->str.cstr; buffer->str.cstr = (char *)""; - assert_true(buffer_text_width(buffer) == 0); + assert_true(buffer_text_visible_width(buffer) == 0); buffer->str.cstr = (char *)"\n\n\n\n"; - assert_true(buffer_text_width(buffer) == 0); + assert_true(buffer_text_visible_width(buffer) == 0); buffer->str.cstr = (char *)"12345"; - assert_true(buffer_text_width(buffer) == 5); + assert_true(buffer_text_visible_width(buffer) == 5); buffer->str.cstr = (char *)"12345\n1234567"; - assert_true(buffer_text_width(buffer) == 7); + assert_true(buffer_text_visible_width(buffer) == 7); buffer->str.cstr = (char *)"12345\n1234567\n"; - assert_true(buffer_text_width(buffer) == 7); + assert_true(buffer_text_visible_width(buffer) == 7); buffer->str.cstr = (char *)"12345\n1234567\n123"; - assert_true(buffer_text_width(buffer) == 7); + assert_true(buffer_text_visible_width(buffer) == 7); #if defined(FT_HAVE_WCHAR) - buffer->type = WCharBuf; + buffer->type = W_CHAR_BUF; buffer->str.wstr = (wchar_t *)L""; - assert_true(buffer_text_width(buffer) == 0); + assert_true(buffer_text_visible_width(buffer) == 0); buffer->str.wstr = (wchar_t *)L"\n\n\n\n"; - assert_true(buffer_text_width(buffer) == 0); + assert_true(buffer_text_visible_width(buffer) == 0); buffer->str.wstr = (wchar_t *)L"12345"; - assert_true(buffer_text_width(buffer) == 5); + assert_true(buffer_text_visible_width(buffer) == 5); buffer->str.wstr = (wchar_t *)L"12345\n1234567"; - assert_true(buffer_text_width(buffer) == 7); + assert_true(buffer_text_visible_width(buffer) == 7); buffer->str.wstr = (wchar_t *)L"12345\n1234567\n"; - assert_true(buffer_text_width(buffer) == 7); + assert_true(buffer_text_visible_width(buffer) == 7); buffer->str.wstr = (wchar_t *)L"12345\n1234567\n123"; - assert_true(buffer_text_width(buffer) == 7); + assert_true(buffer_text_visible_width(buffer) == 7); #endif - buffer->type = CharBuf; +#if defined(FT_HAVE_UTF8) + buffer->type = UTF8_BUF; + + buffer->str.u8str = (void *)""; + assert_true(buffer_text_visible_width(buffer) == 0); + + buffer->str.u8str = (void *)"\n\n\n\n"; + assert_true(buffer_text_visible_width(buffer) == 0); + + buffer->str.u8str = (void *)"12345"; + assert_true(buffer_text_visible_width(buffer) == 5); + + buffer->str.u8str = (void *)"12345\n1234567"; + assert_true(buffer_text_visible_width(buffer) == 7); + + buffer->str.u8str = (void *)"12345\n1234567\n"; + assert_true(buffer_text_visible_width(buffer) == 7); + + buffer->str.u8str = (void *)"12345\n1234567\n123"; + assert_true(buffer_text_visible_width(buffer) == 7); + + + /* panagrams from http://clagnut.com/blog/2380/ */ + /* 10 20 30 40 50 60 70 80 90 100 110 */ + buffer->str.u8str = (void *)"Numbers 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + assert_true(buffer_text_visible_width(buffer) == 110); + buffer->str.u8str = (void *)"Chinese 視野無限廣, 窗外有藍天"; + assert_true(buffer_text_visible_width(buffer) == 22); + buffer->str.u8str = (void *)"German Falsches Üben von Xylophonmusik quält jeden größeren Zwerg"; + assert_true(buffer_text_visible_width(buffer) == 68); + buffer->str.u8str = (void *)"Greek Ταχίστη αλώπηξ βαφής ψημένη γη, δρασκελίζει υπέρ νωθρού κυνός Takhístè"; + assert_true(buffer_text_visible_width(buffer) == 80); + buffer->str.u8str = (void *)"Irish D’ḟuascail Íosa Úrṁac na hÓiġe Beannaiṫe pór Éaḃa agus Áḋaiṁ"; + assert_true(buffer_text_visible_width(buffer) == 70); + buffer->str.u8str = (void *)"Japanese いろはにほへと ちりぬるを わかよたれそ つねならむ うゐ"; + assert_true(buffer_text_visible_width(buffer) == 39); + buffer->str.u8str = (void *)"Polish Pójdźże, kiń tę chmurność w głąb flaszy"; + assert_true(buffer_text_visible_width(buffer) == 49); + buffer->str.u8str = (void *)"Portuguese Luís argüia à Júlia que «brações, fé, chá, óxido, pôr, zângão» eram palavras do português"; + assert_true(buffer_text_visible_width(buffer) == 100); + buffer->str.u8str = (void *)"Russian Съешь же ещё этих мягких французских булок, да выпей чаю"; + assert_true(buffer_text_visible_width(buffer) == 66); + buffer->str.u8str = (void *)"Spanish Benjamín pidió una bebida de kiwi y fresa; Noé, sin vergüenza, la más exquisita champaña del menú"; + assert_true(buffer_text_visible_width(buffer) == 107); + buffer->str.u8str = (void *)"Turkish Vakfın çoğu bu huysuz genci plajda görmüştü"; + assert_true(buffer_text_visible_width(buffer) == 53); + + /* 10 20 30 40 50 60 70 80 90 100 110 */ + buffer->str.u8str = (void *)"Numbers 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + assert_true(buffer_text_visible_width(buffer) == 110); + buffer->str.u8str = (void *)"Chinese 視野無限廣,\n 窗外有藍天"; + assert_true(buffer_text_visible_width(buffer) == 16); + buffer->str.u8str = (void *)"German Falsches Üben von Xy\nlophonmusik quält \njeden größeren Zwerg"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Greek Ταχίστη αλώπηξ βαφής\n ψημένη γη, δρασκελίζει\n υπέρ νωθρού \nκυνός Takhístè"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Irish D’ḟuascail Íosa Úrṁa\nc na hÓiġe Beannaiṫe\n pór Éaḃa agus Áḋaiṁ"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Japanese いろはにほへと ちり\nぬるを わかよたれそ つねならむ うゐ"; + assert_true(buffer_text_visible_width(buffer) == 20); + buffer->str.u8str = (void *)"Polish Pójdźże, kiń tę chmu\nrność w głąb flaszy"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Portuguese Luís argüia à Júlia\n que «brações, fé, chá,\n óxido, pôr, \nzângão» eram palavras\n do português"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Russian Съешь же ещё этих мя\nгких французских булок,\n да выпей чаю"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Spanish Benjamín pidió una b\nebida de kiwi y fresa;\n Noé, sin vergüenza,\n la más exquisita\n champaña del menú"; + assert_true(buffer_text_visible_width(buffer) == 30); + buffer->str.u8str = (void *)"Turkish Vakfın çoğu bu huysu\nz genci plajda gö\nrmüştü"; + assert_true(buffer_text_visible_width(buffer) == 30); + + +#endif + + buffer->type = CHAR_BUF; buffer->str.cstr = old_value; destroy_string_buffer(buffer); } -void test_buffer_text_height(void) +void test_buffer_text_visible_height(void) { - string_buffer_t *buffer = create_string_buffer(200, CharBuf); - buffer->type = CharBuf; + string_buffer_t *buffer = create_string_buffer(200, CHAR_BUF); + buffer->type = CHAR_BUF; char *old_value = buffer->str.cstr; buffer->str.cstr = (char *)""; - assert_true(buffer_text_height(buffer) == 0); + assert_true(buffer_text_visible_height(buffer) == 0); buffer->str.cstr = (char *)"\n"; - assert_true(buffer_text_height(buffer) == 2); + assert_true(buffer_text_visible_height(buffer) == 2); buffer->str.cstr = (char *)"\n\n"; - assert_true(buffer_text_height(buffer) == 3); + assert_true(buffer_text_visible_height(buffer) == 3); buffer->str.cstr = (char *)"\n\n\n\n"; - assert_true(buffer_text_height(buffer) == 5); + assert_true(buffer_text_visible_height(buffer) == 5); buffer->str.cstr = (char *)"12345"; - assert_true(buffer_text_height(buffer) == 1); + assert_true(buffer_text_visible_height(buffer) == 1); buffer->str.cstr = (char *)"\n12345"; - assert_true(buffer_text_height(buffer) == 2); + assert_true(buffer_text_visible_height(buffer) == 2); buffer->str.cstr = (char *)"\n12345\n\n2"; - assert_true(buffer_text_height(buffer) == 4); + assert_true(buffer_text_visible_height(buffer) == 4); #if defined(FT_HAVE_WCHAR) - buffer->type = WCharBuf; + buffer->type = W_CHAR_BUF; buffer->str.wstr = (wchar_t *)L""; - assert_true(buffer_text_height(buffer) == 0); + assert_true(buffer_text_visible_height(buffer) == 0); buffer->str.wstr = (wchar_t *)L"\n"; - assert_true(buffer_text_height(buffer) == 2); + assert_true(buffer_text_visible_height(buffer) == 2); buffer->str.wstr = (wchar_t *)L"\n\n"; - assert_true(buffer_text_height(buffer) == 3); + assert_true(buffer_text_visible_height(buffer) == 3); buffer->str.wstr = (wchar_t *)L"\n\n\n\n"; - assert_true(buffer_text_height(buffer) == 5); + assert_true(buffer_text_visible_height(buffer) == 5); buffer->str.wstr = (wchar_t *)L"\xff0fy2345\xff0f"; - assert_true(buffer_text_height(buffer) == 1); + assert_true(buffer_text_visible_height(buffer) == 1); buffer->str.wstr = (wchar_t *)L"\n12345"; - assert_true(buffer_text_height(buffer) == 2); + assert_true(buffer_text_visible_height(buffer) == 2); buffer->str.wstr = (wchar_t *)L"\n12345\n\n2"; - assert_true(buffer_text_height(buffer) == 4); + assert_true(buffer_text_visible_height(buffer) == 4); +#endif +#if defined(FT_HAVE_UTF8) + buffer->type = UTF8_BUF; + buffer->str.u8str = (void *)""; + assert_true(buffer_text_visible_height(buffer) == 0); + + buffer->str.u8str = (void *)"\n"; + assert_true(buffer_text_visible_height(buffer) == 2); + + buffer->str.u8str = (void *)"\n\n"; + assert_true(buffer_text_visible_height(buffer) == 3); + + buffer->str.u8str = (void *)"\n\n\n\n"; + assert_true(buffer_text_visible_height(buffer) == 5); + + buffer->str.u8str = (void *)"12345"; + assert_true(buffer_text_visible_height(buffer) == 1); + + buffer->str.u8str = (void *)"\n12345"; + assert_true(buffer_text_visible_height(buffer) == 2); + + buffer->str.u8str = (void *)"\n12345\n\n2"; + assert_true(buffer_text_visible_height(buffer) == 4); #endif - buffer->type = CharBuf; + buffer->type = CHAR_BUF; buffer->str.cstr = old_value; destroy_string_buffer(buffer); } @@ -367,3 +599,59 @@ void test_wchar_basics(void) #endif } #endif + + +static void test_print_n_strings_(const char *str, size_t n) +{ + int sz = n * strlen(str); + { + string_buffer_t *buffer = create_string_buffer(200, CHAR_BUF); + conv_context_t cntx; + cntx.buf = (char *)buffer_get_data(buffer); + cntx.buf_origin = (char *)buffer_get_data(buffer); + cntx.raw_avail = 200; + cntx.b_type = CHAR_BUF; + assert_true(print_n_strings(&cntx, n, str) == sz); + assert_true(cntx.buf - cntx.buf_origin == sz); + destroy_string_buffer(buffer); + } + + { + string_buffer_t *buffer = create_string_buffer(200, W_CHAR_BUF); + conv_context_t cntx; + cntx.buf = (char *)buffer_get_data(buffer); + cntx.buf_origin = (char *)buffer_get_data(buffer); + cntx.raw_avail = 200; + cntx.b_type = W_CHAR_BUF; + assert_true(print_n_strings(&cntx, n, str) == /*sizeof(wchar_t) **/ sz); + assert_true(cntx.buf - cntx.buf_origin == sizeof(wchar_t) * sz); + destroy_string_buffer(buffer); + } + + + { + string_buffer_t *buffer = create_string_buffer(200, UTF8_BUF); + conv_context_t cntx; + cntx.buf = (char *)buffer_get_data(buffer); + cntx.buf_origin = (char *)buffer_get_data(buffer); + cntx.raw_avail = 200; + cntx.b_type = UTF8_BUF; + assert_true(print_n_strings(&cntx, n, str) == sz); + assert_true(cntx.buf - cntx.buf_origin == sz); + destroy_string_buffer(buffer); + } +} +void test_print_n_strings(void) +{ + test_print_n_strings_("", 0); + test_print_n_strings_("", 1); + test_print_n_strings_("", 2); + + test_print_n_strings_(" ", 0); + test_print_n_strings_(" ", 1); + test_print_n_strings_(" ", 2); + + test_print_n_strings_("foo", 0); + test_print_n_strings_("foo", 1); + test_print_n_strings_("foo", 2); +}