md

cat markdown files with syntax highlighting
git clone https://noulin.net/git/md.git
Log | Files | Refs | README | LICENSE

fort.c (227773B)


      1 /*
      2 libfort
      3 
      4 MIT License
      5 
      6 Copyright (c) 2017 - 2020 Seleznev Anton
      7 
      8 Permission is hereby granted, free of charge, to any person obtaining a copy
      9 of this software and associated documentation files (the "Software"), to deal
     10 in the Software without restriction, including without limitation the rights
     11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     12 copies of the Software, and to permit persons to whom the Software is
     13 furnished to do so, subject to the following conditions:
     14 
     15 The above copyright notice and this permission notice shall be included in all
     16 copies or substantial portions of the Software.
     17 
     18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     24 SOFTWARE.
     25 */
     26 
     27 /* The file was GENERATED by an amalgamation script.*/
     28 /* DO NOT EDIT BY HAND!!! */
     29 
     30 
     31 #define FT_AMALGAMED_SOURCE /* Macros to make internal libfort functions static */
     32 
     33 
     34 /********************************************************
     35    Begin of file "fort_utils.h"
     36  ********************************************************/
     37 
     38 #ifndef FORT_IMPL_H
     39 #define FORT_IMPL_H
     40 
     41 #if defined(_MSC_VER)
     42 #define _CRT_SECURE_NO_WARNINGS /* To disable warnings for unsafe functions */
     43 #endif
     44 
     45 #include <stddef.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <assert.h>
     49 #include <stdio.h>
     50 #include <stdbool.h>
     51 #include "fort.h"
     52 
     53 /* Define FT_INTERNAL to make internal libfort functions static
     54  * in the result amalgamed source file.
     55  */
     56 #ifdef FT_AMALGAMED_SOURCE
     57 #define FT_INTERNAL static
     58 #else
     59 #define FT_INTERNAL
     60 #endif /* FT_AMALGAMED_SORCE */
     61 
     62 
     63 #define FORT_DEFAULT_COL_SEPARATOR '|'
     64 extern char g_col_separator;
     65 
     66 #define FORT_COL_SEPARATOR_LENGTH 1
     67 
     68 #define FORT_UNUSED  __attribute__((unused))
     69 
     70 #define F_MALLOC fort_malloc
     71 #define F_FREE fort_free
     72 #define F_CALLOC fort_calloc
     73 #define F_REALLOC fort_realloc
     74 #define F_STRDUP fort_strdup
     75 #define F_WCSDUP fort_wcsdup
     76 /* @todo: replace with custom impl !!!*/
     77 #define F_UTF8DUP utf8dup
     78 
     79 #define F_CREATE(type) ((type *)F_CALLOC(sizeof(type), 1))
     80 
     81 #define MAX(a,b) ((a) > (b) ? (a) : (b))
     82 #define MIN(a,b) ((a) < (b) ? (a) : (b))
     83 
     84 #define FT_NEWLINE "\n"
     85 #define FT_SPACE " "
     86 
     87 /*****************************************************************************
     88  *               DEFAULT_SIZES
     89  * ***************************************************************************/
     90 #define DEFAULT_STR_BUF_SIZE 1024
     91 #define DEFAULT_VECTOR_CAPACITY 10
     92 
     93 /*****************************************************************************
     94  *               DATA TYPES
     95  * ***************************************************************************/
     96 
     97 enum f_get_policy {
     98     CREATE_ON_NULL,
     99     DONT_CREATE_ON_NULL
    100 };
    101 
    102 enum f_bool {
    103     F_FALSE = 0,
    104     F_TRUE = 1
    105 };
    106 
    107 enum f_cell_type {
    108     COMMON_CELL,
    109     GROUP_MASTER_CELL,
    110     GROUP_SLAVE_CELL
    111 };
    112 
    113 enum f_geometry_type {
    114     VISIBLE_GEOMETRY,
    115     INTERN_REPR_GEOMETRY
    116 };
    117 
    118 enum f_string_type {
    119     CHAR_BUF,
    120 #ifdef FT_HAVE_WCHAR
    121     W_CHAR_BUF,
    122 #endif /* FT_HAVE_WCHAR */
    123 #ifdef FT_HAVE_UTF8
    124     UTF8_BUF,
    125 #endif /* FT_HAVE_WCHAR */
    126 };
    127 
    128 struct f_string_view {
    129     union {
    130         const char *cstr;
    131 #ifdef FT_HAVE_WCHAR
    132         const wchar_t *wstr;
    133 #endif
    134 #ifdef FT_HAVE_UTF8
    135         const void *u8str;
    136 #endif
    137         const void *data;
    138     } u;
    139     enum f_string_type type;
    140 };
    141 typedef struct f_string_view f_string_view_t;
    142 
    143 
    144 #define FT_STR_2_CAT_(arg1, arg2) \
    145     arg1##arg2
    146 #define FT_STR_2_CAT(arg1, arg2) \
    147     FT_STR_2_CAT_(arg1, arg2)
    148 
    149 #define UNIQUE_NAME_(prefix) \
    150     FT_STR_2_CAT(prefix,__COUNTER__)
    151 #define UNIQUE_NAME(prefix) \
    152     UNIQUE_NAME_(prefix)
    153 
    154 typedef int f_status;
    155 
    156 
    157 
    158 
    159 struct f_table_properties;
    160 struct f_row;
    161 struct f_vector;
    162 struct f_cell;
    163 struct f_string_buffer;
    164 struct f_separator {
    165     int enabled;
    166 };
    167 
    168 typedef struct f_table_properties f_table_properties_t;
    169 typedef struct f_vector f_vector_t;
    170 typedef struct f_cell f_cell_t;
    171 typedef struct f_string_buffer f_string_buffer_t;
    172 typedef struct f_row f_row_t;
    173 typedef struct f_separator f_separator_t;
    174 
    175 struct f_context {
    176     f_table_properties_t *table_properties;
    177     size_t row;
    178     size_t column;
    179 };
    180 typedef struct f_context f_context_t;
    181 
    182 struct f_conv_context {
    183     union {
    184         char *buf;
    185 #ifdef FT_HAVE_WCHAR
    186         wchar_t *wbuf;
    187 #endif
    188 #ifdef FT_HAVE_UTF8
    189         const void *u8str;
    190 #endif
    191     } u;
    192     size_t raw_avail;
    193     struct f_context *cntx;
    194     enum f_string_type b_type;
    195 };
    196 typedef struct f_conv_context f_conv_context_t;
    197 
    198 
    199 /*****************************************************************************
    200  *               LIBFORT helpers
    201  *****************************************************************************/
    202 
    203 extern void *(*fort_malloc)(size_t size);
    204 extern void (*fort_free)(void *ptr);
    205 extern void *(*fort_calloc)(size_t nmemb, size_t size);
    206 extern void *(*fort_realloc)(void *ptr, size_t size);
    207 
    208 FT_INTERNAL
    209 void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr));
    210 
    211 FT_INTERNAL
    212 char *fort_strdup(const char *str);
    213 
    214 
    215 
    216 FT_INTERNAL
    217 size_t number_of_columns_in_format_string(const f_string_view_t *fmt);
    218 
    219 FT_INTERNAL
    220 size_t number_of_columns_in_format_buffer(const f_string_buffer_t *fmt);
    221 
    222 #if defined(FT_HAVE_WCHAR)
    223 FT_INTERNAL
    224 wchar_t *fort_wcsdup(const wchar_t *str);
    225 #endif
    226 
    227 
    228 
    229 FT_INTERNAL
    230 int print_n_strings(f_conv_context_t *cntx, size_t n, const char *str);
    231 
    232 
    233 FT_INTERNAL
    234 int ft_nprint(f_conv_context_t *cntx, const char *str, size_t strlen);
    235 #ifdef FT_HAVE_WCHAR
    236 FT_INTERNAL
    237 int ft_nwprint(f_conv_context_t *cntx, const wchar_t *str, size_t strlen);
    238 #endif /* FT_HAVE_WCHAR */
    239 #ifdef FT_HAVE_UTF8
    240 FT_INTERNAL
    241 int ft_nu8print(f_conv_context_t *cntx, const void *beg, const void *end);
    242 #endif /* FT_HAVE_UTF8 */
    243 
    244 
    245 /*#define PRINT_DEBUG_INFO fprintf(stderr, "error in %s(%s:%d)\n", __FUNCTION__, __FILE__, __LINE__);*/
    246 #define PRINT_DEBUG_INFO
    247 
    248 #define FT_CHECK(statement) \
    249     do { \
    250         tmp = statement; \
    251         if (tmp < 0) {\
    252             PRINT_DEBUG_INFO \
    253             goto clear; \
    254         } \
    255     } while(0)
    256 
    257 #define CHCK_RSLT_ADD_TO_WRITTEN(statement) \
    258     do { \
    259         tmp = statement; \
    260         if (tmp < 0) {\
    261             PRINT_DEBUG_INFO \
    262             goto clear; \
    263         } \
    264         written += (size_t)tmp; \
    265     } while(0)
    266 
    267 #define CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(statement) \
    268     do { \
    269         tmp = statement; \
    270         if (tmp < 0) {\
    271             PRINT_DEBUG_INFO \
    272             goto clear; \
    273         } \
    274         invisible_written += (size_t)tmp; \
    275     } while(0)
    276 
    277 
    278 #define CHECK_NOT_NEGATIVE(x) \
    279     do { if ((x) < 0) goto fort_fail; } while (0)
    280 
    281 #endif /* FORT_IMPL_H */
    282 
    283 /********************************************************
    284    End of file "fort_utils.h"
    285  ********************************************************/
    286 
    287 
    288 /********************************************************
    289    Begin of file "vector.h"
    290  ********************************************************/
    291 
    292 #ifndef VECTOR_H
    293 #define VECTOR_H
    294 
    295 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
    296 
    297 
    298 #define INVALID_VEC_INDEX ((size_t) -1)
    299 
    300 FT_INTERNAL
    301 f_vector_t *create_vector(size_t item_size, size_t capacity);
    302 
    303 FT_INTERNAL
    304 void destroy_vector(f_vector_t *);
    305 
    306 FT_INTERNAL
    307 size_t vector_size(const f_vector_t *);
    308 
    309 FT_INTERNAL
    310 size_t vector_capacity(const f_vector_t *);
    311 
    312 FT_INTERNAL
    313 int vector_push(f_vector_t *, const void *item);
    314 
    315 FT_INTERNAL
    316 int vector_insert(f_vector_t *, const void *item, size_t pos);
    317 
    318 FT_INTERNAL
    319 f_vector_t *vector_split(f_vector_t *, size_t pos);
    320 
    321 FT_INTERNAL
    322 const void *vector_at_c(const f_vector_t *vector, size_t index);
    323 
    324 FT_INTERNAL
    325 void *vector_at(f_vector_t *, size_t index);
    326 
    327 FT_INTERNAL
    328 f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos);
    329 
    330 FT_INTERNAL
    331 void vector_clear(f_vector_t *);
    332 
    333 FT_INTERNAL
    334 int vector_erase(f_vector_t *, size_t index);
    335 
    336 #ifdef FT_TEST_BUILD
    337 f_vector_t *copy_vector(f_vector_t *);
    338 size_t vector_index_of(const f_vector_t *, const void *item);
    339 #endif
    340 
    341 #define VECTOR_AT(vector, pos, data_type) \
    342     *(data_type *)vector_at((vector), (pos))
    343 
    344 #define VECTOR_AT_C(vector, pos, const_data_type) \
    345     *(const_data_type *)vector_at_c((vector), (pos))
    346 
    347 #endif /* VECTOR_H */
    348 
    349 /********************************************************
    350    End of file "vector.h"
    351  ********************************************************/
    352 
    353 
    354 /********************************************************
    355    Begin of file "wcwidth.h"
    356  ********************************************************/
    357 
    358 #ifndef WCWIDTH_H
    359 #define WCWIDTH_H
    360 
    361 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
    362 
    363 #ifdef FT_HAVE_WCHAR
    364 #include <wchar.h>
    365 
    366 FT_INTERNAL
    367 int mk_wcswidth(const wchar_t *pwcs, size_t n);
    368 
    369 #endif /* FT_HAVE_WCHAR */
    370 
    371 #endif /* WCWIDTH_H */
    372 
    373 /********************************************************
    374    End of file "wcwidth.h"
    375  ********************************************************/
    376 
    377 
    378 /********************************************************
    379    Begin of file "utf8.h"
    380  ********************************************************/
    381 
    382 // The latest version of this library is available on GitHub;
    383 // https://github.com/sheredom/utf8.h
    384 
    385 // This is free and unencumbered software released into the public domain.
    386 //
    387 // Anyone is free to copy, modify, publish, use, compile, sell, or
    388 // distribute this software, either in source code form or as a compiled
    389 // binary, for any purpose, commercial or non-commercial, and by any
    390 // means.
    391 //
    392 // In jurisdictions that recognize copyright laws, the author or authors
    393 // of this software dedicate any and all copyright interest in the
    394 // software to the public domain. We make this dedication for the benefit
    395 // of the public at large and to the detriment of our heirs and
    396 // successors. We intend this dedication to be an overt act of
    397 // relinquishment in perpetuity of all present and future rights to this
    398 // software under copyright law.
    399 //
    400 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    401 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    402 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    403 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    404 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    405 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    406 // OTHER DEALINGS IN THE SOFTWARE.
    407 //
    408 // For more information, please refer to <http://unlicense.org/>
    409 
    410 #ifndef SHEREDOM_UTF8_H_INCLUDED
    411 #define SHEREDOM_UTF8_H_INCLUDED
    412 
    413 #if defined(_MSC_VER)
    414 #pragma warning(push)
    415 
    416 // disable 'bytes padding added after construct' warning
    417 #pragma warning(disable : 4820)
    418 #endif
    419 
    420 #include <stddef.h>
    421 #include <stdlib.h>
    422 
    423 #if defined(_MSC_VER)
    424 #pragma warning(pop)
    425 #endif
    426 
    427 #if defined(_MSC_VER)
    428 typedef __int32 utf8_int32_t;
    429 #else
    430 #include <stdint.h>
    431 typedef int32_t utf8_int32_t;
    432 #endif
    433 
    434 #if defined(__clang__)
    435 #pragma clang diagnostic push
    436 #pragma clang diagnostic ignored "-Wold-style-cast"
    437 #pragma clang diagnostic ignored "-Wcast-qual"
    438 #endif
    439 
    440 #ifdef __cplusplus
    441 extern "C" {
    442 #endif
    443 
    444 #if defined(__clang__) || defined(__GNUC__)
    445 #define utf8_nonnull __attribute__((nonnull))
    446 #define utf8_pure __attribute__((pure))
    447 #define utf8_restrict __restrict__
    448 #define utf8_weak __attribute__((weak))
    449 #elif defined(_MSC_VER)
    450 #define utf8_nonnull
    451 #define utf8_pure
    452 #define utf8_restrict __restrict
    453 #define utf8_weak __inline
    454 #else
    455 #define utf8_nonnull
    456 #define utf8_pure
    457 #define utf8_restrict
    458 #define utf8_weak inline
    459 #endif
    460 
    461 #ifdef __cplusplus
    462 #define utf8_null NULL
    463 #else
    464 #define utf8_null 0
    465 #endif
    466 
    467 // Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
    468 // src2 respectively, case insensitive.
    469 utf8_nonnull utf8_pure utf8_weak int utf8casecmp(const void *src1,
    470         const void *src2);
    471 
    472 // Append the utf8 string src onto the utf8 string dst.
    473 utf8_nonnull utf8_weak void *utf8cat(void *utf8_restrict dst,
    474                                      const void *utf8_restrict src);
    475 
    476 // Find the first match of the utf8 codepoint chr in the utf8 string src.
    477 utf8_nonnull utf8_pure utf8_weak void *utf8chr(const void *src,
    478         utf8_int32_t chr);
    479 
    480 // Return less than 0, 0, greater than 0 if src1 < src2,
    481 // src1 == src2, src1 > src2 respectively.
    482 utf8_nonnull utf8_pure utf8_weak int utf8cmp(const void *src1,
    483         const void *src2);
    484 
    485 // Copy the utf8 string src onto the memory allocated in dst.
    486 utf8_nonnull utf8_weak void *utf8cpy(void *utf8_restrict dst,
    487                                      const void *utf8_restrict src);
    488 
    489 // Number of utf8 codepoints in the utf8 string src that consists entirely
    490 // of utf8 codepoints not from the utf8 string reject.
    491 utf8_nonnull utf8_pure utf8_weak size_t utf8cspn(const void *src,
    492         const void *reject);
    493 
    494 // Duplicate the utf8 string src by getting its size, malloc'ing a new buffer
    495 // copying over the data, and returning that. Or 0 if malloc failed.
    496 utf8_nonnull utf8_weak void *utf8dup(const void *src);
    497 
    498 // Number of utf8 codepoints in the utf8 string str,
    499 // excluding the null terminating byte.
    500 utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str);
    501 
    502 // Visible width of utf8string.
    503 utf8_nonnull utf8_pure utf8_weak size_t utf8width(const void *str);
    504 
    505 // Visible width of codepoint.
    506 utf8_nonnull utf8_pure utf8_weak int utf8cwidth(utf8_int32_t c);
    507 
    508 // Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
    509 // src2 respectively, case insensitive. Checking at most n bytes of each utf8
    510 // string.
    511 utf8_nonnull utf8_pure utf8_weak int utf8ncasecmp(const void *src1,
    512         const void *src2, size_t n);
    513 
    514 // Append the utf8 string src onto the utf8 string dst,
    515 // writing at most n+1 bytes. Can produce an invalid utf8
    516 // string if n falls partway through a utf8 codepoint.
    517 utf8_nonnull utf8_weak void *utf8ncat(void *utf8_restrict dst,
    518                                       const void *utf8_restrict src, size_t n);
    519 
    520 // Return less than 0, 0, greater than 0 if src1 < src2,
    521 // src1 == src2, src1 > src2 respectively. Checking at most n
    522 // bytes of each utf8 string.
    523 utf8_nonnull utf8_pure utf8_weak int utf8ncmp(const void *src1,
    524         const void *src2, size_t n);
    525 
    526 // Copy the utf8 string src onto the memory allocated in dst.
    527 // Copies at most n bytes. If there is no terminating null byte in
    528 // the first n bytes of src, the string placed into dst will not be
    529 // null-terminated. If the size (in bytes) of src is less than n,
    530 // extra null terminating bytes are appended to dst such that at
    531 // total of n bytes are written. Can produce an invalid utf8
    532 // string if n falls partway through a utf8 codepoint.
    533 utf8_nonnull utf8_weak void *utf8ncpy(void *utf8_restrict dst,
    534                                       const void *utf8_restrict src, size_t n);
    535 
    536 // Similar to utf8dup, except that at most n bytes of src are copied. If src is
    537 // longer than n, only n bytes are copied and a null byte is added.
    538 //
    539 // Returns a new string if successful, 0 otherwise
    540 utf8_nonnull utf8_weak void *utf8ndup(const void *src, size_t n);
    541 
    542 // Locates the first occurence in the utf8 string str of any byte in the
    543 // utf8 string accept, or 0 if no match was found.
    544 utf8_nonnull utf8_pure utf8_weak void *utf8pbrk(const void *str,
    545         const void *accept);
    546 
    547 // Find the last match of the utf8 codepoint chr in the utf8 string src.
    548 utf8_nonnull utf8_pure utf8_weak void *utf8rchr(const void *src, int chr);
    549 
    550 // Number of bytes in the utf8 string str,
    551 // including the null terminating byte.
    552 utf8_nonnull utf8_pure utf8_weak size_t utf8size(const void *str);
    553 
    554 // Number of utf8 codepoints in the utf8 string src that consists entirely
    555 // of utf8 codepoints from the utf8 string accept.
    556 utf8_nonnull utf8_pure utf8_weak size_t utf8spn(const void *src,
    557         const void *accept);
    558 
    559 // The position of the utf8 string needle in the utf8 string haystack.
    560 utf8_nonnull utf8_pure utf8_weak void *utf8str(const void *haystack,
    561         const void *needle);
    562 
    563 // The position of the utf8 string needle in the utf8 string haystack, case
    564 // insensitive.
    565 utf8_nonnull utf8_pure utf8_weak void *utf8casestr(const void *haystack,
    566         const void *needle);
    567 
    568 // Return 0 on success, or the position of the invalid
    569 // utf8 codepoint on failure.
    570 utf8_nonnull utf8_pure utf8_weak void *utf8valid(const void *str);
    571 
    572 // Sets out_codepoint to the next utf8 codepoint in str, and returns the address
    573 // of the utf8 codepoint after the current one in str.
    574 utf8_nonnull utf8_weak void *
    575 utf8codepoint(const void *utf8_restrict str,
    576               utf8_int32_t *utf8_restrict out_codepoint);
    577 
    578 // Returns the size of the given codepoint in bytes.
    579 utf8_weak size_t utf8codepointsize(utf8_int32_t chr);
    580 
    581 // Write a codepoint to the given string, and return the address to the next
    582 // place after the written codepoint. Pass how many bytes left in the buffer to
    583 // n. If there is not enough space for the codepoint, this function returns
    584 // null.
    585 utf8_nonnull utf8_weak void *utf8catcodepoint(void *utf8_restrict str,
    586         utf8_int32_t chr, size_t n);
    587 
    588 // Returns 1 if the given character is lowercase, or 0 if it is not.
    589 utf8_weak int utf8islower(utf8_int32_t chr);
    590 
    591 // Returns 1 if the given character is uppercase, or 0 if it is not.
    592 utf8_weak int utf8isupper(utf8_int32_t chr);
    593 
    594 // Transform the given string into all lowercase codepoints.
    595 utf8_nonnull utf8_weak void utf8lwr(void *utf8_restrict str);
    596 
    597 // Transform the given string into all uppercase codepoints.
    598 utf8_nonnull utf8_weak void utf8upr(void *utf8_restrict str);
    599 
    600 // Make a codepoint lower case if possible.
    601 utf8_weak utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp);
    602 
    603 // Make a codepoint upper case if possible.
    604 utf8_weak utf8_int32_t utf8uprcodepoint(utf8_int32_t cp);
    605 
    606 #undef utf8_weak
    607 #undef utf8_pure
    608 #undef utf8_nonnull
    609 
    610 int utf8casecmp(const void *src1, const void *src2)
    611 {
    612     utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp;
    613 
    614     for (;;) {
    615         src1 = utf8codepoint(src1, &src1_cp);
    616         src2 = utf8codepoint(src2, &src2_cp);
    617 
    618         // Take a copy of src1 & src2
    619         src1_orig_cp = src1_cp;
    620         src2_orig_cp = src2_cp;
    621 
    622         // Lower the srcs if required
    623         src1_cp = utf8lwrcodepoint(src1_cp);
    624         src2_cp = utf8lwrcodepoint(src2_cp);
    625 
    626         // Check if the lowered codepoints match
    627         if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
    628             return 0;
    629         } else if (src1_cp == src2_cp) {
    630             continue;
    631         }
    632 
    633         // If they don't match, then we return which of the original's are less
    634         if (src1_orig_cp < src2_orig_cp) {
    635             return -1;
    636         } else if (src1_orig_cp > src2_orig_cp) {
    637             return 1;
    638         }
    639     }
    640 }
    641 
    642 void *utf8cat(void *utf8_restrict dst, const void *utf8_restrict src)
    643 {
    644     char *d = (char *)dst;
    645     const char *s = (const char *)src;
    646 
    647     // find the null terminating byte in dst
    648     while ('\0' != *d) {
    649         d++;
    650     }
    651 
    652     // overwriting the null terminating byte in dst, append src byte-by-byte
    653     while ('\0' != *s) {
    654         *d++ = *s++;
    655     }
    656 
    657     // write out a new null terminating byte into dst
    658     *d = '\0';
    659 
    660     return dst;
    661 }
    662 
    663 void *utf8chr(const void *src, utf8_int32_t chr)
    664 {
    665     char c[5] = {'\0', '\0', '\0', '\0', '\0'};
    666 
    667     if (0 == chr) {
    668         // being asked to return position of null terminating byte, so
    669         // just run s to the end, and return!
    670         const char *s = (const char *)src;
    671         while ('\0' != *s) {
    672             s++;
    673         }
    674         return (void *)s;
    675     } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
    676         // 1-byte/7-bit ascii
    677         // (0b0xxxxxxx)
    678         c[0] = (char)chr;
    679     } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
    680         // 2-byte/11-bit utf8 code point
    681         // (0b110xxxxx 0b10xxxxxx)
    682         c[0] = 0xc0 | (char)(chr >> 6);
    683         c[1] = 0x80 | (char)(chr & 0x3f);
    684     } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
    685         // 3-byte/16-bit utf8 code point
    686         // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
    687         c[0] = 0xe0 | (char)(chr >> 12);
    688         c[1] = 0x80 | (char)((chr >> 6) & 0x3f);
    689         c[2] = 0x80 | (char)(chr & 0x3f);
    690     } else { // if (0 == ((int)0xffe00000 & chr)) {
    691         // 4-byte/21-bit utf8 code point
    692         // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
    693         c[0] = 0xf0 | (char)(chr >> 18);
    694         c[1] = 0x80 | (char)((chr >> 12) & 0x3f);
    695         c[2] = 0x80 | (char)((chr >> 6) & 0x3f);
    696         c[3] = 0x80 | (char)(chr & 0x3f);
    697     }
    698 
    699     // we've made c into a 2 utf8 codepoint string, one for the chr we are
    700     // seeking, another for the null terminating byte. Now use utf8str to
    701     // search
    702     return utf8str(src, c);
    703 }
    704 
    705 int utf8cmp(const void *src1, const void *src2)
    706 {
    707     const unsigned char *s1 = (const unsigned char *)src1;
    708     const unsigned char *s2 = (const unsigned char *)src2;
    709 
    710     while (('\0' != *s1) || ('\0' != *s2)) {
    711         if (*s1 < *s2) {
    712             return -1;
    713         } else if (*s1 > *s2) {
    714             return 1;
    715         }
    716 
    717         s1++;
    718         s2++;
    719     }
    720 
    721     // both utf8 strings matched
    722     return 0;
    723 }
    724 
    725 int utf8coll(const void *src1, const void *src2);
    726 
    727 void *utf8cpy(void *utf8_restrict dst, const void *utf8_restrict src)
    728 {
    729     char *d = (char *)dst;
    730     const char *s = (const char *)src;
    731 
    732     // overwriting anything previously in dst, write byte-by-byte
    733     // from src
    734     while ('\0' != *s) {
    735         *d++ = *s++;
    736     }
    737 
    738     // append null terminating byte
    739     *d = '\0';
    740 
    741     return dst;
    742 }
    743 
    744 size_t utf8cspn(const void *src, const void *reject)
    745 {
    746     const char *s = (const char *)src;
    747     size_t chars = 0;
    748 
    749     while ('\0' != *s) {
    750         const char *r = (const char *)reject;
    751         size_t offset = 0;
    752 
    753         while ('\0' != *r) {
    754             // checking that if *r is the start of a utf8 codepoint
    755             // (it is not 0b10xxxxxx) and we have successfully matched
    756             // a previous character (0 < offset) - we found a match
    757             if ((0x80 != (0xc0 & *r)) && (0 < offset)) {
    758                 return chars;
    759             } else {
    760                 if (*r == s[offset]) {
    761                     // part of a utf8 codepoint matched, so move our checking
    762                     // onwards to the next byte
    763                     offset++;
    764                     r++;
    765                 } else {
    766                     // r could be in the middle of an unmatching utf8 code point,
    767                     // so we need to march it on to the next character beginning,
    768 
    769                     do {
    770                         r++;
    771                     } while (0x80 == (0xc0 & *r));
    772 
    773                     // reset offset too as we found a mismatch
    774                     offset = 0;
    775                 }
    776             }
    777         }
    778 
    779         // the current utf8 codepoint in src did not match reject, but src
    780         // could have been partway through a utf8 codepoint, so we need to
    781         // march it onto the next utf8 codepoint starting byte
    782         do {
    783             s++;
    784         } while ((0x80 == (0xc0 & *s)));
    785         chars++;
    786     }
    787 
    788     return chars;
    789 }
    790 
    791 size_t utf8size(const void *str);
    792 
    793 void *utf8dup(const void *src)
    794 {
    795     const char *s = (const char *)src;
    796     char *n = utf8_null;
    797 
    798     // figure out how many bytes (including the terminator) we need to copy first
    799     size_t bytes = utf8size(src);
    800 
    801     n = (char *)malloc(bytes);
    802 
    803     if (utf8_null == n) {
    804         // out of memory so we bail
    805         return utf8_null;
    806     } else {
    807         bytes = 0;
    808 
    809         // copy src byte-by-byte into our new utf8 string
    810         while ('\0' != s[bytes]) {
    811             n[bytes] = s[bytes];
    812             bytes++;
    813         }
    814 
    815         // append null terminating byte
    816         n[bytes] = '\0';
    817         return n;
    818     }
    819 }
    820 
    821 void *utf8fry(const void *str);
    822 
    823 size_t utf8len(const void *str)
    824 {
    825     const unsigned char *s = (const unsigned char *)str;
    826     size_t length = 0;
    827 
    828     while ('\0' != *s) {
    829         if (0xf0 == (0xf8 & *s)) {
    830             // 4-byte utf8 code point (began with 0b11110xxx)
    831             s += 4;
    832         } else if (0xe0 == (0xf0 & *s)) {
    833             // 3-byte utf8 code point (began with 0b1110xxxx)
    834             s += 3;
    835         } else if (0xc0 == (0xe0 & *s)) {
    836             // 2-byte utf8 code point (began with 0b110xxxxx)
    837             s += 2;
    838         } else { // if (0x00 == (0x80 & *s)) {
    839             // 1-byte ascii (began with 0b0xxxxxxx)
    840             s += 1;
    841         }
    842 
    843         // no matter the bytes we marched s forward by, it was
    844         // only 1 utf8 codepoint
    845         length++;
    846     }
    847 
    848     return length;
    849 }
    850 
    851 // See
    852 // https://unicode.org/Public/UNIDATA/EastAsianWidth.txt
    853 // http://www.unicode.org/reports/tr11/tr11-33.html
    854 int utf8cwidth(utf8_int32_t c)
    855 {
    856     // TODO: add non printable characters check
    857     if (c == 0)
    858         return 0;
    859 
    860     if (c < 0x1100)
    861         return 1;
    862 
    863     // Fullwidth
    864     if ((0x3000 == c) ||
    865         (0xFF01 <= c && c <= 0xFF60) ||
    866         (0xFFE0 <= c && c <= 0xFFE6)) {
    867         return 2;
    868     }
    869 
    870     // Wide
    871     if ((0x1100 <= c && c <= 0x115F) ||
    872         (0x11A3 <= c && c <= 0x11A7) ||
    873         (0x11FA <= c && c <= 0x11FF) ||
    874         (0x2329 <= c && c <= 0x232A) ||
    875         (0x2E80 <= c && c <= 0x2E99) ||
    876         (0x2E9B <= c && c <= 0x2EF3) ||
    877         (0x2F00 <= c && c <= 0x2FD5) ||
    878         (0x2FF0 <= c && c <= 0x2FFB) ||
    879         (0x3001 <= c && c <= 0x303E) ||
    880         (0x3041 <= c && c <= 0x3096) ||
    881         (0x3099 <= c && c <= 0x30FF) ||
    882         (0x3105 <= c && c <= 0x312D) ||
    883         (0x3131 <= c && c <= 0x318E) ||
    884         (0x3190 <= c && c <= 0x31BA) ||
    885         (0x31C0 <= c && c <= 0x31E3) ||
    886         (0x31F0 <= c && c <= 0x321E) ||
    887         (0x3220 <= c && c <= 0x3247) ||
    888         (0x3250 <= c && c <= 0x32FE) ||
    889         (0x3300 <= c && c <= 0x4DBF) ||
    890         (0x4E00 <= c && c <= 0xA48C) ||
    891         (0xA490 <= c && c <= 0xA4C6) ||
    892         (0xA960 <= c && c <= 0xA97C) ||
    893         (0xAC00 <= c && c <= 0xD7A3) ||
    894         (0xD7B0 <= c && c <= 0xD7C6) ||
    895         (0xD7CB <= c && c <= 0xD7FB) ||
    896         (0xF900 <= c && c <= 0xFAFF) ||
    897         (0xFE10 <= c && c <= 0xFE19) ||
    898         (0xFE30 <= c && c <= 0xFE52) ||
    899         (0xFE54 <= c && c <= 0xFE66) ||
    900         (0xFE68 <= c && c <= 0xFE6B) ||
    901         (0x1B000 <= c && c <= 0x1B001) ||
    902         (0x1F200 <= c && c <= 0x1F202) ||
    903         (0x1F210 <= c && c <= 0x1F23A) ||
    904         (0x1F240 <= c && c <= 0x1F248) ||
    905         (0x1F250 <= c && c <= 0x1F251) ||
    906         (0x20000 <= c && c <= 0x2F73F) ||
    907         (0x2B740 <= c && c <= 0x2FFFD) ||
    908         (0x30000 <= c && c <= 0x3FFFD)) {
    909         return 2;
    910     }
    911 
    912     return 1;
    913 }
    914 
    915 size_t utf8width(const void *str)
    916 {
    917     size_t length = 0;
    918     utf8_int32_t c = 0;
    919 
    920     str = utf8codepoint(str, &c);
    921     while (c != 0) {
    922         length += utf8cwidth(c);
    923         str = utf8codepoint(str, &c);
    924     }
    925     return length;
    926 }
    927 
    928 int utf8ncasecmp(const void *src1, const void *src2, size_t n)
    929 {
    930     utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp;
    931 
    932     do {
    933         const unsigned char *const s1 = (const unsigned char *)src1;
    934         const unsigned char *const s2 = (const unsigned char *)src2;
    935 
    936         // first check that we have enough bytes left in n to contain an entire
    937         // codepoint
    938         if (0 == n) {
    939             return 0;
    940         }
    941 
    942         if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) {
    943             const utf8_int32_t c1 = (0xe0 & *s1);
    944             const utf8_int32_t c2 = (0xe0 & *s2);
    945 
    946             if (c1 < c2) {
    947                 return -1;
    948             } else if (c1 > c2) {
    949                 return 1;
    950             } else {
    951                 return 0;
    952             }
    953         }
    954 
    955         if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) {
    956             const utf8_int32_t c1 = (0xf0 & *s1);
    957             const utf8_int32_t c2 = (0xf0 & *s2);
    958 
    959             if (c1 < c2) {
    960                 return -1;
    961             } else if (c1 > c2) {
    962                 return 1;
    963             } else {
    964                 return 0;
    965             }
    966         }
    967 
    968         if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) {
    969             const utf8_int32_t c1 = (0xf8 & *s1);
    970             const utf8_int32_t c2 = (0xf8 & *s2);
    971 
    972             if (c1 < c2) {
    973                 return -1;
    974             } else if (c1 > c2) {
    975                 return 1;
    976             } else {
    977                 return 0;
    978             }
    979         }
    980 
    981         src1 = utf8codepoint(src1, &src1_cp);
    982         src2 = utf8codepoint(src2, &src2_cp);
    983         n -= utf8codepointsize(src1_cp);
    984 
    985         // Take a copy of src1 & src2
    986         src1_orig_cp = src1_cp;
    987         src2_orig_cp = src2_cp;
    988 
    989         // Lower srcs if required
    990         src1_cp = utf8lwrcodepoint(src1_cp);
    991         src2_cp = utf8lwrcodepoint(src2_cp);
    992 
    993         // Check if the lowered codepoints match
    994         if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
    995             return 0;
    996         } else if (src1_cp == src2_cp) {
    997             continue;
    998         }
    999 
   1000         // If they don't match, then we return which of the original's are less
   1001         if (src1_orig_cp < src2_orig_cp) {
   1002             return -1;
   1003         } else if (src1_orig_cp > src2_orig_cp) {
   1004             return 1;
   1005         }
   1006     } while (0 < n);
   1007 
   1008     // both utf8 strings matched
   1009     return 0;
   1010 }
   1011 
   1012 void *utf8ncat(void *utf8_restrict dst, const void *utf8_restrict src,
   1013                size_t n)
   1014 {
   1015     char *d = (char *)dst;
   1016     const char *s = (const char *)src;
   1017 
   1018     // find the null terminating byte in dst
   1019     while ('\0' != *d) {
   1020         d++;
   1021     }
   1022 
   1023     // overwriting the null terminating byte in dst, append src byte-by-byte
   1024     // stopping if we run out of space
   1025     do {
   1026         *d++ = *s++;
   1027     } while (('\0' != *s) && (0 != --n));
   1028 
   1029     // write out a new null terminating byte into dst
   1030     *d = '\0';
   1031 
   1032     return dst;
   1033 }
   1034 
   1035 int utf8ncmp(const void *src1, const void *src2, size_t n)
   1036 {
   1037     const unsigned char *s1 = (const unsigned char *)src1;
   1038     const unsigned char *s2 = (const unsigned char *)src2;
   1039 
   1040     while ((0 != n--) && (('\0' != *s1) || ('\0' != *s2))) {
   1041         if (*s1 < *s2) {
   1042             return -1;
   1043         } else if (*s1 > *s2) {
   1044             return 1;
   1045         }
   1046 
   1047         s1++;
   1048         s2++;
   1049     }
   1050 
   1051     // both utf8 strings matched
   1052     return 0;
   1053 }
   1054 
   1055 void *utf8ncpy(void *utf8_restrict dst, const void *utf8_restrict src,
   1056                size_t n)
   1057 {
   1058     char *d = (char *)dst;
   1059     const char *s = (const char *)src;
   1060     size_t index;
   1061 
   1062     // overwriting anything previously in dst, write byte-by-byte
   1063     // from src
   1064     for (index = 0; index < n; index++) {
   1065         d[index] = s[index];
   1066         if ('\0' == s[index]) {
   1067             break;
   1068         }
   1069     }
   1070 
   1071     // append null terminating byte
   1072     for (; index < n; index++) {
   1073         d[index] = 0;
   1074     }
   1075 
   1076     return dst;
   1077 }
   1078 
   1079 void *utf8ndup(const void *src, size_t n)
   1080 {
   1081     const char *s = (const char *)src;
   1082     char *c = utf8_null;
   1083     size_t bytes = 0;
   1084 
   1085     // Find the end of the string or stop when n is reached
   1086     while ('\0' != s[bytes] && bytes < n) {
   1087         bytes++;
   1088     }
   1089 
   1090     // In case bytes is actually less than n, we need to set it
   1091     // to be used later in the copy byte by byte.
   1092     n = bytes;
   1093 
   1094     c = (char *)malloc(bytes + 1);
   1095     if (utf8_null == c) {
   1096         // out of memory so we bail
   1097         return utf8_null;
   1098     }
   1099 
   1100     bytes = 0;
   1101 
   1102     // copy src byte-by-byte into our new utf8 string
   1103     while ('\0' != s[bytes] && bytes < n) {
   1104         c[bytes] = s[bytes];
   1105         bytes++;
   1106     }
   1107 
   1108     // append null terminating byte
   1109     c[bytes] = '\0';
   1110     return c;
   1111 }
   1112 
   1113 void *utf8rchr(const void *src, int chr)
   1114 {
   1115     const char *s = (const char *)src;
   1116     const char *match = utf8_null;
   1117     char c[5] = {'\0', '\0', '\0', '\0', '\0'};
   1118 
   1119     if (0 == chr) {
   1120         // being asked to return position of null terminating byte, so
   1121         // just run s to the end, and return!
   1122         while ('\0' != *s) {
   1123             s++;
   1124         }
   1125         return (void *)s;
   1126     } else if (0 == ((int)0xffffff80 & chr)) {
   1127         // 1-byte/7-bit ascii
   1128         // (0b0xxxxxxx)
   1129         c[0] = (char)chr;
   1130     } else if (0 == ((int)0xfffff800 & chr)) {
   1131         // 2-byte/11-bit utf8 code point
   1132         // (0b110xxxxx 0b10xxxxxx)
   1133         c[0] = 0xc0 | (char)(chr >> 6);
   1134         c[1] = 0x80 | (char)(chr & 0x3f);
   1135     } else if (0 == ((int)0xffff0000 & chr)) {
   1136         // 3-byte/16-bit utf8 code point
   1137         // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
   1138         c[0] = 0xe0 | (char)(chr >> 12);
   1139         c[1] = 0x80 | (char)((chr >> 6) & 0x3f);
   1140         c[2] = 0x80 | (char)(chr & 0x3f);
   1141     } else { // if (0 == ((int)0xffe00000 & chr)) {
   1142         // 4-byte/21-bit utf8 code point
   1143         // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
   1144         c[0] = 0xf0 | (char)(chr >> 18);
   1145         c[1] = 0x80 | (char)((chr >> 12) & 0x3f);
   1146         c[2] = 0x80 | (char)((chr >> 6) & 0x3f);
   1147         c[3] = 0x80 | (char)(chr & 0x3f);
   1148     }
   1149 
   1150     // we've created a 2 utf8 codepoint string in c that is
   1151     // the utf8 character asked for by chr, and a null
   1152     // terminating byte
   1153 
   1154     while ('\0' != *s) {
   1155         size_t offset = 0;
   1156 
   1157         while (s[offset] == c[offset]) {
   1158             offset++;
   1159         }
   1160 
   1161         if ('\0' == c[offset]) {
   1162             // we found a matching utf8 code point
   1163             match = s;
   1164             s += offset;
   1165         } else {
   1166             s += offset;
   1167 
   1168             // need to march s along to next utf8 codepoint start
   1169             // (the next byte that doesn't match 0b10xxxxxx)
   1170             if ('\0' != *s) {
   1171                 do {
   1172                     s++;
   1173                 } while (0x80 == (0xc0 & *s));
   1174             }
   1175         }
   1176     }
   1177 
   1178     // return the last match we found (or 0 if no match was found)
   1179     return (void *)match;
   1180 }
   1181 
   1182 void *utf8pbrk(const void *str, const void *accept)
   1183 {
   1184     const char *s = (const char *)str;
   1185 
   1186     while ('\0' != *s) {
   1187         const char *a = (const char *)accept;
   1188         size_t offset = 0;
   1189 
   1190         while ('\0' != *a) {
   1191             // checking that if *a is the start of a utf8 codepoint
   1192             // (it is not 0b10xxxxxx) and we have successfully matched
   1193             // a previous character (0 < offset) - we found a match
   1194             if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
   1195                 return (void *)s;
   1196             } else {
   1197                 if (*a == s[offset]) {
   1198                     // part of a utf8 codepoint matched, so move our checking
   1199                     // onwards to the next byte
   1200                     offset++;
   1201                     a++;
   1202                 } else {
   1203                     // r could be in the middle of an unmatching utf8 code point,
   1204                     // so we need to march it on to the next character beginning,
   1205 
   1206                     do {
   1207                         a++;
   1208                     } while (0x80 == (0xc0 & *a));
   1209 
   1210                     // reset offset too as we found a mismatch
   1211                     offset = 0;
   1212                 }
   1213             }
   1214         }
   1215 
   1216         // we found a match on the last utf8 codepoint
   1217         if (0 < offset) {
   1218             return (void *)s;
   1219         }
   1220 
   1221         // the current utf8 codepoint in src did not match accept, but src
   1222         // could have been partway through a utf8 codepoint, so we need to
   1223         // march it onto the next utf8 codepoint starting byte
   1224         do {
   1225             s++;
   1226         } while ((0x80 == (0xc0 & *s)));
   1227     }
   1228 
   1229     return utf8_null;
   1230 }
   1231 
   1232 size_t utf8size(const void *str)
   1233 {
   1234     const char *s = (const char *)str;
   1235     size_t size = 0;
   1236     while ('\0' != s[size]) {
   1237         size++;
   1238     }
   1239 
   1240     // we are including the null terminating byte in the size calculation
   1241     size++;
   1242     return size;
   1243 }
   1244 
   1245 size_t utf8spn(const void *src, const void *accept)
   1246 {
   1247     const char *s = (const char *)src;
   1248     size_t chars = 0;
   1249 
   1250     while ('\0' != *s) {
   1251         const char *a = (const char *)accept;
   1252         size_t offset = 0;
   1253 
   1254         while ('\0' != *a) {
   1255             // checking that if *r is the start of a utf8 codepoint
   1256             // (it is not 0b10xxxxxx) and we have successfully matched
   1257             // a previous character (0 < offset) - we found a match
   1258             if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
   1259                 // found a match, so increment the number of utf8 codepoints
   1260                 // that have matched and stop checking whether any other utf8
   1261                 // codepoints in a match
   1262                 chars++;
   1263                 s += offset;
   1264                 break;
   1265             } else {
   1266                 if (*a == s[offset]) {
   1267                     offset++;
   1268                     a++;
   1269                 } else {
   1270                     // a could be in the middle of an unmatching utf8 codepoint,
   1271                     // so we need to march it on to the next character beginning,
   1272                     do {
   1273                         a++;
   1274                     } while (0x80 == (0xc0 & *a));
   1275 
   1276                     // reset offset too as we found a mismatch
   1277                     offset = 0;
   1278                 }
   1279             }
   1280         }
   1281 
   1282         // if a got to its terminating null byte, then we didn't find a match.
   1283         // Return the current number of matched utf8 codepoints
   1284         if ('\0' == *a) {
   1285             return chars;
   1286         }
   1287     }
   1288 
   1289     return chars;
   1290 }
   1291 
   1292 void *utf8str(const void *haystack, const void *needle)
   1293 {
   1294     const char *h = (const char *)haystack;
   1295     utf8_int32_t throwaway_codepoint;
   1296 
   1297     // if needle has no utf8 codepoints before the null terminating
   1298     // byte then return haystack
   1299     if ('\0' == *((const char *)needle)) {
   1300         return (void *)haystack;
   1301     }
   1302 
   1303     while ('\0' != *h) {
   1304         const char *maybeMatch = h;
   1305         const char *n = (const char *)needle;
   1306 
   1307         while (*h == *n && (*h != '\0' && *n != '\0')) {
   1308             n++;
   1309             h++;
   1310         }
   1311 
   1312         if ('\0' == *n) {
   1313             // we found the whole utf8 string for needle in haystack at
   1314             // maybeMatch, so return it
   1315             return (void *)maybeMatch;
   1316         } else {
   1317             // h could be in the middle of an unmatching utf8 codepoint,
   1318             // so we need to march it on to the next character beginning
   1319             // starting from the current character
   1320             h = (const char *)utf8codepoint(maybeMatch, &throwaway_codepoint);
   1321         }
   1322     }
   1323 
   1324     // no match
   1325     return utf8_null;
   1326 }
   1327 
   1328 void *utf8casestr(const void *haystack, const void *needle)
   1329 {
   1330     const void *h = haystack;
   1331 
   1332     // if needle has no utf8 codepoints before the null terminating
   1333     // byte then return haystack
   1334     if ('\0' == *((const char *)needle)) {
   1335         return (void *)haystack;
   1336     }
   1337 
   1338     for (;;) {
   1339         const void *maybeMatch = h;
   1340         const void *n = needle;
   1341         utf8_int32_t h_cp, n_cp;
   1342 
   1343         // Get the next code point and track it
   1344         const void *nextH = h = utf8codepoint(h, &h_cp);
   1345         n = utf8codepoint(n, &n_cp);
   1346 
   1347         while ((0 != h_cp) && (0 != n_cp)) {
   1348             h_cp = utf8lwrcodepoint(h_cp);
   1349             n_cp = utf8lwrcodepoint(n_cp);
   1350 
   1351             // if we find a mismatch, bail out!
   1352             if (h_cp != n_cp) {
   1353                 break;
   1354             }
   1355 
   1356             h = utf8codepoint(h, &h_cp);
   1357             n = utf8codepoint(n, &n_cp);
   1358         }
   1359 
   1360         if (0 == n_cp) {
   1361             // we found the whole utf8 string for needle in haystack at
   1362             // maybeMatch, so return it
   1363             return (void *)maybeMatch;
   1364         }
   1365 
   1366         if (0 == h_cp) {
   1367             // no match
   1368             return utf8_null;
   1369         }
   1370 
   1371         // Roll back to the next code point in the haystack to test
   1372         h = nextH;
   1373     }
   1374 }
   1375 
   1376 void *utf8valid(const void *str)
   1377 {
   1378     const char *s = (const char *)str;
   1379 
   1380     while ('\0' != *s) {
   1381         if (0xf0 == (0xf8 & *s)) {
   1382             // ensure each of the 3 following bytes in this 4-byte
   1383             // utf8 codepoint began with 0b10xxxxxx
   1384             if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2])) ||
   1385                 (0x80 != (0xc0 & s[3]))) {
   1386                 return (void *)s;
   1387             }
   1388 
   1389             // ensure that our utf8 codepoint ended after 4 bytes
   1390             if (0x80 == (0xc0 & s[4])) {
   1391                 return (void *)s;
   1392             }
   1393 
   1394             // ensure that the top 5 bits of this 4-byte utf8
   1395             // codepoint were not 0, as then we could have used
   1396             // one of the smaller encodings
   1397             if ((0 == (0x07 & s[0])) && (0 == (0x30 & s[1]))) {
   1398                 return (void *)s;
   1399             }
   1400 
   1401             // 4-byte utf8 code point (began with 0b11110xxx)
   1402             s += 4;
   1403         } else if (0xe0 == (0xf0 & *s)) {
   1404             // ensure each of the 2 following bytes in this 3-byte
   1405             // utf8 codepoint began with 0b10xxxxxx
   1406             if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2]))) {
   1407                 return (void *)s;
   1408             }
   1409 
   1410             // ensure that our utf8 codepoint ended after 3 bytes
   1411             if (0x80 == (0xc0 & s[3])) {
   1412                 return (void *)s;
   1413             }
   1414 
   1415             // ensure that the top 5 bits of this 3-byte utf8
   1416             // codepoint were not 0, as then we could have used
   1417             // one of the smaller encodings
   1418             if ((0 == (0x0f & s[0])) && (0 == (0x20 & s[1]))) {
   1419                 return (void *)s;
   1420             }
   1421 
   1422             // 3-byte utf8 code point (began with 0b1110xxxx)
   1423             s += 3;
   1424         } else if (0xc0 == (0xe0 & *s)) {
   1425             // ensure the 1 following byte in this 2-byte
   1426             // utf8 codepoint began with 0b10xxxxxx
   1427             if (0x80 != (0xc0 & s[1])) {
   1428                 return (void *)s;
   1429             }
   1430 
   1431             // ensure that our utf8 codepoint ended after 2 bytes
   1432             if (0x80 == (0xc0 & s[2])) {
   1433                 return (void *)s;
   1434             }
   1435 
   1436             // ensure that the top 4 bits of this 2-byte utf8
   1437             // codepoint were not 0, as then we could have used
   1438             // one of the smaller encodings
   1439             if (0 == (0x1e & s[0])) {
   1440                 return (void *)s;
   1441             }
   1442 
   1443             // 2-byte utf8 code point (began with 0b110xxxxx)
   1444             s += 2;
   1445         } else if (0x00 == (0x80 & *s)) {
   1446             // 1-byte ascii (began with 0b0xxxxxxx)
   1447             s += 1;
   1448         } else {
   1449             // we have an invalid 0b1xxxxxxx utf8 code point entry
   1450             return (void *)s;
   1451         }
   1452     }
   1453 
   1454     return utf8_null;
   1455 }
   1456 
   1457 void *utf8codepoint(const void *utf8_restrict str,
   1458                     utf8_int32_t *utf8_restrict out_codepoint)
   1459 {
   1460     const char *s = (const char *)str;
   1461 
   1462     if (0xf0 == (0xf8 & s[0])) {
   1463         // 4 byte utf8 codepoint
   1464         *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) |
   1465                          ((0x3f & s[2]) << 6) | (0x3f & s[3]);
   1466         s += 4;
   1467     } else if (0xe0 == (0xf0 & s[0])) {
   1468         // 3 byte utf8 codepoint
   1469         *out_codepoint =
   1470             ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
   1471         s += 3;
   1472     } else if (0xc0 == (0xe0 & s[0])) {
   1473         // 2 byte utf8 codepoint
   1474         *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
   1475         s += 2;
   1476     } else {
   1477         // 1 byte utf8 codepoint otherwise
   1478         *out_codepoint = s[0];
   1479         s += 1;
   1480     }
   1481 
   1482     return (void *)s;
   1483 }
   1484 
   1485 size_t utf8codepointsize(utf8_int32_t chr)
   1486 {
   1487     if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
   1488         return 1;
   1489     } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
   1490         return 2;
   1491     } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
   1492         return 3;
   1493     } else { // if (0 == ((int)0xffe00000 & chr)) {
   1494         return 4;
   1495     }
   1496 }
   1497 
   1498 void *utf8catcodepoint(void *utf8_restrict str, utf8_int32_t chr, size_t n)
   1499 {
   1500     char *s = (char *)str;
   1501 
   1502     if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
   1503         // 1-byte/7-bit ascii
   1504         // (0b0xxxxxxx)
   1505         if (n < 1) {
   1506             return utf8_null;
   1507         }
   1508         s[0] = (char)chr;
   1509         s += 1;
   1510     } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
   1511         // 2-byte/11-bit utf8 code point
   1512         // (0b110xxxxx 0b10xxxxxx)
   1513         if (n < 2) {
   1514             return utf8_null;
   1515         }
   1516         s[0] = 0xc0 | (char)(chr >> 6);
   1517         s[1] = 0x80 | (char)(chr & 0x3f);
   1518         s += 2;
   1519     } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
   1520         // 3-byte/16-bit utf8 code point
   1521         // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
   1522         if (n < 3) {
   1523             return utf8_null;
   1524         }
   1525         s[0] = 0xe0 | (char)(chr >> 12);
   1526         s[1] = 0x80 | (char)((chr >> 6) & 0x3f);
   1527         s[2] = 0x80 | (char)(chr & 0x3f);
   1528         s += 3;
   1529     } else { // if (0 == ((int)0xffe00000 & chr)) {
   1530         // 4-byte/21-bit utf8 code point
   1531         // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
   1532         if (n < 4) {
   1533             return utf8_null;
   1534         }
   1535         s[0] = 0xf0 | (char)(chr >> 18);
   1536         s[1] = 0x80 | (char)((chr >> 12) & 0x3f);
   1537         s[2] = 0x80 | (char)((chr >> 6) & 0x3f);
   1538         s[3] = 0x80 | (char)(chr & 0x3f);
   1539         s += 4;
   1540     }
   1541 
   1542     return s;
   1543 }
   1544 
   1545 int utf8islower(utf8_int32_t chr) { return chr != utf8uprcodepoint(chr); }
   1546 
   1547 int utf8isupper(utf8_int32_t chr) { return chr != utf8lwrcodepoint(chr); }
   1548 
   1549 void utf8lwr(void *utf8_restrict str)
   1550 {
   1551     void *p, *pn;
   1552     utf8_int32_t cp;
   1553 
   1554     p = (char *)str;
   1555     pn = utf8codepoint(p, &cp);
   1556 
   1557     while (cp != 0) {
   1558         const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp);
   1559         const size_t size = utf8codepointsize(lwr_cp);
   1560 
   1561         if (lwr_cp != cp) {
   1562             utf8catcodepoint(p, lwr_cp, size);
   1563         }
   1564 
   1565         p = pn;
   1566         pn = utf8codepoint(p, &cp);
   1567     }
   1568 }
   1569 
   1570 void utf8upr(void *utf8_restrict str)
   1571 {
   1572     void *p, *pn;
   1573     utf8_int32_t cp;
   1574 
   1575     p = (char *)str;
   1576     pn = utf8codepoint(p, &cp);
   1577 
   1578     while (cp != 0) {
   1579         const utf8_int32_t lwr_cp = utf8uprcodepoint(cp);
   1580         const size_t size = utf8codepointsize(lwr_cp);
   1581 
   1582         if (lwr_cp != cp) {
   1583             utf8catcodepoint(p, lwr_cp, size);
   1584         }
   1585 
   1586         p = pn;
   1587         pn = utf8codepoint(p, &cp);
   1588     }
   1589 }
   1590 
   1591 utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp)
   1592 {
   1593     if (((0x0041 <= cp) && (0x005a >= cp)) ||
   1594         ((0x00c0 <= cp) && (0x00d6 >= cp)) ||
   1595         ((0x00d8 <= cp) && (0x00de >= cp)) ||
   1596         ((0x0391 <= cp) && (0x03a1 >= cp)) ||
   1597         ((0x03a3 <= cp) && (0x03ab >= cp))) {
   1598         cp += 32;
   1599     } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
   1600                ((0x0132 <= cp) && (0x0137 >= cp)) ||
   1601                ((0x014a <= cp) && (0x0177 >= cp)) ||
   1602                ((0x0182 <= cp) && (0x0185 >= cp)) ||
   1603                ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
   1604                ((0x01de <= cp) && (0x01ef >= cp)) ||
   1605                ((0x01f8 <= cp) && (0x021f >= cp)) ||
   1606                ((0x0222 <= cp) && (0x0233 >= cp)) ||
   1607                ((0x0246 <= cp) && (0x024f >= cp)) ||
   1608                ((0x03d8 <= cp) && (0x03ef >= cp))) {
   1609         cp |= 0x1;
   1610     } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
   1611                ((0x0179 <= cp) && (0x017e >= cp)) ||
   1612                ((0x01af <= cp) && (0x01b0 >= cp)) ||
   1613                ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
   1614                ((0x01cd <= cp) && (0x01dc >= cp))) {
   1615         cp += 1;
   1616         cp &= ~0x1;
   1617     } else {
   1618         switch (cp) {
   1619             default: break;
   1620             case 0x0178: cp = 0x00ff; break;
   1621             case 0x0243: cp = 0x0180; break;
   1622             case 0x018e: cp = 0x01dd; break;
   1623             case 0x023d: cp = 0x019a; break;
   1624             case 0x0220: cp = 0x019e; break;
   1625             case 0x01b7: cp = 0x0292; break;
   1626             case 0x01c4: cp = 0x01c6; break;
   1627             case 0x01c7: cp = 0x01c9; break;
   1628             case 0x01ca: cp = 0x01cc; break;
   1629             case 0x01f1: cp = 0x01f3; break;
   1630             case 0x01f7: cp = 0x01bf; break;
   1631             case 0x0187: cp = 0x0188; break;
   1632             case 0x018b: cp = 0x018c; break;
   1633             case 0x0191: cp = 0x0192; break;
   1634             case 0x0198: cp = 0x0199; break;
   1635             case 0x01a7: cp = 0x01a8; break;
   1636             case 0x01ac: cp = 0x01ad; break;
   1637             case 0x01af: cp = 0x01b0; break;
   1638             case 0x01b8: cp = 0x01b9; break;
   1639             case 0x01bc: cp = 0x01bd; break;
   1640             case 0x01f4: cp = 0x01f5; break;
   1641             case 0x023b: cp = 0x023c; break;
   1642             case 0x0241: cp = 0x0242; break;
   1643             case 0x03fd: cp = 0x037b; break;
   1644             case 0x03fe: cp = 0x037c; break;
   1645             case 0x03ff: cp = 0x037d; break;
   1646             case 0x037f: cp = 0x03f3; break;
   1647             case 0x0386: cp = 0x03ac; break;
   1648             case 0x0388: cp = 0x03ad; break;
   1649             case 0x0389: cp = 0x03ae; break;
   1650             case 0x038a: cp = 0x03af; break;
   1651             case 0x038c: cp = 0x03cc; break;
   1652             case 0x038e: cp = 0x03cd; break;
   1653             case 0x038f: cp = 0x03ce; break;
   1654             case 0x0370: cp = 0x0371; break;
   1655             case 0x0372: cp = 0x0373; break;
   1656             case 0x0376: cp = 0x0377; break;
   1657             case 0x03f4: cp = 0x03d1; break;
   1658             case 0x03cf: cp = 0x03d7; break;
   1659             case 0x03f9: cp = 0x03f2; break;
   1660             case 0x03f7: cp = 0x03f8; break;
   1661             case 0x03fa: cp = 0x03fb; break;
   1662         };
   1663     }
   1664 
   1665     return cp;
   1666 }
   1667 
   1668 utf8_int32_t utf8uprcodepoint(utf8_int32_t cp)
   1669 {
   1670     if (((0x0061 <= cp) && (0x007a >= cp)) ||
   1671         ((0x00e0 <= cp) && (0x00f6 >= cp)) ||
   1672         ((0x00f8 <= cp) && (0x00fe >= cp)) ||
   1673         ((0x03b1 <= cp) && (0x03c1 >= cp)) ||
   1674         ((0x03c3 <= cp) && (0x03cb >= cp))) {
   1675         cp -= 32;
   1676     } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
   1677                ((0x0132 <= cp) && (0x0137 >= cp)) ||
   1678                ((0x014a <= cp) && (0x0177 >= cp)) ||
   1679                ((0x0182 <= cp) && (0x0185 >= cp)) ||
   1680                ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
   1681                ((0x01de <= cp) && (0x01ef >= cp)) ||
   1682                ((0x01f8 <= cp) && (0x021f >= cp)) ||
   1683                ((0x0222 <= cp) && (0x0233 >= cp)) ||
   1684                ((0x0246 <= cp) && (0x024f >= cp)) ||
   1685                ((0x03d8 <= cp) && (0x03ef >= cp))) {
   1686         cp &= ~0x1;
   1687     } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
   1688                ((0x0179 <= cp) && (0x017e >= cp)) ||
   1689                ((0x01af <= cp) && (0x01b0 >= cp)) ||
   1690                ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
   1691                ((0x01cd <= cp) && (0x01dc >= cp))) {
   1692         cp -= 1;
   1693         cp |= 0x1;
   1694     } else {
   1695         switch (cp) {
   1696             default: break;
   1697             case 0x00ff: cp = 0x0178; break;
   1698             case 0x0180: cp = 0x0243; break;
   1699             case 0x01dd: cp = 0x018e; break;
   1700             case 0x019a: cp = 0x023d; break;
   1701             case 0x019e: cp = 0x0220; break;
   1702             case 0x0292: cp = 0x01b7; break;
   1703             case 0x01c6: cp = 0x01c4; break;
   1704             case 0x01c9: cp = 0x01c7; break;
   1705             case 0x01cc: cp = 0x01ca; break;
   1706             case 0x01f3: cp = 0x01f1; break;
   1707             case 0x01bf: cp = 0x01f7; break;
   1708             case 0x0188: cp = 0x0187; break;
   1709             case 0x018c: cp = 0x018b; break;
   1710             case 0x0192: cp = 0x0191; break;
   1711             case 0x0199: cp = 0x0198; break;
   1712             case 0x01a8: cp = 0x01a7; break;
   1713             case 0x01ad: cp = 0x01ac; break;
   1714             case 0x01b0: cp = 0x01af; break;
   1715             case 0x01b9: cp = 0x01b8; break;
   1716             case 0x01bd: cp = 0x01bc; break;
   1717             case 0x01f5: cp = 0x01f4; break;
   1718             case 0x023c: cp = 0x023b; break;
   1719             case 0x0242: cp = 0x0241; break;
   1720             case 0x037b: cp = 0x03fd; break;
   1721             case 0x037c: cp = 0x03fe; break;
   1722             case 0x037d: cp = 0x03ff; break;
   1723             case 0x03f3: cp = 0x037f; break;
   1724             case 0x03ac: cp = 0x0386; break;
   1725             case 0x03ad: cp = 0x0388; break;
   1726             case 0x03ae: cp = 0x0389; break;
   1727             case 0x03af: cp = 0x038a; break;
   1728             case 0x03cc: cp = 0x038c; break;
   1729             case 0x03cd: cp = 0x038e; break;
   1730             case 0x03ce: cp = 0x038f; break;
   1731             case 0x0371: cp = 0x0370; break;
   1732             case 0x0373: cp = 0x0372; break;
   1733             case 0x0377: cp = 0x0376; break;
   1734             case 0x03d1: cp = 0x03f4; break;
   1735             case 0x03d7: cp = 0x03cf; break;
   1736             case 0x03f2: cp = 0x03f9; break;
   1737             case 0x03f8: cp = 0x03f7; break;
   1738             case 0x03fb: cp = 0x03fa; break;
   1739         };
   1740     }
   1741 
   1742     return cp;
   1743 }
   1744 
   1745 #undef utf8_restrict
   1746 #undef utf8_null
   1747 
   1748 #ifdef __cplusplus
   1749 } // extern "C"
   1750 #endif
   1751 
   1752 #if defined(__clang__)
   1753 #pragma clang diagnostic pop
   1754 #endif
   1755 
   1756 #endif // SHEREDOM_UTF8_H_INCLUDED
   1757 
   1758 
   1759 /********************************************************
   1760    End of file "utf8.h"
   1761  ********************************************************/
   1762 
   1763 
   1764 /********************************************************
   1765    Begin of file "string_buffer.h"
   1766  ********************************************************/
   1767 
   1768 #ifndef STRING_BUFFER_H
   1769 #define STRING_BUFFER_H
   1770 
   1771 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   1772 
   1773 
   1774 /*****************************************************************************
   1775  *               STRING BUFFER
   1776  * ***************************************************************************/
   1777 
   1778 struct f_string_buffer {
   1779     union {
   1780         char *cstr;
   1781 #ifdef FT_HAVE_WCHAR
   1782         wchar_t *wstr;
   1783 #endif
   1784 #ifdef FT_HAVE_UTF8
   1785         void *u8str;
   1786 #endif
   1787         void *data;
   1788     } str;
   1789     size_t data_sz;
   1790     enum f_string_type type;
   1791 };
   1792 
   1793 FT_INTERNAL
   1794 f_string_buffer_t *create_string_buffer(size_t number_of_chars, enum f_string_type type);
   1795 
   1796 FT_INTERNAL
   1797 void destroy_string_buffer(f_string_buffer_t *buffer);
   1798 
   1799 FT_INTERNAL
   1800 f_string_buffer_t *copy_string_buffer(const f_string_buffer_t *buffer);
   1801 
   1802 FT_INTERNAL
   1803 f_status realloc_string_buffer_without_copy(f_string_buffer_t *buffer);
   1804 
   1805 FT_INTERNAL
   1806 f_status fill_buffer_from_string(f_string_buffer_t *buffer, const char *str);
   1807 
   1808 #ifdef FT_HAVE_WCHAR
   1809 FT_INTERNAL
   1810 f_status fill_buffer_from_wstring(f_string_buffer_t *buffer, const wchar_t *str);
   1811 #endif /* FT_HAVE_WCHAR */
   1812 
   1813 #ifdef FT_HAVE_UTF8
   1814 FT_INTERNAL
   1815 f_status fill_buffer_from_u8string(f_string_buffer_t *buffer, const void *str);
   1816 #endif /* FT_HAVE_UTF8 */
   1817 
   1818 FT_INTERNAL
   1819 size_t buffer_text_visible_width(const f_string_buffer_t *buffer);
   1820 
   1821 FT_INTERNAL
   1822 size_t buffer_text_visible_height(const f_string_buffer_t *buffer);
   1823 
   1824 FT_INTERNAL
   1825 size_t string_buffer_cod_width_capacity(const f_string_buffer_t *buffer);
   1826 
   1827 FT_INTERNAL
   1828 size_t string_buffer_raw_capacity(const f_string_buffer_t *buffer);
   1829 
   1830 FT_INTERNAL
   1831 size_t string_buffer_width_capacity(const f_string_buffer_t *buffer);
   1832 
   1833 FT_INTERNAL
   1834 void *buffer_get_data(f_string_buffer_t *buffer);
   1835 
   1836 FT_INTERNAL
   1837 int buffer_check_align(f_string_buffer_t *buffer);
   1838 
   1839 FT_INTERNAL
   1840 int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t *cntx, size_t cod_width,
   1841                   const char *content_style_tag, const char *reset_content_style_tag);
   1842 
   1843 #ifdef FT_HAVE_UTF8
   1844 FT_INTERNAL
   1845 void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width));
   1846 #endif /* FT_HAVE_UTF8 */
   1847 
   1848 
   1849 #endif /* STRING_BUFFER_H */
   1850 
   1851 /********************************************************
   1852    End of file "string_buffer.h"
   1853  ********************************************************/
   1854 
   1855 
   1856 /********************************************************
   1857    Begin of file "properties.h"
   1858  ********************************************************/
   1859 
   1860 #ifndef PROPERTIES_H
   1861 #define PROPERTIES_H
   1862 
   1863 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   1864 #include <stdint.h>
   1865 #include <limits.h>
   1866 
   1867 #define PROP_IS_SET(ft_props, property) ((ft_props) & (property))
   1868 #define PROP_SET(ft_props, property) ((ft_props) |=(property))
   1869 #define PROP_UNSET(ft_props, property) ((ft_props) &= ~((uint32_t)(property)))
   1870 
   1871 #define TEXT_STYLE_TAG_MAX_SIZE (64 * 2)
   1872 
   1873 FT_INTERNAL
   1874 void get_style_tag_for_cell(const f_table_properties_t *props,
   1875                             size_t row, size_t col, char *style_tag, size_t sz);
   1876 
   1877 FT_INTERNAL
   1878 void get_reset_style_tag_for_cell(const f_table_properties_t *props,
   1879                                   size_t row, size_t col, char *style_tag, size_t sz);
   1880 
   1881 FT_INTERNAL
   1882 void get_style_tag_for_content(const f_table_properties_t *props,
   1883                                size_t row, size_t col, char *style_tag, size_t sz);
   1884 
   1885 FT_INTERNAL
   1886 void get_reset_style_tag_for_content(const f_table_properties_t *props,
   1887                                      size_t row, size_t col, char *style_tag, size_t sz);
   1888 
   1889 
   1890 struct f_cell_props {
   1891     size_t cell_row;
   1892     size_t cell_col;
   1893     uint32_t properties_flags;
   1894 
   1895     unsigned int col_min_width;
   1896     enum ft_text_alignment align;
   1897     unsigned int cell_padding_top;
   1898     unsigned int cell_padding_bottom;
   1899     unsigned int cell_padding_left;
   1900     unsigned int cell_padding_right;
   1901     unsigned int cell_empty_string_height;
   1902     enum ft_row_type row_type;
   1903     unsigned int content_fg_color_number;
   1904     unsigned int content_bg_color_number;
   1905     unsigned int cell_bg_color_number;
   1906     enum ft_text_style cell_text_style;
   1907     enum ft_text_style content_text_style;
   1908     bool rgb;
   1909 };
   1910 
   1911 typedef struct f_cell_props f_cell_props_t;
   1912 typedef f_vector_t f_cell_prop_container_t;
   1913 
   1914 FT_INTERNAL
   1915 f_cell_prop_container_t *create_cell_prop_container(void);
   1916 
   1917 FT_INTERNAL
   1918 void destroy_cell_prop_container(f_cell_prop_container_t *cont);
   1919 
   1920 FT_INTERNAL
   1921 const f_cell_props_t *cget_cell_prop(const f_cell_prop_container_t *cont, size_t row, size_t col);
   1922 
   1923 FT_INTERNAL
   1924 f_cell_props_t *get_cell_prop_and_create_if_not_exists(f_cell_prop_container_t *cont, size_t row, size_t col);
   1925 
   1926 FT_INTERNAL
   1927 f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t col, uint32_t property, int value);
   1928 
   1929 FT_INTERNAL
   1930 int get_cell_property_hierarchically(const f_table_properties_t *properties, size_t row, size_t column, uint32_t property);
   1931 
   1932 FT_INTERNAL
   1933 f_status set_default_cell_property(uint32_t property, int value);
   1934 
   1935 
   1936 /*         TABLE BORDER DESСRIPTION
   1937  *
   1938  *
   1939  *   TL TT TT TT TV TT TT TT TT TT TT TT TR
   1940  *   LL          IV                      RR
   1941  *   LL          IV                      RR
   1942  *   LH IH IH IH II IH IH IH TI IH IH IH RH
   1943  *   LL          IV          IV          RR
   1944  *   LL          IV          IV          RR
   1945  *   LL          LI IH IH IH RI          RH
   1946  *   LL          IV          IV          RR
   1947  *   LL          IV          IV          RR
   1948  *   LH IH IH IH BI IH IH IH II IH IH IH RH
   1949  *   LL                      IV          RR
   1950  *   LL                      IV          RR
   1951  *   BL BB BB BB BV BB BB BB BV BB BB BB BR
   1952  */
   1953 
   1954 
   1955 /*      HORIZONTAL SEPARATOR DESCRIPTION
   1956  *
   1957  *
   1958  *   TL TT TT TT TV TT TT TT TV TT TT TT TR        <----- TOP_SEPARATOR
   1959  *   LL          IV          IV          RR
   1960  *   LH IH IH IH II IH IH IH II IH IH IH RH        <----- INSIDE_SEPARATOR
   1961  *   LL          IV          IV          RR
   1962  *   BL BB BB BB BV BB BB BB BV BB BB BB BR        <----- BOTTOM_SEPARATOR
   1963  */
   1964 
   1965 enum f_hor_separator_pos {
   1966     TOP_SEPARATOR,
   1967     INSIDE_SEPARATOR,
   1968     BOTTOM_SEPARATOR
   1969 };
   1970 
   1971 enum f_border_item_pos {
   1972     TL_bip = 0,
   1973     TT_bip = 1,
   1974     TV_bip = 2,
   1975     TR_bip = 3,
   1976 
   1977     LL_bip = 4,
   1978     IV_bip = 5,
   1979     RR_bip = 6,
   1980 
   1981     LH_bip = 7,
   1982     IH_bip = 8,
   1983     II_bip = 9,
   1984     RH_bip = 10,
   1985 
   1986     BL_bip = 11,
   1987     BB_bip = 12,
   1988     BV_bip = 13,
   1989     BR_bip = 14,
   1990 
   1991     LI_bip = 15,
   1992     TI_bip = 16,
   1993     RI_bip = 17,
   1994     BI_bip = 18,
   1995 
   1996     BORDER_ITEM_POS_SIZE
   1997 };
   1998 
   1999 
   2000 enum f_separator_item_pos {
   2001     LH_sip = 0,
   2002     IH_sip = 1,
   2003     II_sip = 2,
   2004     RH_sip = 3,
   2005 
   2006     TI_sip = 4,
   2007     BI_sip = 5,
   2008 
   2009     SEPARATOR_ITEM_POS_SIZE
   2010 };
   2011 
   2012 
   2013 struct fort_border_style {
   2014     const char *border_chars[BORDER_ITEM_POS_SIZE];
   2015     const char *header_border_chars[BORDER_ITEM_POS_SIZE];
   2016     const char *separator_chars[SEPARATOR_ITEM_POS_SIZE];
   2017 };
   2018 extern struct fort_border_style FORT_BASIC_STYLE;
   2019 extern struct fort_border_style FORT_BASIC2_STYLE;
   2020 extern struct fort_border_style FORT_SIMPLE_STYLE;
   2021 extern struct fort_border_style FORT_PLAIN_STYLE;
   2022 extern struct fort_border_style FORT_DOT_STYLE;
   2023 extern struct fort_border_style FORT_EMPTY_STYLE;
   2024 extern struct fort_border_style FORT_EMPTY2_STYLE;
   2025 extern struct fort_border_style FORT_SOLID_STYLE;
   2026 extern struct fort_border_style FORT_SOLID_ROUND_STYLE;
   2027 extern struct fort_border_style FORT_NICE_STYLE;
   2028 extern struct fort_border_style FORT_DOUBLE_STYLE;
   2029 extern struct fort_border_style FORT_DOUBLE2_STYLE;
   2030 extern struct fort_border_style FORT_BOLD_STYLE;
   2031 extern struct fort_border_style FORT_BOLD2_STYLE;
   2032 extern struct fort_border_style FORT_FRAME_STYLE;
   2033 
   2034 
   2035 struct fort_entire_table_properties {
   2036     unsigned int left_margin;
   2037     unsigned int top_margin;
   2038     unsigned int right_margin;
   2039     unsigned int bottom_margin;
   2040     enum ft_adding_strategy add_strategy;
   2041 };
   2042 typedef struct fort_entire_table_properties fort_entire_table_properties_t;
   2043 extern fort_entire_table_properties_t g_entire_table_properties;
   2044 
   2045 FT_INTERNAL
   2046 f_status set_entire_table_property(f_table_properties_t *table_properties, uint32_t property, int value);
   2047 
   2048 FT_INTERNAL
   2049 f_status set_default_entire_table_property(uint32_t property, int value);
   2050 
   2051 struct f_table_properties {
   2052     struct fort_border_style border_style;
   2053     f_cell_prop_container_t *cell_properties;
   2054     fort_entire_table_properties_t entire_table_properties;
   2055 };
   2056 extern f_table_properties_t g_table_properties;
   2057 
   2058 FT_INTERNAL
   2059 size_t max_border_elem_strlen(struct f_table_properties *);
   2060 
   2061 FT_INTERNAL
   2062 f_table_properties_t *create_table_properties(void);
   2063 
   2064 FT_INTERNAL
   2065 void destroy_table_properties(f_table_properties_t *properties);
   2066 
   2067 FT_INTERNAL
   2068 f_table_properties_t *copy_table_properties(const f_table_properties_t *property);
   2069 
   2070 #endif /* PROPERTIES_H */
   2071 
   2072 /********************************************************
   2073    End of file "properties.h"
   2074  ********************************************************/
   2075 
   2076 
   2077 /********************************************************
   2078    Begin of file "cell.h"
   2079  ********************************************************/
   2080 
   2081 #ifndef CELL_H
   2082 #define CELL_H
   2083 
   2084 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   2085 
   2086 FT_INTERNAL
   2087 f_cell_t *create_cell(void);
   2088 
   2089 FT_INTERNAL
   2090 void destroy_cell(f_cell_t *cell);
   2091 
   2092 FT_INTERNAL
   2093 f_cell_t *copy_cell(f_cell_t *cell);
   2094 
   2095 FT_INTERNAL
   2096 size_t cell_vis_width(const f_cell_t *cell, const f_context_t *context);
   2097 
   2098 FT_INTERNAL
   2099 size_t cell_invis_codes_width(const f_cell_t *cell, const f_context_t *context);
   2100 
   2101 FT_INTERNAL
   2102 size_t hint_height_cell(const f_cell_t *cell, const f_context_t *context);
   2103 
   2104 FT_INTERNAL
   2105 void set_cell_type(f_cell_t *cell, enum f_cell_type type);
   2106 
   2107 FT_INTERNAL
   2108 enum f_cell_type get_cell_type(const f_cell_t *cell);
   2109 
   2110 FT_INTERNAL
   2111 int cell_printf(f_cell_t *cell, size_t row, f_conv_context_t *cntx, size_t cod_width);
   2112 
   2113 FT_INTERNAL
   2114 f_status fill_cell_from_string(f_cell_t *cell, const char *str);
   2115 
   2116 #ifdef FT_HAVE_WCHAR
   2117 FT_INTERNAL
   2118 f_status fill_cell_from_wstring(f_cell_t *cell, const wchar_t *str);
   2119 #endif
   2120 
   2121 FT_INTERNAL
   2122 f_status fill_cell_from_buffer(f_cell_t *cell, const f_string_buffer_t *buf);
   2123 
   2124 FT_INTERNAL
   2125 f_string_buffer_t *cell_get_string_buffer(f_cell_t *cell);
   2126 
   2127 #endif /* CELL_H */
   2128 
   2129 /********************************************************
   2130    End of file "cell.h"
   2131  ********************************************************/
   2132 
   2133 
   2134 /********************************************************
   2135    Begin of file "row.h"
   2136  ********************************************************/
   2137 
   2138 #ifndef ROW_H
   2139 #define ROW_H
   2140 
   2141 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   2142 #include "fort.h"
   2143 #include <stdarg.h>
   2144 /* #include "properties.h" */ /* Commented by amalgamation script */
   2145 #ifdef FT_HAVE_WCHAR
   2146 #include <wchar.h>
   2147 #endif
   2148 
   2149 FT_INTERNAL
   2150 f_row_t *create_row(void);
   2151 
   2152 FT_INTERNAL
   2153 void destroy_row(f_row_t *row);
   2154 
   2155 FT_INTERNAL
   2156 f_row_t *copy_row(f_row_t *row);
   2157 
   2158 FT_INTERNAL
   2159 f_row_t *split_row(f_row_t *row, size_t pos);
   2160 
   2161 // Delete range [left; right] of cells (both ends included)
   2162 FT_INTERNAL
   2163 int ft_row_erase_range(f_row_t *row, size_t left, size_t right);
   2164 
   2165 FT_INTERNAL
   2166 f_row_t *create_row_from_string(const char *str);
   2167 
   2168 FT_INTERNAL
   2169 f_row_t *create_row_from_fmt_string(const struct f_string_view  *fmt, va_list *va_args);
   2170 
   2171 FT_INTERNAL
   2172 size_t columns_in_row(const f_row_t *row);
   2173 
   2174 FT_INTERNAL
   2175 f_cell_t *get_cell(f_row_t *row, size_t col);
   2176 
   2177 FT_INTERNAL
   2178 const f_cell_t *get_cell_c(const f_row_t *row, size_t col);
   2179 
   2180 FT_INTERNAL
   2181 f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col);
   2182 
   2183 FT_INTERNAL
   2184 f_cell_t *create_cell_in_position(f_row_t *row, size_t col);
   2185 
   2186 FT_INTERNAL
   2187 f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
   2188 
   2189 FT_INTERNAL
   2190 f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
   2191 
   2192 FT_INTERNAL
   2193 size_t group_cell_number(const f_row_t *row, size_t master_cell_col);
   2194 
   2195 FT_INTERNAL
   2196 int get_row_cell_types(const f_row_t *row, enum f_cell_type *types, size_t types_sz);
   2197 
   2198 FT_INTERNAL
   2199 f_status row_set_cell_span(f_row_t *row, size_t cell_column, size_t hor_span);
   2200 
   2201 FT_INTERNAL
   2202 int print_row_separator(f_conv_context_t *cntx,
   2203                         const size_t *col_width_arr, size_t cols,
   2204                         const f_row_t *upper_row, const f_row_t *lower_row,
   2205                         enum f_hor_separator_pos separatorPos, const f_separator_t *sep);
   2206 
   2207 FT_INTERNAL
   2208 int snprintf_row(const f_row_t *row, f_conv_context_t *cntx, size_t *col_width_arr, size_t col_width_arr_sz,
   2209                  size_t row_height);
   2210 
   2211 #ifdef FT_HAVE_WCHAR
   2212 FT_INTERNAL
   2213 f_row_t *create_row_from_wstring(const wchar_t *str);
   2214 #endif
   2215 
   2216 
   2217 #endif /* ROW_H */
   2218 
   2219 /********************************************************
   2220    End of file "row.h"
   2221  ********************************************************/
   2222 
   2223 
   2224 /********************************************************
   2225    Begin of file "table.h"
   2226  ********************************************************/
   2227 
   2228 #ifndef TABLE_H
   2229 #define TABLE_H
   2230 
   2231 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   2232 
   2233 struct ft_table {
   2234     f_vector_t *rows;
   2235     f_table_properties_t *properties;
   2236     f_string_buffer_t *conv_buffer;
   2237     size_t cur_row;
   2238     size_t cur_col;
   2239     f_vector_t *separators;
   2240 };
   2241 
   2242 FT_INTERNAL
   2243 f_separator_t *create_separator(int enabled);
   2244 
   2245 FT_INTERNAL
   2246 void destroy_separator(f_separator_t *sep);
   2247 
   2248 FT_INTERNAL
   2249 f_separator_t *copy_separator(f_separator_t *sep);
   2250 
   2251 FT_INTERNAL
   2252 f_status get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols);
   2253 
   2254 FT_INTERNAL
   2255 f_row_t *get_row(ft_table_t *table, size_t row);
   2256 
   2257 FT_INTERNAL
   2258 const f_row_t *get_row_c(const ft_table_t *table, size_t row);
   2259 
   2260 FT_INTERNAL
   2261 f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row);
   2262 
   2263 FT_INTERNAL
   2264 f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table);
   2265 
   2266 
   2267 FT_INTERNAL
   2268 f_status table_rows_and_cols_geometry(const ft_table_t *table,
   2269                                       size_t **col_width_arr_p, size_t *col_width_arr_sz,
   2270                                       size_t **row_height_arr_p, size_t *row_height_arr_sz,
   2271                                       enum f_geometry_type geom);
   2272 
   2273 FT_INTERNAL
   2274 f_status table_geometry(const ft_table_t *table, size_t *height, size_t *width);
   2275 
   2276 /*
   2277  * Returns geometry in codepoints(characters) (include codepoints of invisible
   2278  * elements: e.g. styles tags).
   2279  */
   2280 FT_INTERNAL
   2281 f_status table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width);
   2282 
   2283 #endif /* TABLE_H */
   2284 
   2285 /********************************************************
   2286    End of file "table.h"
   2287  ********************************************************/
   2288 
   2289 
   2290 /********************************************************
   2291    Begin of file "cell.c"
   2292  ********************************************************/
   2293 
   2294 /* #include "cell.h" */ /* Commented by amalgamation script */
   2295 /* #include "properties.h" */ /* Commented by amalgamation script */
   2296 /* #include "string_buffer.h" */ /* Commented by amalgamation script */
   2297 #include <assert.h>
   2298 
   2299 struct f_cell {
   2300     f_string_buffer_t *str_buffer;
   2301     enum f_cell_type cell_type;
   2302 };
   2303 
   2304 FT_INTERNAL
   2305 f_cell_t *create_cell(void)
   2306 {
   2307     f_cell_t *cell = (f_cell_t *)F_CALLOC(sizeof(f_cell_t), 1);
   2308     if (cell == NULL)
   2309         return NULL;
   2310     cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CHAR_BUF);
   2311     if (cell->str_buffer == NULL) {
   2312         F_FREE(cell);
   2313         return NULL;
   2314     }
   2315     cell->cell_type = COMMON_CELL;
   2316     return cell;
   2317 }
   2318 
   2319 FT_INTERNAL
   2320 void destroy_cell(f_cell_t *cell)
   2321 {
   2322     if (cell == NULL)
   2323         return;
   2324     destroy_string_buffer(cell->str_buffer);
   2325     F_FREE(cell);
   2326 }
   2327 
   2328 FT_INTERNAL
   2329 f_cell_t *copy_cell(f_cell_t *cell)
   2330 {
   2331     assert(cell);
   2332 
   2333     f_cell_t *result = create_cell();
   2334     if (result == NULL)
   2335         return NULL;
   2336     destroy_string_buffer(result->str_buffer);
   2337     result->str_buffer = copy_string_buffer(cell->str_buffer);
   2338     if (result->str_buffer == NULL) {
   2339         destroy_cell(result);
   2340         return NULL;
   2341     }
   2342     result->cell_type = cell->cell_type;
   2343     return result;
   2344 }
   2345 
   2346 FT_INTERNAL
   2347 void set_cell_type(f_cell_t *cell, enum f_cell_type type)
   2348 {
   2349     assert(cell);
   2350     cell->cell_type = type;
   2351 }
   2352 
   2353 FT_INTERNAL
   2354 enum f_cell_type get_cell_type(const f_cell_t *cell)
   2355 {
   2356     assert(cell);
   2357     return cell->cell_type;
   2358 }
   2359 
   2360 FT_INTERNAL
   2361 size_t cell_vis_width(const f_cell_t *cell, const f_context_t *context)
   2362 {
   2363     /* todo:
   2364      * At the moment min width includes paddings. Maybe it is better that min width weren't include
   2365      * paddings but be min width of the cell content without padding
   2366      */
   2367 
   2368     assert(cell);
   2369     assert(context);
   2370 
   2371     f_table_properties_t *properties = context->table_properties;
   2372     size_t row = context->row;
   2373     size_t column = context->column;
   2374 
   2375     size_t padding_left = get_cell_property_hierarchically(properties, row, column, FT_CPROP_LEFT_PADDING);
   2376     size_t padding_right = get_cell_property_hierarchically(properties, row, column, FT_CPROP_RIGHT_PADDING);
   2377     size_t result = padding_left + padding_right;
   2378     if (cell->str_buffer && cell->str_buffer->str.data) {
   2379         result += buffer_text_visible_width(cell->str_buffer);
   2380     }
   2381     result = MAX(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MIN_WIDTH));
   2382     return result;
   2383 }
   2384 
   2385 FT_INTERNAL
   2386 size_t cell_invis_codes_width(const f_cell_t *cell, const f_context_t *context)
   2387 {
   2388     assert(cell);
   2389     assert(context);
   2390 
   2391     f_table_properties_t *properties = context->table_properties;
   2392     size_t row = context->row;
   2393     size_t column = context->column;
   2394 
   2395     size_t result = 0;
   2396     char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2397     get_style_tag_for_cell(properties, row, column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2398     result += strlen(cell_style_tag);
   2399 
   2400     char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2401     get_reset_style_tag_for_cell(properties, row, column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2402     result += strlen(reset_cell_style_tag);
   2403 
   2404     char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2405     get_style_tag_for_content(properties, row, column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2406     result += strlen(content_style_tag);
   2407 
   2408     char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2409     get_reset_style_tag_for_content(properties, row, column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2410     result += strlen(reset_content_style_tag);
   2411     return result;
   2412 }
   2413 
   2414 FT_INTERNAL
   2415 size_t hint_height_cell(const f_cell_t *cell, const f_context_t *context)
   2416 {
   2417     assert(cell);
   2418     assert(context);
   2419     f_table_properties_t *properties = context->table_properties;
   2420     size_t row = context->row;
   2421     size_t column = context->column;
   2422 
   2423     size_t padding_top = get_cell_property_hierarchically(properties, row, column, FT_CPROP_TOP_PADDING);
   2424     size_t padding_bottom = get_cell_property_hierarchically(properties, row, column, FT_CPROP_BOTTOM_PADDING);
   2425     size_t empty_string_height = get_cell_property_hierarchically(properties, row, column, FT_CPROP_EMPTY_STR_HEIGHT);
   2426 
   2427     size_t result = padding_top + padding_bottom;
   2428     if (cell->str_buffer && cell->str_buffer->str.data) {
   2429         size_t text_height = buffer_text_visible_height(cell->str_buffer);
   2430         result += text_height == 0 ? empty_string_height : text_height;
   2431     }
   2432     return result;
   2433 }
   2434 
   2435 
   2436 FT_INTERNAL
   2437 int cell_printf(f_cell_t *cell, size_t row, f_conv_context_t *cntx, size_t vis_width)
   2438 {
   2439     const f_context_t *context = cntx->cntx;
   2440     size_t buf_len = vis_width;
   2441 
   2442     if (cell == NULL || (vis_width < cell_vis_width(cell, context))) {
   2443         return -1;
   2444     }
   2445 
   2446     f_table_properties_t *properties = context->table_properties;
   2447     unsigned int padding_top = get_cell_property_hierarchically(properties, context->row, context->column, FT_CPROP_TOP_PADDING);
   2448     unsigned int padding_left = get_cell_property_hierarchically(properties, context->row, context->column, FT_CPROP_LEFT_PADDING);
   2449     unsigned int padding_right = get_cell_property_hierarchically(properties, context->row, context->column, FT_CPROP_RIGHT_PADDING);
   2450 
   2451     size_t written = 0;
   2452     size_t invisible_written = 0;
   2453     int tmp = 0;
   2454 
   2455     /* todo: Dirty hack with changing buf_len! need refactoring. */
   2456     /* Also maybe it is better to move all struff with colors to buffers? */
   2457     char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2458     get_style_tag_for_cell(properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2459     buf_len += strlen(cell_style_tag);
   2460 
   2461     char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2462     get_reset_style_tag_for_cell(properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2463     buf_len += strlen(reset_cell_style_tag);
   2464 
   2465     char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2466     get_style_tag_for_content(properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2467     buf_len += strlen(content_style_tag);
   2468 
   2469     char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
   2470     get_reset_style_tag_for_content(properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
   2471     buf_len += strlen(reset_content_style_tag);
   2472 
   2473     /*    CELL_STYLE_T   LEFT_PADDING   CONTENT_STYLE_T  CONTENT   RESET_CONTENT_STYLE_T    RIGHT_PADDING   RESET_CELL_STYLE_T
   2474      *  |              |              |                |         |                       |                |                    |
   2475      *        L1                                                                                                    R1
   2476      *                     L2                                                                   R2
   2477      *                                     L3                               R3
   2478      */
   2479 
   2480     size_t L2 = padding_left;
   2481 
   2482     size_t R2 = padding_right;
   2483     size_t R3 = strlen(reset_cell_style_tag);
   2484 
   2485 #define TOTAL_WRITTEN (written + invisible_written)
   2486 #define RIGHT (padding_right + extra_right)
   2487 
   2488 #define WRITE_CELL_STYLE_TAG        CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, cell_style_tag))
   2489 #define WRITE_RESET_CELL_STYLE_TAG  CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_cell_style_tag))
   2490 #define WRITE_CONTENT_STYLE_TAG        CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, content_style_tag))
   2491 #define WRITE_RESET_CONTENT_STYLE_TAG  CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag))
   2492 
   2493     if (row >= hint_height_cell(cell, context)
   2494         || row < padding_top
   2495         || row >= (padding_top + buffer_text_visible_height(cell->str_buffer))) {
   2496         WRITE_CELL_STYLE_TAG;
   2497         WRITE_CONTENT_STYLE_TAG;
   2498         WRITE_RESET_CONTENT_STYLE_TAG;
   2499         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, buf_len - TOTAL_WRITTEN - R3, FT_SPACE));
   2500         WRITE_RESET_CELL_STYLE_TAG;
   2501 
   2502         return (int)TOTAL_WRITTEN;
   2503     }
   2504 
   2505     WRITE_CELL_STYLE_TAG;
   2506     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, L2, FT_SPACE));
   2507     if (cell->str_buffer) {
   2508         CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf(cell->str_buffer, row - padding_top, cntx, vis_width - L2 - R2, content_style_tag, reset_content_style_tag));
   2509     } else {
   2510         WRITE_CONTENT_STYLE_TAG;
   2511         WRITE_RESET_CONTENT_STYLE_TAG;
   2512         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, vis_width - L2 - R2, FT_SPACE));
   2513     }
   2514     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, R2, FT_SPACE));
   2515     WRITE_RESET_CELL_STYLE_TAG;
   2516 
   2517     return (int)TOTAL_WRITTEN;
   2518 
   2519 clear:
   2520     return -1;
   2521 #undef WRITE_CELL_STYLE_TAG
   2522 #undef WRITE_RESET_CELL_STYLE_TAG
   2523 #undef WRITE_CONTENT_STYLE_TAG
   2524 #undef WRITE_RESET_CONTENT_STYLE_TAG
   2525 #undef TOTAL_WRITTEN
   2526 #undef RIGHT
   2527 }
   2528 
   2529 FT_INTERNAL
   2530 f_status fill_cell_from_string(f_cell_t *cell, const char *str)
   2531 {
   2532     assert(str);
   2533     assert(cell);
   2534 
   2535     return fill_buffer_from_string(cell->str_buffer, str);
   2536 }
   2537 
   2538 #ifdef FT_HAVE_WCHAR
   2539 FT_INTERNAL
   2540 f_status fill_cell_from_wstring(f_cell_t *cell, const wchar_t *str)
   2541 {
   2542     assert(str);
   2543     assert(cell);
   2544 
   2545     return fill_buffer_from_wstring(cell->str_buffer, str);
   2546 }
   2547 #endif
   2548 
   2549 #ifdef FT_HAVE_UTF8
   2550 static
   2551 f_status fill_cell_from_u8string(f_cell_t *cell, const void *str)
   2552 {
   2553     assert(str);
   2554     assert(cell);
   2555     return fill_buffer_from_u8string(cell->str_buffer, str);
   2556 }
   2557 #endif /* FT_HAVE_UTF8 */
   2558 
   2559 FT_INTERNAL
   2560 f_string_buffer_t *cell_get_string_buffer(f_cell_t *cell)
   2561 {
   2562     assert(cell);
   2563     assert(cell->str_buffer);
   2564     return cell->str_buffer;
   2565 }
   2566 
   2567 FT_INTERNAL
   2568 f_status fill_cell_from_buffer(f_cell_t *cell, const f_string_buffer_t *buffer)
   2569 {
   2570     assert(cell);
   2571     assert(buffer);
   2572     switch (buffer->type) {
   2573         case CHAR_BUF:
   2574             return fill_cell_from_string(cell, buffer->str.cstr);
   2575 #ifdef FT_HAVE_WCHAR
   2576         case W_CHAR_BUF:
   2577             return fill_cell_from_wstring(cell, buffer->str.wstr);
   2578 #endif /* FT_HAVE_WCHAR */
   2579 #ifdef FT_HAVE_UTF8
   2580         case UTF8_BUF:
   2581             return fill_cell_from_u8string(cell, buffer->str.u8str);
   2582 #endif /* FT_HAVE_UTF8 */
   2583         default:
   2584             assert(0);
   2585             return FT_GEN_ERROR;
   2586     }
   2587 
   2588 }
   2589 
   2590 /********************************************************
   2591    End of file "cell.c"
   2592  ********************************************************/
   2593 
   2594 
   2595 /********************************************************
   2596    Begin of file "fort_impl.c"
   2597  ********************************************************/
   2598 
   2599 /*
   2600 libfort
   2601 
   2602 MIT License
   2603 
   2604 Copyright (c) 2017 - 2018 Seleznev Anton
   2605 
   2606 Permission is hereby granted, free of charge, to any person obtaining a copy
   2607 of this software and associated documentation files (the "Software"), to deal
   2608 in the Software without restriction, including without limitation the rights
   2609 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   2610 copies of the Software, and to permit persons to whom the Software is
   2611 furnished to do so, subject to the following conditions:
   2612 
   2613 The above copyright notice and this permission notice shall be included in all
   2614 copies or substantial portions of the Software.
   2615 
   2616 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   2617 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   2618 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   2619 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   2620 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   2621 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   2622 SOFTWARE.
   2623 */
   2624 
   2625 #include <stdlib.h>
   2626 #include <stdarg.h>
   2627 #include <stdio.h>
   2628 #include "fort.h"
   2629 #include <assert.h>
   2630 #include <string.h>
   2631 #include <wchar.h>
   2632 
   2633 /* #include "vector.h" */ /* Commented by amalgamation script */
   2634 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   2635 /* #include "string_buffer.h" */ /* Commented by amalgamation script */
   2636 /* #include "table.h" */ /* Commented by amalgamation script */
   2637 /* #include "row.h" */ /* Commented by amalgamation script */
   2638 /* #include "properties.h" */ /* Commented by amalgamation script */
   2639 
   2640 
   2641 ft_table_t *ft_create_table(void)
   2642 {
   2643     ft_table_t *result = (ft_table_t *)F_CALLOC(1, sizeof(ft_table_t));
   2644     if (result == NULL)
   2645         return NULL;
   2646 
   2647     result->rows = create_vector(sizeof(f_row_t *), DEFAULT_VECTOR_CAPACITY);
   2648     if (result->rows == NULL) {
   2649         F_FREE(result);
   2650         return NULL;
   2651     }
   2652     result->separators = create_vector(sizeof(f_separator_t *), DEFAULT_VECTOR_CAPACITY);
   2653     if (result->separators == NULL) {
   2654         destroy_vector(result->rows);
   2655         F_FREE(result);
   2656         return NULL;
   2657     }
   2658 
   2659     result->properties = create_table_properties();
   2660     if (result->properties == NULL) {
   2661         destroy_vector(result->separators);
   2662         destroy_vector(result->rows);
   2663         F_FREE(result);
   2664         return NULL;
   2665     }
   2666     result->conv_buffer = NULL;
   2667     result->cur_row = 0;
   2668     result->cur_col = 0;
   2669     return result;
   2670 }
   2671 
   2672 
   2673 void ft_destroy_table(ft_table_t *table)
   2674 {
   2675     size_t i = 0;
   2676 
   2677     if (table == NULL)
   2678         return;
   2679 
   2680     if (table->rows) {
   2681         size_t row_n = vector_size(table->rows);
   2682         for (i = 0; i < row_n; ++i) {
   2683             destroy_row(VECTOR_AT(table->rows, i, f_row_t *));
   2684         }
   2685         destroy_vector(table->rows);
   2686     }
   2687     if (table->separators) {
   2688         size_t row_n = vector_size(table->separators);
   2689         for (i = 0; i < row_n; ++i) {
   2690             destroy_separator(VECTOR_AT(table->separators, i, f_separator_t *));
   2691         }
   2692         destroy_vector(table->separators);
   2693     }
   2694     destroy_table_properties(table->properties);
   2695     destroy_string_buffer(table->conv_buffer);
   2696     F_FREE(table);
   2697 }
   2698 
   2699 ft_table_t *ft_copy_table(ft_table_t *table)
   2700 {
   2701     if (table == NULL)
   2702         return NULL;
   2703 
   2704     ft_table_t *result = ft_create_table();
   2705     if (result == NULL)
   2706         return NULL;
   2707 
   2708     size_t i = 0;
   2709     size_t rows_n = vector_size(table->rows);
   2710     for (i = 0; i < rows_n; ++i) {
   2711         f_row_t *row = VECTOR_AT(table->rows, i, f_row_t *);
   2712         f_row_t *new_row = copy_row(row);
   2713         if (new_row == NULL) {
   2714             ft_destroy_table(result);
   2715             return NULL;
   2716         }
   2717         vector_push(result->rows, &new_row);
   2718     }
   2719 
   2720     size_t sep_sz = vector_size(table->separators);
   2721     for (i = 0; i < sep_sz; ++i) {
   2722         f_separator_t *sep = VECTOR_AT(table->separators, i, f_separator_t *);
   2723         f_separator_t *new_sep = copy_separator(sep);
   2724         if (new_sep == NULL) {
   2725             ft_destroy_table(result);
   2726             return NULL;
   2727         }
   2728         vector_push(result->separators, &new_sep);
   2729     }
   2730 
   2731     /* note: by default new table has allocated default properties, so we
   2732      * have to destroy them first.
   2733      */
   2734     if (result->properties) {
   2735         destroy_table_properties(result->properties);
   2736     }
   2737     result->properties = copy_table_properties(table->properties);
   2738     if (result->properties == NULL) {
   2739         ft_destroy_table(result);
   2740         return NULL;
   2741     }
   2742 
   2743     /* todo: copy conv_buffer  ??  */
   2744 
   2745     result->cur_row = table->cur_row;
   2746     result->cur_col = table->cur_col;
   2747     return result;
   2748 }
   2749 
   2750 static int split_cur_row(ft_table_t *table, f_row_t **tail_of_cur_row)
   2751 {
   2752     if (table->cur_row >= vector_size(table->rows)) {
   2753         tail_of_cur_row = NULL;
   2754         return 0;
   2755     }
   2756 
   2757     f_row_t *row = VECTOR_AT(table->rows, table->cur_row, f_row_t *);
   2758     if (table->cur_col >= columns_in_row(row)) {
   2759         tail_of_cur_row = NULL;
   2760         return 0;
   2761     }
   2762 
   2763     f_row_t *tail = split_row(row, table->cur_col);
   2764     if (!tail) {
   2765         tail_of_cur_row = NULL;
   2766         return FT_GEN_ERROR;
   2767     }
   2768 
   2769     *tail_of_cur_row = tail;
   2770     return 0;
   2771 }
   2772 
   2773 int ft_ln(ft_table_t *table)
   2774 {
   2775     assert(table);
   2776     fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
   2777     switch (table_props->add_strategy) {
   2778         case FT_STRATEGY_INSERT: {
   2779             f_row_t *new_row = NULL;
   2780             if (FT_IS_ERROR(split_cur_row(table, &new_row))) {
   2781                 return FT_GEN_ERROR;
   2782             }
   2783             if (new_row) {
   2784                 if (FT_IS_ERROR(vector_insert(table->rows, &new_row, table->cur_row + 1))) {
   2785                     destroy_row(new_row);
   2786                     return FT_GEN_ERROR;
   2787                 }
   2788             }
   2789             break;
   2790         }
   2791         case FT_STRATEGY_REPLACE:
   2792             // do nothing
   2793             break;
   2794         default:
   2795             assert(0 && "Unexpected situation inside libfort");
   2796             break;
   2797     }
   2798     table->cur_col = 0;
   2799     table->cur_row++;
   2800     return FT_SUCCESS;
   2801 }
   2802 
   2803 size_t ft_cur_row(const ft_table_t *table)
   2804 {
   2805     assert(table);
   2806     return table->cur_row;
   2807 }
   2808 
   2809 size_t ft_cur_col(const ft_table_t *table)
   2810 {
   2811     assert(table);
   2812     return table->cur_col;
   2813 }
   2814 
   2815 void ft_set_cur_cell(ft_table_t *table, size_t row, size_t col)
   2816 {
   2817     assert(table);
   2818     table->cur_row = row;
   2819     table->cur_col = col;
   2820 }
   2821 
   2822 int ft_is_empty(const ft_table_t *table)
   2823 {
   2824     assert(table);
   2825     return ft_row_count(table) == 0;
   2826 }
   2827 
   2828 size_t ft_row_count(const ft_table_t *table)
   2829 {
   2830     assert(table && table->rows);
   2831     return vector_size(table->rows);
   2832 }
   2833 
   2834 size_t ft_col_count(const ft_table_t *table)
   2835 {
   2836     assert(table && table->rows);
   2837     size_t i = 0;
   2838     size_t cols_n = 0;
   2839     size_t rows_n = vector_size(table->rows);
   2840     for (i = 0; i < rows_n; ++i) {
   2841         f_row_t *row = VECTOR_AT(table->rows, i, f_row_t *);
   2842         size_t ncols = columns_in_row(row);
   2843         cols_n = MAX(cols_n, ncols);
   2844     }
   2845     return cols_n;
   2846 }
   2847 
   2848 int ft_erase_range(ft_table_t *table,
   2849                    size_t top_left_row, size_t top_left_col,
   2850                    size_t bottom_right_row, size_t bottom_right_col)
   2851 {
   2852     assert(table && table->rows);
   2853     int status = FT_SUCCESS;
   2854 
   2855     size_t rows_n = vector_size(table->rows);
   2856 
   2857     if (top_left_row == FT_CUR_ROW)
   2858         top_left_row = table->cur_row;
   2859     if (bottom_right_row == FT_CUR_ROW)
   2860         bottom_right_row = table->cur_row;
   2861 
   2862     if (top_left_col == FT_CUR_COLUMN)
   2863         top_left_col = table->cur_row;
   2864     if (bottom_right_col == FT_CUR_COLUMN)
   2865         bottom_right_col = table->cur_row;
   2866 
   2867     if (top_left_row > bottom_right_row || top_left_col > bottom_right_col)
   2868         return FT_EINVAL;
   2869 
   2870     f_row_t *row = NULL;
   2871     size_t i = top_left_row;
   2872     while (i < rows_n && i <= bottom_right_row) {
   2873         row = VECTOR_AT(table->rows, i, f_row_t *);
   2874         status = ft_row_erase_range(row, top_left_col, bottom_right_col);
   2875         if (FT_IS_ERROR(status))
   2876             return status;
   2877         ++i;
   2878     }
   2879 
   2880     f_separator_t *separator = NULL;
   2881 
   2882     size_t n_iterations = MIN(rows_n - 1, bottom_right_row) - top_left_row + 1;
   2883     size_t j = 0;
   2884     i = top_left_row;
   2885     for (j = 0; j < n_iterations; ++j) {
   2886         row = VECTOR_AT(table->rows, i, f_row_t *);
   2887         if (columns_in_row(row)) {
   2888             ++i;
   2889         } else {
   2890             destroy_row(row);
   2891             status = vector_erase(table->rows, i);
   2892             if (FT_IS_ERROR(status))
   2893                 return status;
   2894             if (i < vector_size(table->separators)) {
   2895                 separator = VECTOR_AT(table->separators, i, f_separator_t *);
   2896                 destroy_separator(separator);
   2897                 vector_erase(table->separators, i);
   2898             }
   2899         }
   2900     }
   2901 
   2902     return FT_SUCCESS;
   2903 }
   2904 
   2905 
   2906 static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct f_string_view *fmt, va_list *va)
   2907 {
   2908     size_t i = 0;
   2909     size_t new_cols = 0;
   2910 
   2911     if (table == NULL)
   2912         return -1;
   2913 
   2914     f_row_t *new_row = create_row_from_fmt_string(fmt, va);
   2915 
   2916     if (new_row == NULL) {
   2917         return -1;
   2918     }
   2919 
   2920     f_row_t **cur_row_p = NULL;
   2921     size_t sz = vector_size(table->rows);
   2922     if (row >= sz) {
   2923         size_t push_n = row - sz + 1;
   2924         for (i = 0; i < push_n; ++i) {
   2925             f_row_t *padding_row = create_row();
   2926             if (padding_row == NULL)
   2927                 goto clear;
   2928 
   2929             if (FT_IS_ERROR(vector_push(table->rows, &padding_row))) {
   2930                 destroy_row(padding_row);
   2931                 goto clear;
   2932             }
   2933         }
   2934     }
   2935     /* todo: clearing pushed items in case of error ?? */
   2936 
   2937     new_cols = columns_in_row(new_row);
   2938     cur_row_p = &VECTOR_AT(table->rows, row, f_row_t *);
   2939 
   2940     switch (table->properties->entire_table_properties.add_strategy) {
   2941         case FT_STRATEGY_INSERT: {
   2942             if (FT_IS_ERROR(insert_row(*cur_row_p, new_row, table->cur_col)))
   2943                 goto clear;
   2944             break;
   2945         }
   2946         case FT_STRATEGY_REPLACE: {
   2947             if (FT_IS_ERROR(swap_row(*cur_row_p, new_row, table->cur_col)))
   2948                 goto clear;
   2949             break;
   2950         }
   2951         default:
   2952             assert(0 && "Unexpected situation inside libfort");
   2953             break;
   2954     }
   2955 
   2956     table->cur_col += new_cols;
   2957     destroy_row(new_row);
   2958     return (int)new_cols;
   2959 
   2960 clear:
   2961     destroy_row(new_row);
   2962     return -1;
   2963 }
   2964 
   2965 #if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER)
   2966 #define FT_PRINTF ft_printf
   2967 #define FT_PRINTF_LN ft_printf_ln
   2968 #else
   2969 #define FT_PRINTF ft_printf_impl
   2970 #define FT_PRINTF_LN ft_printf_ln_impl
   2971 #endif
   2972 
   2973 
   2974 
   2975 int FT_PRINTF(ft_table_t *table, const char *fmt, ...)
   2976 {
   2977     assert(table);
   2978     va_list va;
   2979     va_start(va, fmt);
   2980 
   2981     struct f_string_view fmt_str;
   2982     fmt_str.type = CHAR_BUF;
   2983     fmt_str.u.cstr = fmt;
   2984     int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va);
   2985     va_end(va);
   2986     return result;
   2987 }
   2988 
   2989 int FT_PRINTF_LN(ft_table_t *table, const char *fmt, ...)
   2990 {
   2991     assert(table);
   2992     va_list va;
   2993     va_start(va, fmt);
   2994 
   2995     struct f_string_view fmt_str;
   2996     fmt_str.type = CHAR_BUF;
   2997     fmt_str.u.cstr = fmt;
   2998     int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va);
   2999     if (result >= 0) {
   3000         ft_ln(table);
   3001     }
   3002     va_end(va);
   3003     return result;
   3004 }
   3005 
   3006 #undef FT_PRINTF
   3007 #undef FT_PRINTF_LN
   3008 
   3009 #ifdef FT_HAVE_WCHAR
   3010 int ft_wprintf(ft_table_t *table, const wchar_t *fmt, ...)
   3011 {
   3012     assert(table);
   3013     va_list va;
   3014     va_start(va, fmt);
   3015 
   3016     struct f_string_view fmt_str;
   3017     fmt_str.type = W_CHAR_BUF;
   3018     fmt_str.u.wstr = fmt;
   3019     int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va);
   3020     va_end(va);
   3021     return result;
   3022 }
   3023 
   3024 int ft_wprintf_ln(ft_table_t *table, const wchar_t *fmt, ...)
   3025 {
   3026     assert(table);
   3027     va_list va;
   3028     va_start(va, fmt);
   3029 
   3030     struct f_string_view fmt_str;
   3031     fmt_str.type = W_CHAR_BUF;
   3032     fmt_str.u.wstr = fmt;
   3033     int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va);
   3034     if (result >= 0) {
   3035         ft_ln(table);
   3036     }
   3037     va_end(va);
   3038     return result;
   3039 }
   3040 
   3041 #endif
   3042 
   3043 void ft_set_default_printf_field_separator(char separator)
   3044 {
   3045     g_col_separator = separator;
   3046 }
   3047 
   3048 static int ft_write_impl_(ft_table_t *table, const f_string_view_t *cell_content)
   3049 {
   3050     assert(table);
   3051     f_string_buffer_t *buf = get_cur_str_buffer_and_create_if_not_exists(table);
   3052     if (buf == NULL)
   3053         return FT_GEN_ERROR;
   3054 
   3055     int status = FT_SUCCESS;
   3056     switch (cell_content->type) {
   3057         case CHAR_BUF:
   3058             status = fill_buffer_from_string(buf, cell_content->u.cstr);
   3059             break;
   3060 #ifdef FT_HAVE_WCHAR
   3061         case W_CHAR_BUF:
   3062             status = fill_buffer_from_wstring(buf, cell_content->u.wstr);
   3063             break;
   3064 #endif
   3065 #ifdef FT_HAVE_UTF8
   3066         case UTF8_BUF:
   3067             status = fill_buffer_from_u8string(buf, cell_content->u.u8str);
   3068             break;
   3069 #endif
   3070         default:
   3071             status = FT_GEN_ERROR;
   3072     }
   3073     if (FT_IS_SUCCESS(status)) {
   3074         table->cur_col++;
   3075     }
   3076     return status;
   3077 }
   3078 
   3079 static int ft_write_impl(ft_table_t *table, const char *cell_content)
   3080 {
   3081     f_string_view_t content;
   3082     content.type = CHAR_BUF;
   3083     content.u.cstr = cell_content;
   3084     return ft_write_impl_(table, &content);
   3085 }
   3086 
   3087 #ifdef FT_HAVE_UTF8
   3088 static int ft_u8write_impl(ft_table_t *table, const void *cell_content)
   3089 {
   3090     f_string_view_t content;
   3091     content.type = UTF8_BUF;
   3092     content.u.u8str = cell_content;
   3093     return ft_write_impl_(table, &content);
   3094 }
   3095 #endif /* FT_HAVE_UTF8 */
   3096 
   3097 #ifdef FT_HAVE_WCHAR
   3098 static int ft_wwrite_impl(ft_table_t *table, const wchar_t *cell_content)
   3099 {
   3100     f_string_view_t content;
   3101     content.type = W_CHAR_BUF;
   3102     content.u.wstr = cell_content;
   3103     return ft_write_impl_(table, &content);
   3104 }
   3105 #endif
   3106 
   3107 
   3108 int ft_nwrite(ft_table_t *table, size_t count, const char *cell_content, ...)
   3109 {
   3110     size_t i = 0;
   3111     assert(table);
   3112     int status = ft_write_impl(table, cell_content);
   3113     if (FT_IS_ERROR(status))
   3114         return status;
   3115 
   3116     va_list va;
   3117     va_start(va, cell_content);
   3118     --count;
   3119     for (i = 0; i < count; ++i) {
   3120         const char *cell = va_arg(va, const char *);
   3121         status = ft_write_impl(table, cell);
   3122         if (FT_IS_ERROR(status)) {
   3123             va_end(va);
   3124             return status;
   3125         }
   3126     }
   3127     va_end(va);
   3128     return status;
   3129 }
   3130 
   3131 int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...)
   3132 {
   3133     size_t i = 0;
   3134     assert(table);
   3135     int status = ft_write_impl(table, cell_content);
   3136     if (FT_IS_ERROR(status))
   3137         return status;
   3138 
   3139     va_list va;
   3140     va_start(va, cell_content);
   3141     --count;
   3142     for (i = 0; i < count; ++i) {
   3143         const char *cell = va_arg(va, const char *);
   3144         status = ft_write_impl(table, cell);
   3145         if (FT_IS_ERROR(status)) {
   3146             va_end(va);
   3147             return status;
   3148         }
   3149     }
   3150     va_end(va);
   3151 
   3152     ft_ln(table);
   3153     return status;
   3154 }
   3155 
   3156 
   3157 
   3158 
   3159 #ifdef FT_HAVE_WCHAR
   3160 
   3161 int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...)
   3162 {
   3163     size_t i = 0;
   3164     assert(table);
   3165     int status = ft_wwrite_impl(table, cell_content);
   3166     if (FT_IS_ERROR(status))
   3167         return status;
   3168 
   3169     va_list va;
   3170     va_start(va, cell_content);
   3171     --n;
   3172     for (i = 0; i < n; ++i) {
   3173         const wchar_t *cell = va_arg(va, const wchar_t *);
   3174         status = ft_wwrite_impl(table, cell);
   3175         if (FT_IS_ERROR(status)) {
   3176             va_end(va);
   3177             return status;
   3178         }
   3179     }
   3180     va_end(va);
   3181     return status;
   3182 }
   3183 
   3184 int ft_nwwrite_ln(ft_table_t *table, size_t n, const wchar_t *cell_content, ...)
   3185 {
   3186     size_t i = 0;
   3187     assert(table);
   3188     int status = ft_wwrite_impl(table, cell_content);
   3189     if (FT_IS_ERROR(status))
   3190         return status;
   3191 
   3192     va_list va;
   3193     va_start(va, cell_content);
   3194     --n;
   3195     for (i = 0; i < n; ++i) {
   3196         const wchar_t *cell = va_arg(va, const wchar_t *);
   3197         status = ft_wwrite_impl(table, cell);
   3198         if (FT_IS_ERROR(status)) {
   3199             va_end(va);
   3200             return status;
   3201         }
   3202     }
   3203     va_end(va);
   3204 
   3205     ft_ln(table);
   3206     return status;
   3207 }
   3208 #endif
   3209 
   3210 
   3211 int ft_row_write(ft_table_t *table, size_t cols, const char *cells[])
   3212 {
   3213     size_t i = 0;
   3214     assert(table);
   3215     for (i = 0; i < cols; ++i) {
   3216         int status = ft_write_impl(table, cells[i]);
   3217         if (FT_IS_ERROR(status)) {
   3218             /* todo: maybe current pos in case of error should be equal to the one before function call? */
   3219             return status;
   3220         }
   3221     }
   3222     return FT_SUCCESS;
   3223 }
   3224 
   3225 int ft_row_write_ln(ft_table_t *table, size_t cols, const char *cells[])
   3226 {
   3227     assert(table);
   3228     int status = ft_row_write(table, cols, cells);
   3229     if (FT_IS_SUCCESS(status)) {
   3230         ft_ln(table);
   3231     }
   3232     return status;
   3233 }
   3234 
   3235 #ifdef FT_HAVE_WCHAR
   3236 int ft_row_wwrite(ft_table_t *table, size_t cols, const wchar_t *cells[])
   3237 {
   3238     size_t i = 0;
   3239     assert(table);
   3240     for (i = 0; i < cols; ++i) {
   3241         int status = ft_wwrite_impl(table, cells[i]);
   3242         if (FT_IS_ERROR(status)) {
   3243             /* todo: maybe current pos in case of error should be equal
   3244              * to the one before function call?
   3245              */
   3246             return status;
   3247         }
   3248     }
   3249     return FT_SUCCESS;
   3250 }
   3251 
   3252 int ft_row_wwrite_ln(ft_table_t *table, size_t cols, const wchar_t *cells[])
   3253 {
   3254     assert(table);
   3255     int status = ft_row_wwrite(table, cols, cells);
   3256     if (FT_IS_SUCCESS(status)) {
   3257         ft_ln(table);
   3258     }
   3259     return status;
   3260 }
   3261 #endif
   3262 
   3263 
   3264 
   3265 int ft_table_write(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[])
   3266 {
   3267     size_t i = 0;
   3268     assert(table);
   3269     for (i = 0; i < rows; ++i) {
   3270         int status = ft_row_write(table, cols, (const char **)&table_cells[i * cols]);
   3271         if (FT_IS_ERROR(status)) {
   3272             /* todo: maybe current pos in case of error should be equal
   3273              * to the one before function call?
   3274              */
   3275             return status;
   3276         }
   3277         if (i != rows - 1)
   3278             ft_ln(table);
   3279     }
   3280     return FT_SUCCESS;
   3281 }
   3282 
   3283 int ft_table_write_ln(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[])
   3284 {
   3285     assert(table);
   3286     int status = ft_table_write(table, rows, cols, table_cells);
   3287     if (FT_IS_SUCCESS(status)) {
   3288         ft_ln(table);
   3289     }
   3290     return status;
   3291 }
   3292 
   3293 
   3294 #ifdef FT_HAVE_WCHAR
   3295 int ft_table_wwrite(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[])
   3296 {
   3297     size_t i = 0;
   3298     assert(table);
   3299     for (i = 0; i < rows; ++i) {
   3300         int status = ft_row_wwrite(table, cols, (const wchar_t **)&table_cells[i * cols]);
   3301         if (FT_IS_ERROR(status)) {
   3302             /* todo: maybe current pos in case of error should be equal
   3303              * to the one before function call?
   3304              */
   3305             return status;
   3306         }
   3307         if (i != rows - 1)
   3308             ft_ln(table);
   3309     }
   3310     return FT_SUCCESS;
   3311 }
   3312 
   3313 int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[])
   3314 {
   3315     assert(table);
   3316     int status = ft_table_wwrite(table, rows, cols, table_cells);
   3317     if (FT_IS_SUCCESS(status)) {
   3318         ft_ln(table);
   3319     }
   3320     return status;
   3321 }
   3322 #endif
   3323 
   3324 static
   3325 const char *empty_str_arr[] = {"", (const char *)L"", ""};
   3326 
   3327 static
   3328 const void *ft_to_string_impl(const ft_table_t *table, enum f_string_type b_type)
   3329 {
   3330     assert(table);
   3331 
   3332     const char *result = NULL;
   3333 
   3334     /* Determine size of table string representation */
   3335     size_t cod_height = 0;
   3336     size_t cod_width = 0;
   3337     int status = table_internal_codepoints_geometry(table, &cod_height, &cod_width);
   3338     if (FT_IS_ERROR(status)) {
   3339         return NULL;
   3340     }
   3341     size_t n_codepoints = cod_height * cod_width + 1;
   3342 
   3343     /* Allocate string buffer for string representation */
   3344     if (table->conv_buffer == NULL) {
   3345         ((ft_table_t *)table)->conv_buffer = create_string_buffer(n_codepoints, b_type);
   3346         if (table->conv_buffer == NULL)
   3347             return NULL;
   3348     }
   3349     while (string_buffer_cod_width_capacity(table->conv_buffer) < n_codepoints) {
   3350         if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) {
   3351             return NULL;
   3352         }
   3353     }
   3354     if (!buffer_check_align(table->conv_buffer))
   3355         return NULL;
   3356     char *buffer = (char *)buffer_get_data(table->conv_buffer);
   3357 
   3358     size_t cols = 0;
   3359     size_t rows = 0;
   3360     size_t *col_vis_width_arr = NULL;
   3361     size_t *row_vis_height_arr = NULL;
   3362     status = table_rows_and_cols_geometry(table, &col_vis_width_arr, &cols, &row_vis_height_arr, &rows, VISIBLE_GEOMETRY);
   3363     if (FT_IS_ERROR(status))
   3364         return NULL;
   3365 
   3366     if (rows == 0) {
   3367         F_FREE(col_vis_width_arr);
   3368         F_FREE(row_vis_height_arr);
   3369         return empty_str_arr[b_type];
   3370     }
   3371 
   3372     int tmp = 0;
   3373     size_t i = 0;
   3374     f_context_t context;
   3375     context.table_properties = (table->properties ? table->properties : &g_table_properties);
   3376     f_row_t *prev_row = NULL;
   3377     f_row_t *cur_row = NULL;
   3378     f_separator_t *cur_sep = NULL;
   3379     size_t sep_size = vector_size(table->separators);
   3380 
   3381     f_conv_context_t cntx;
   3382     cntx.u.buf = buffer;
   3383     cntx.raw_avail = string_buffer_raw_capacity(table->conv_buffer);
   3384     cntx.cntx = &context;
   3385     cntx.b_type = b_type;
   3386 
   3387     /* Print top margin */
   3388     for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) {
   3389         FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE));
   3390         FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE));
   3391     }
   3392 
   3393     for (i = 0; i < rows; ++i) {
   3394         cur_sep = (i < sep_size) ? VECTOR_AT(table->separators, i, f_separator_t *) : NULL;
   3395         cur_row = VECTOR_AT(table->rows, i, f_row_t *);
   3396         enum f_hor_separator_pos separatorPos = (i == 0) ? TOP_SEPARATOR : INSIDE_SEPARATOR;
   3397         context.row = i;
   3398         FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep));
   3399         FT_CHECK(snprintf_row(cur_row, &cntx, col_vis_width_arr, cols, row_vis_height_arr[i]));
   3400         prev_row = cur_row;
   3401     }
   3402     cur_row = NULL;
   3403     cur_sep = (i < sep_size) ? VECTOR_AT(table->separators, i, f_separator_t *) : NULL;
   3404     context.row = i;
   3405     FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, BOTTOM_SEPARATOR, cur_sep));
   3406 
   3407     /* Print bottom margin */
   3408     for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) {
   3409         FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE));
   3410         FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE));
   3411     }
   3412 
   3413     result = buffer;
   3414 
   3415 clear:
   3416     F_FREE(col_vis_width_arr);
   3417     F_FREE(row_vis_height_arr);
   3418     return result;
   3419 }
   3420 
   3421 const char *ft_to_string(const ft_table_t *table)
   3422 {
   3423     return (const char *)ft_to_string_impl(table, CHAR_BUF);
   3424 }
   3425 
   3426 #ifdef FT_HAVE_WCHAR
   3427 const wchar_t *ft_to_wstring(const ft_table_t *table)
   3428 {
   3429     return (const wchar_t *)ft_to_string_impl(table, W_CHAR_BUF);
   3430 }
   3431 #endif
   3432 
   3433 
   3434 int ft_add_separator(ft_table_t *table)
   3435 {
   3436     assert(table);
   3437     assert(table->separators);
   3438 
   3439     while (vector_size(table->separators) <= table->cur_row) {
   3440         f_separator_t *sep_p = create_separator(F_FALSE);
   3441         if (sep_p == NULL)
   3442             return FT_MEMORY_ERROR;
   3443         int status = vector_push(table->separators, &sep_p);
   3444         if (FT_IS_ERROR(status))
   3445             return status;
   3446     }
   3447 
   3448     f_separator_t **sep_p = &VECTOR_AT(table->separators, table->cur_row, f_separator_t *);
   3449     if (*sep_p == NULL)
   3450         *sep_p = create_separator(F_TRUE);
   3451     else
   3452         (*sep_p)->enabled = F_TRUE;
   3453 
   3454     if (*sep_p == NULL)
   3455         return FT_GEN_ERROR;
   3456     return FT_SUCCESS;
   3457 }
   3458 
   3459 static const struct fort_border_style *built_in_styles[] = {
   3460     &FORT_BASIC_STYLE,
   3461     &FORT_BASIC2_STYLE,
   3462     &FORT_SIMPLE_STYLE,
   3463     &FORT_PLAIN_STYLE,
   3464     &FORT_DOT_STYLE,
   3465     &FORT_EMPTY_STYLE,
   3466     &FORT_EMPTY2_STYLE,
   3467     &FORT_SOLID_STYLE,
   3468     &FORT_SOLID_ROUND_STYLE,
   3469     &FORT_NICE_STYLE,
   3470     &FORT_DOUBLE_STYLE,
   3471     &FORT_DOUBLE2_STYLE,
   3472     &FORT_BOLD_STYLE,
   3473     &FORT_BOLD2_STYLE,
   3474     &FORT_FRAME_STYLE,
   3475 };
   3476 #define BUILT_IN_STYLES_SZ (sizeof(built_in_styles) / sizeof(built_in_styles[0]))
   3477 
   3478 /* todo: remove this stupid and dangerous code */
   3479 static const struct ft_border_style built_in_external_styles[BUILT_IN_STYLES_SZ] = {
   3480     {
   3481         {"", "", "", "", "", ""},
   3482         {"", "", "", "", "", ""},
   3483         ""
   3484     }
   3485 };
   3486 
   3487 const struct ft_border_style *const FT_BASIC_STYLE = &built_in_external_styles[0];
   3488 const struct ft_border_style *const FT_BASIC2_STYLE = &built_in_external_styles[1];
   3489 const struct ft_border_style *const FT_SIMPLE_STYLE = &built_in_external_styles[2];
   3490 const struct ft_border_style *const FT_PLAIN_STYLE = &built_in_external_styles[3];
   3491 const struct ft_border_style *const FT_DOT_STYLE = &built_in_external_styles[4];
   3492 const struct ft_border_style *const FT_EMPTY_STYLE  = &built_in_external_styles[5];
   3493 const struct ft_border_style *const FT_EMPTY2_STYLE  = &built_in_external_styles[6];
   3494 const struct ft_border_style *const FT_SOLID_STYLE  = &built_in_external_styles[7];
   3495 const struct ft_border_style *const FT_SOLID_ROUND_STYLE  = &built_in_external_styles[8];
   3496 const struct ft_border_style *const FT_NICE_STYLE  = &built_in_external_styles[9];
   3497 const struct ft_border_style *const FT_DOUBLE_STYLE  = &built_in_external_styles[10];
   3498 const struct ft_border_style *const FT_DOUBLE2_STYLE  = &built_in_external_styles[11];
   3499 const struct ft_border_style *const FT_BOLD_STYLE  = &built_in_external_styles[12];
   3500 const struct ft_border_style *const FT_BOLD2_STYLE  = &built_in_external_styles[13];
   3501 const struct ft_border_style *const FT_FRAME_STYLE  = &built_in_external_styles[14];
   3502 
   3503 static void set_border_props_for_props(f_table_properties_t *properties, const struct ft_border_style *style)
   3504 {
   3505     if (style >= built_in_external_styles && style < (built_in_external_styles + BUILT_IN_STYLES_SZ)) {
   3506         size_t pos = (size_t)(style - built_in_external_styles);
   3507         memcpy(&(properties->border_style), built_in_styles[pos], sizeof(struct fort_border_style));
   3508         return;
   3509     }
   3510 
   3511     const struct ft_border_chars *border_chs = &(style->border_chs);
   3512     const struct ft_border_chars *header_border_chs = &(style->header_border_chs);
   3513 
   3514 #define BOR_CHARS properties->border_style.border_chars
   3515 #define H_BOR_CHARS properties->border_style.header_border_chars
   3516 #define SEP_CHARS properties->border_style.separator_chars
   3517 
   3518     BOR_CHARS[TT_bip] = border_chs->top_border_ch;
   3519     BOR_CHARS[IH_bip] = border_chs->separator_ch;
   3520     BOR_CHARS[BB_bip] = border_chs->bottom_border_ch;
   3521     BOR_CHARS[LL_bip] = BOR_CHARS[IV_bip] = BOR_CHARS[RR_bip] = border_chs->side_border_ch;
   3522 
   3523     BOR_CHARS[TL_bip] = BOR_CHARS[TV_bip] = BOR_CHARS[TR_bip] = border_chs->out_intersect_ch;
   3524     BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = border_chs->out_intersect_ch;
   3525     BOR_CHARS[BL_bip] = BOR_CHARS[BV_bip] = BOR_CHARS[BR_bip] = border_chs->out_intersect_ch;
   3526     BOR_CHARS[II_bip] = border_chs->in_intersect_ch;
   3527 
   3528     BOR_CHARS[LI_bip] = BOR_CHARS[TI_bip] = BOR_CHARS[RI_bip] = BOR_CHARS[BI_bip] = border_chs->in_intersect_ch;
   3529 
   3530     if (strlen(border_chs->separator_ch) == 0 && strlen(border_chs->in_intersect_ch) == 0) {
   3531         BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = "\0";
   3532     }
   3533 
   3534     H_BOR_CHARS[TT_bip] = header_border_chs->top_border_ch;
   3535     H_BOR_CHARS[IH_bip] = header_border_chs->separator_ch;
   3536     H_BOR_CHARS[BB_bip] = header_border_chs->bottom_border_ch;
   3537     H_BOR_CHARS[LL_bip] = H_BOR_CHARS[IV_bip] = H_BOR_CHARS[RR_bip] = header_border_chs->side_border_ch;
   3538 
   3539     H_BOR_CHARS[TL_bip] = H_BOR_CHARS[TV_bip] = H_BOR_CHARS[TR_bip] = header_border_chs->out_intersect_ch;
   3540     H_BOR_CHARS[LH_bip] = H_BOR_CHARS[RH_bip] = header_border_chs->out_intersect_ch;
   3541     H_BOR_CHARS[BL_bip] = H_BOR_CHARS[BV_bip] = H_BOR_CHARS[BR_bip] = header_border_chs->out_intersect_ch;
   3542     H_BOR_CHARS[II_bip] = header_border_chs->in_intersect_ch;
   3543 
   3544     H_BOR_CHARS[LI_bip] = H_BOR_CHARS[TI_bip] = H_BOR_CHARS[RI_bip] = H_BOR_CHARS[BI_bip] = header_border_chs->in_intersect_ch;
   3545 
   3546     if (strlen(header_border_chs->separator_ch) == 0 && strlen(header_border_chs->in_intersect_ch) == 0) {
   3547         BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = "\0";
   3548     }
   3549 
   3550     SEP_CHARS[LH_sip] = SEP_CHARS[RH_sip] = SEP_CHARS[II_sip] = header_border_chs->out_intersect_ch;
   3551     SEP_CHARS[TI_sip] = SEP_CHARS[BI_sip] = header_border_chs->out_intersect_ch;
   3552     SEP_CHARS[IH_sip] = style->hor_separator_char;
   3553 
   3554 
   3555 #undef BOR_CHARS
   3556 #undef H_BOR_CHARS
   3557 #undef SEP_CHARS
   3558 }
   3559 
   3560 
   3561 int ft_set_default_border_style(const struct ft_border_style *style)
   3562 {
   3563     set_border_props_for_props(&g_table_properties, style);
   3564     return FT_SUCCESS;
   3565 }
   3566 
   3567 int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style)
   3568 {
   3569     assert(table);
   3570     if (table->properties == NULL) {
   3571         table->properties = create_table_properties();
   3572         if (table->properties == NULL)
   3573             return FT_MEMORY_ERROR;
   3574     }
   3575     set_border_props_for_props(table->properties, style);
   3576     return FT_SUCCESS;
   3577 }
   3578 
   3579 
   3580 
   3581 int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t property, int value)
   3582 {
   3583     assert(table);
   3584 
   3585     if (table->properties == NULL) {
   3586         table->properties = create_table_properties();
   3587         if (table->properties == NULL)
   3588             return FT_MEMORY_ERROR;
   3589     }
   3590     if (table->properties->cell_properties == NULL) {
   3591         table->properties->cell_properties = create_cell_prop_container();
   3592         if (table->properties->cell_properties == NULL) {
   3593             return FT_GEN_ERROR;
   3594         }
   3595     }
   3596 
   3597     if (row == FT_CUR_ROW)
   3598         row = table->cur_row;
   3599     if (col == FT_CUR_COLUMN)
   3600         col = table->cur_col;
   3601 
   3602     return set_cell_property(table->properties->cell_properties, row, col, property, value);
   3603 }
   3604 
   3605 int ft_set_default_cell_prop(uint32_t property, int value)
   3606 {
   3607     return set_default_cell_property(property, value);
   3608 }
   3609 
   3610 
   3611 int ft_set_default_tbl_prop(uint32_t property, int value)
   3612 {
   3613     return set_default_entire_table_property(property, value);
   3614 }
   3615 
   3616 int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value)
   3617 {
   3618     assert(table);
   3619 
   3620     if (table->properties == NULL) {
   3621         table->properties = create_table_properties();
   3622         if (table->properties == NULL)
   3623             return FT_MEMORY_ERROR;
   3624     }
   3625     return set_entire_table_property(table->properties, property, value);
   3626 }
   3627 
   3628 void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr))
   3629 {
   3630     set_memory_funcs(f_malloc, f_free);
   3631 }
   3632 
   3633 const char *ft_strerror(int error_code)
   3634 {
   3635     switch (error_code) {
   3636         case FT_MEMORY_ERROR:
   3637             return "Out of memory";
   3638         case FT_GEN_ERROR:
   3639             return "General error";
   3640         case FT_EINVAL:
   3641             return "Invalid argument";
   3642         case FT_INTERN_ERROR:
   3643             return "Internal libfort error";
   3644         default:
   3645             if (error_code < 0)
   3646                 return "Unknown error code";
   3647             else
   3648                 return "Success";
   3649     }
   3650 }
   3651 
   3652 int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span)
   3653 {
   3654     assert(table);
   3655     if (hor_span < 2)
   3656         return FT_EINVAL;
   3657 
   3658     if (row == FT_CUR_ROW)
   3659         row = table->cur_row;
   3660     if (row == FT_CUR_COLUMN)
   3661         col = table->cur_col;
   3662 
   3663     f_row_t *row_p = get_row_and_create_if_not_exists(table, row);
   3664     if (row_p == NULL)
   3665         return FT_GEN_ERROR;
   3666 
   3667     return row_set_cell_span(row_p, col, hor_span);
   3668 }
   3669 
   3670 #ifdef FT_HAVE_UTF8
   3671 
   3672 int ft_u8nwrite(ft_table_t *table, size_t n, const void *cell_content, ...)
   3673 {
   3674     size_t i = 0;
   3675     assert(table);
   3676     int status = ft_u8write_impl(table, cell_content);
   3677     if (FT_IS_ERROR(status))
   3678         return status;
   3679 
   3680     va_list va;
   3681     va_start(va, cell_content);
   3682     --n;
   3683     for (i = 0; i < n; ++i) {
   3684         const void *cell = va_arg(va, const void *);
   3685         status = ft_u8write_impl(table, cell);
   3686         if (FT_IS_ERROR(status)) {
   3687             va_end(va);
   3688             return status;
   3689         }
   3690     }
   3691     va_end(va);
   3692 
   3693     return status;
   3694 }
   3695 
   3696 int ft_u8nwrite_ln(ft_table_t *table, size_t n, const void *cell_content, ...)
   3697 {
   3698     size_t i = 0;
   3699     assert(table);
   3700     int status = ft_u8write_impl(table, cell_content);
   3701     if (FT_IS_ERROR(status))
   3702         return status;
   3703 
   3704     va_list va;
   3705     va_start(va, cell_content);
   3706     --n;
   3707     for (i = 0; i < n; ++i) {
   3708         const void *cell = va_arg(va, const void *);
   3709         status = ft_u8write_impl(table, cell);
   3710         if (FT_IS_ERROR(status)) {
   3711             va_end(va);
   3712             return status;
   3713         }
   3714     }
   3715     va_end(va);
   3716 
   3717     ft_ln(table);
   3718     return status;
   3719 }
   3720 
   3721 FT_PRINTF_ATTRIBUTE_FORMAT(2, 3)
   3722 int ft_u8printf(ft_table_t *table, const char *fmt, ...)
   3723 {
   3724     assert(table);
   3725     va_list va;
   3726     va_start(va, fmt);
   3727 
   3728     struct f_string_view fmt_str;
   3729     fmt_str.type = UTF8_BUF;
   3730     fmt_str.u.cstr = fmt;
   3731     int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va);
   3732     va_end(va);
   3733     return result;
   3734 }
   3735 
   3736 FT_PRINTF_ATTRIBUTE_FORMAT(2, 3)
   3737 int ft_u8printf_ln(ft_table_t *table, const char *fmt, ...)
   3738 {
   3739     assert(table);
   3740     va_list va;
   3741     va_start(va, fmt);
   3742 
   3743     struct f_string_view fmt_str;
   3744     fmt_str.type = UTF8_BUF;
   3745     fmt_str.u.cstr = fmt;
   3746     int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va);
   3747     if (result >= 0) {
   3748         ft_ln(table);
   3749     }
   3750     va_end(va);
   3751     return result;
   3752 }
   3753 
   3754 const void *ft_to_u8string(const ft_table_t *table)
   3755 {
   3756     return (const void *)ft_to_string_impl(table, UTF8_BUF);
   3757 }
   3758 
   3759 void ft_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width))
   3760 {
   3761     buffer_set_u8strwid_func(u8strwid);
   3762 }
   3763 
   3764 #endif /* FT_HAVE_UTF8 */
   3765 
   3766 /********************************************************
   3767    End of file "fort_impl.c"
   3768  ********************************************************/
   3769 
   3770 
   3771 /********************************************************
   3772    Begin of file "fort_utils.c"
   3773  ********************************************************/
   3774 
   3775 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   3776 #ifdef FT_HAVE_WCHAR
   3777 #include <wchar.h>
   3778 #endif
   3779 #if defined(FT_HAVE_UTF8)
   3780 /* #include "utf8.h" */ /* Commented by amalgamation script */
   3781 #endif
   3782 /* #include "string_buffer.h" */ /* Commented by amalgamation script */
   3783 
   3784 
   3785 char g_col_separator = FORT_DEFAULT_COL_SEPARATOR;
   3786 
   3787 /*****************************************************************************
   3788  *               LIBFORT helpers
   3789  *****************************************************************************/
   3790 
   3791 #if defined(FT_GCC_COMPILER) || defined(FT_CLANG_COMPILER)
   3792 void *(*fort_malloc)(size_t size) = &malloc;
   3793 void (*fort_free)(void *ptr) = &free;
   3794 void *(*fort_calloc)(size_t nmemb, size_t size) = &calloc;
   3795 void *(*fort_realloc)(void *ptr, size_t size) = &realloc;
   3796 #else
   3797 static void *local_malloc(size_t size)
   3798 {
   3799     return malloc(size);
   3800 }
   3801 
   3802 static void local_free(void *ptr)
   3803 {
   3804     free(ptr);
   3805 }
   3806 
   3807 static void *local_calloc(size_t nmemb, size_t size)
   3808 {
   3809     return calloc(nmemb, size);
   3810 }
   3811 
   3812 static void *local_realloc(void *ptr, size_t size)
   3813 {
   3814     return realloc(ptr, size);
   3815 }
   3816 
   3817 void *(*fort_malloc)(size_t size) = &local_malloc;
   3818 void (*fort_free)(void *ptr) = &local_free;
   3819 void *(*fort_calloc)(size_t nmemb, size_t size) = &local_calloc;
   3820 void *(*fort_realloc)(void *ptr, size_t size) = &local_realloc;
   3821 #endif
   3822 
   3823 static void *custom_fort_calloc(size_t nmemb, size_t size)
   3824 {
   3825     size_t total_size = nmemb * size;
   3826     void *result = F_MALLOC(total_size);
   3827     if (result != NULL)
   3828         memset(result, 0, total_size);
   3829     return result;
   3830 }
   3831 
   3832 static void *custom_fort_realloc(void *ptr, size_t size)
   3833 {
   3834     if (ptr == NULL)
   3835         return F_MALLOC(size);
   3836     if (size == 0) {
   3837         F_FREE(ptr);
   3838         return NULL;
   3839     }
   3840 
   3841     void *new_chunk = F_MALLOC(size);
   3842     if (new_chunk == NULL)
   3843         return NULL;
   3844 
   3845     /*
   3846      * In theory we should copy MIN(size, size allocated for ptr) bytes,
   3847      * but this is rather dummy implementation so we don't care about it
   3848      */
   3849     memcpy(new_chunk, ptr, size);
   3850     F_FREE(ptr);
   3851     return new_chunk;
   3852 }
   3853 
   3854 
   3855 FT_INTERNAL
   3856 void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr))
   3857 {
   3858     assert((f_malloc == NULL && f_free == NULL) /* Use std functions */
   3859            || (f_malloc != NULL && f_free != NULL) /* Use custom functions */);
   3860 
   3861     if (f_malloc == NULL && f_free == NULL) {
   3862 #if defined(FT_GCC_COMPILER) || defined(FT_CLANG_COMPILER)
   3863         fort_malloc = &malloc;
   3864         fort_free = &free;
   3865         fort_calloc = &calloc;
   3866         fort_realloc = &realloc;
   3867 #else
   3868         fort_malloc = &local_malloc;
   3869         fort_free = &local_free;
   3870         fort_calloc = &local_calloc;
   3871         fort_realloc = &local_realloc;
   3872 #endif
   3873     } else {
   3874         fort_malloc = f_malloc;
   3875         fort_free = f_free;
   3876         fort_calloc = &custom_fort_calloc;
   3877         fort_realloc = &custom_fort_realloc;
   3878     }
   3879 
   3880 }
   3881 
   3882 FT_INTERNAL
   3883 char *fort_strdup(const char *str)
   3884 {
   3885     if (str == NULL)
   3886         return NULL;
   3887 
   3888     size_t sz = strlen(str);
   3889     char *str_copy = (char *)F_MALLOC((sz + 1) * sizeof(char));
   3890     if (str_copy == NULL)
   3891         return NULL;
   3892 
   3893     strcpy(str_copy, str);
   3894     return str_copy;
   3895 }
   3896 
   3897 #if defined(FT_HAVE_WCHAR)
   3898 FT_INTERNAL
   3899 wchar_t *fort_wcsdup(const wchar_t *str)
   3900 {
   3901     if (str == NULL)
   3902         return NULL;
   3903 
   3904     size_t sz = wcslen(str);
   3905     wchar_t *str_copy = (wchar_t *)F_MALLOC((sz + 1) * sizeof(wchar_t));
   3906     if (str_copy == NULL)
   3907         return NULL;
   3908 
   3909     wcscpy(str_copy, str);
   3910     return str_copy;
   3911 }
   3912 #endif
   3913 
   3914 
   3915 static
   3916 size_t columns_number_in_fmt_string(const char *fmt)
   3917 {
   3918     size_t separator_counter = 0;
   3919     const char *pos = fmt;
   3920     while (1) {
   3921         pos = strchr(pos, g_col_separator);
   3922         if (pos == NULL)
   3923             break;
   3924 
   3925         separator_counter++;
   3926         ++pos;
   3927     }
   3928     return separator_counter + 1;
   3929 }
   3930 
   3931 #if defined(FT_HAVE_WCHAR)
   3932 static
   3933 size_t columns_number_in_fmt_wstring(const wchar_t *fmt)
   3934 {
   3935     size_t separator_counter = 0;
   3936     const wchar_t *pos = fmt;
   3937     while (1) {
   3938         pos = wcschr(pos, g_col_separator);
   3939         if (pos == NULL)
   3940             break;
   3941 
   3942         separator_counter++;
   3943         ++pos;
   3944     }
   3945     return separator_counter + 1;
   3946 }
   3947 #endif
   3948 
   3949 #if defined(FT_HAVE_UTF8)
   3950 static
   3951 size_t columns_number_in_fmt_u8string(const void *fmt)
   3952 {
   3953     size_t separator_counter = 0;
   3954     const char *pos = (const char *)fmt;
   3955     while (1) {
   3956         pos = (const char *)utf8chr(pos, g_col_separator);
   3957         if (pos == NULL)
   3958             break;
   3959 
   3960         separator_counter++;
   3961         ++pos;
   3962     }
   3963     return separator_counter + 1;
   3964 }
   3965 #endif
   3966 
   3967 FT_INTERNAL
   3968 size_t number_of_columns_in_format_string(const f_string_view_t *fmt)
   3969 {
   3970     switch (fmt->type) {
   3971         case CHAR_BUF:
   3972             return columns_number_in_fmt_string(fmt->u.cstr);
   3973 #ifdef FT_HAVE_WCHAR
   3974         case W_CHAR_BUF:
   3975             return columns_number_in_fmt_wstring(fmt->u.wstr);
   3976 #endif /* FT_HAVE_WCHAR */
   3977 #ifdef FT_HAVE_UTF8
   3978         case UTF8_BUF:
   3979             return columns_number_in_fmt_u8string(fmt->u.u8str);
   3980 #endif /* FT_HAVE_UTF8 */
   3981         default:
   3982             assert(0);
   3983     }
   3984     return 0;
   3985 }
   3986 
   3987 FT_INTERNAL
   3988 size_t number_of_columns_in_format_buffer(const f_string_buffer_t *fmt)
   3989 {
   3990     switch (fmt->type) {
   3991         case CHAR_BUF:
   3992             return columns_number_in_fmt_string(fmt->str.cstr);
   3993 #ifdef FT_HAVE_WCHAR
   3994         case W_CHAR_BUF:
   3995             return columns_number_in_fmt_wstring(fmt->str.wstr);
   3996 #endif /* FT_HAVE_WCHAR */
   3997 #ifdef FT_HAVE_UTF8
   3998         case UTF8_BUF:
   3999             return columns_number_in_fmt_u8string(fmt->str.u8str);
   4000 #endif /* FT_HAVE_UTF8 */
   4001         default:
   4002             assert(0);
   4003     }
   4004     return 0;
   4005 }
   4006 
   4007 static
   4008 int snprint_n_strings_impl(char *buf, size_t length, size_t n, const char *str)
   4009 {
   4010     size_t str_len = strlen(str);
   4011     if (length <= n * str_len)
   4012         return -1;
   4013 
   4014     if (n == 0)
   4015         return 0;
   4016 
   4017     /* To ensure valid return value it is safely not print such big strings */
   4018     if (n * str_len > INT_MAX)
   4019         return -1;
   4020 
   4021     if (str_len == 0)
   4022         return 0;
   4023 
   4024     int status = snprintf(buf, length, "%0*d", (int)(n * str_len), 0);
   4025     if (status < 0)
   4026         return status;
   4027 
   4028     size_t i = 0;
   4029     for (i = 0; i < n; ++i) {
   4030         const char *str_p = str;
   4031         while (*str_p)
   4032             *(buf++) = *(str_p++);
   4033     }
   4034     return (int)(n * str_len);
   4035 }
   4036 
   4037 static
   4038 int snprint_n_strings(f_conv_context_t *cntx, size_t n, const char *str)
   4039 {
   4040     int w = snprint_n_strings_impl(cntx->u.buf, cntx->raw_avail, n, str);
   4041     if (w >= 0) {
   4042         cntx->u.buf += w;
   4043         cntx->raw_avail -= w;
   4044     }
   4045     return w;
   4046 }
   4047 
   4048 #if defined(FT_HAVE_WCHAR)
   4049 static
   4050 int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str);
   4051 #endif
   4052 
   4053 #if defined(FT_HAVE_UTF8)
   4054 static
   4055 int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str);
   4056 #endif
   4057 
   4058 
   4059 FT_INTERNAL
   4060 int print_n_strings(f_conv_context_t *cntx, size_t n, const char *str)
   4061 {
   4062     int cod_w;
   4063     int raw_written;
   4064 
   4065     switch (cntx->b_type) {
   4066         case CHAR_BUF:
   4067             raw_written = snprint_n_strings(cntx, n, str);
   4068             cod_w = raw_written;
   4069             return cod_w;
   4070 #ifdef FT_HAVE_WCHAR
   4071         case W_CHAR_BUF:
   4072             cod_w = wsnprint_n_string(cntx->u.wbuf, cntx->raw_avail, n, str);
   4073             if (cod_w < 0)
   4074                 return cod_w;
   4075             raw_written = sizeof(wchar_t) * cod_w;
   4076 
   4077             cntx->u.buf += raw_written;
   4078             cntx->raw_avail -= raw_written;
   4079             return cod_w;
   4080 #endif /* FT_HAVE_WCHAR */
   4081 #ifdef FT_HAVE_UTF8
   4082         case UTF8_BUF:
   4083             /* Everying is very strange and differs with W_CHAR_BUF */
   4084             raw_written = u8nprint_n_strings(cntx->u.buf, cntx->raw_avail, n, str);
   4085             if (raw_written < 0) {
   4086                 fprintf(stderr, " raw_written = %d\n", raw_written);
   4087                 return raw_written;
   4088             }
   4089 
   4090             cntx->u.buf += raw_written;
   4091             cntx->raw_avail -= raw_written;
   4092             return utf8len(str) * n;
   4093 #endif /* FT_HAVE_UTF8 */
   4094         default:
   4095             assert(0);
   4096             return -1;
   4097     }
   4098 }
   4099 
   4100 FT_INTERNAL
   4101 int ft_nprint(f_conv_context_t *cntx, const char *str, size_t strlen)
   4102 {
   4103     if (cntx->raw_avail + 1/* for 0 */ < strlen)
   4104         return -1;
   4105 
   4106     memcpy(cntx->u.buf, str, strlen);
   4107     cntx->u.buf += strlen;
   4108     cntx->raw_avail -= strlen;
   4109     *cntx->u.buf = '\0'; /* Do we need this ? */
   4110     return strlen;
   4111 }
   4112 
   4113 #ifdef FT_HAVE_WCHAR
   4114 int ft_nwprint(f_conv_context_t *cntx, const wchar_t *str, size_t strlen)
   4115 {
   4116     if (cntx->raw_avail + 1/* for 0 */ < strlen)
   4117         return -1;
   4118 
   4119     size_t raw_len = strlen * sizeof(wchar_t);
   4120 
   4121     memcpy(cntx->u.buf, str, raw_len);
   4122     cntx->u.buf += raw_len;
   4123     cntx->raw_avail -= raw_len;
   4124 
   4125     /* Do we need this ? */
   4126     wchar_t end_of_string = L'\0';
   4127     memcpy(cntx->u.buf, &end_of_string, sizeof(wchar_t));
   4128     return strlen;
   4129 }
   4130 #endif /* FT_HAVE_WCHAR */
   4131 
   4132 #ifdef FT_HAVE_UTF8
   4133 FT_INTERNAL
   4134 int ft_nu8print(f_conv_context_t *cntx, const void *beg, const void *end)
   4135 {
   4136     const char *bc = (const char *)beg;
   4137     const char *ec = (const char *)end;
   4138     size_t raw_len = ec - bc;
   4139     if (cntx->raw_avail + 1 < raw_len)
   4140         return -1;
   4141 
   4142     memcpy(cntx->u.buf, beg, raw_len);
   4143     cntx->u.buf += raw_len;
   4144     cntx->raw_avail -= raw_len;
   4145     *(cntx->u.buf) = '\0'; /* Do we need this ? */
   4146     return raw_len; /* what return here ? */
   4147 }
   4148 #endif /* FT_HAVE_UTF8 */
   4149 
   4150 #if defined(FT_HAVE_WCHAR)
   4151 #define WCS_SIZE 64
   4152 
   4153 static
   4154 int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str)
   4155 {
   4156     size_t str_len = strlen(str);
   4157 
   4158     /* note: maybe it's, better to return -1 in case of multibyte character
   4159      * strings (not sure this case is done correctly).
   4160      */
   4161     if (str_len > 1) {
   4162         const unsigned char *p = (const unsigned char *)str;
   4163         while (*p) {
   4164             if (*p <= 127)
   4165                 p++;
   4166             else {
   4167                 wchar_t wcs[WCS_SIZE];
   4168                 const char *ptr = str;
   4169                 size_t wcs_len;
   4170                 mbstate_t mbst;
   4171                 memset(&mbst, 0, sizeof(mbst));
   4172                 wcs_len = mbsrtowcs(wcs, (const char **)&ptr, WCS_SIZE, &mbst);
   4173                 /* for simplicity */
   4174                 if ((wcs_len == (size_t) - 1) || wcs_len > 1) {
   4175                     return -1;
   4176                 } else {
   4177                     wcs[wcs_len] = L'\0';
   4178                     size_t k = n;
   4179                     while (k) {
   4180                         *buf = *wcs;
   4181                         ++buf;
   4182                         --k;
   4183                     }
   4184                     buf[n] = L'\0';
   4185                     return (int)n;
   4186                 }
   4187             }
   4188         }
   4189     }
   4190 
   4191     if (length <= n * str_len)
   4192         return -1;
   4193 
   4194     if (n == 0)
   4195         return 0;
   4196 
   4197     /* To ensure valid return value it is safely not print such big strings */
   4198     if (n * str_len > INT_MAX)
   4199         return -1;
   4200 
   4201     if (str_len == 0)
   4202         return 0;
   4203 
   4204     int status = swprintf(buf, length, L"%0*d", (int)(n * str_len), 0);
   4205     if (status < 0)
   4206         return status;
   4207 
   4208     size_t i = 0;
   4209     for (i = 0; i < n; ++i) {
   4210         const char *str_p = str;
   4211         while (*str_p)
   4212             *(buf++) = (wchar_t) * (str_p++);
   4213     }
   4214     return (int)(n * str_len);
   4215 }
   4216 #endif
   4217 
   4218 
   4219 #if defined(FT_HAVE_UTF8)
   4220 static
   4221 int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str)
   4222 {
   4223     size_t str_size = utf8size(str) - 1; /* str_size - raw size in bytes, excluding \0 */
   4224     if (length <= n * str_size)
   4225         return -1;
   4226 
   4227     if (n == 0)
   4228         return 0;
   4229 
   4230     /* To ensure valid return value it is safely not print such big strings */
   4231     if (n * str_size > INT_MAX)
   4232         return -1;
   4233 
   4234     if (str_size == 0)
   4235         return 0;
   4236 
   4237     size_t i = n;
   4238     while (i) {
   4239         memcpy(buf, str, str_size);
   4240         buf = (char *)buf + str_size;
   4241         --i;
   4242     }
   4243     *(char *)buf = '\0';
   4244     return (int)(n * str_size);
   4245 }
   4246 #endif
   4247 
   4248 /********************************************************
   4249    End of file "fort_utils.c"
   4250  ********************************************************/
   4251 
   4252 
   4253 /********************************************************
   4254    Begin of file "properties.c"
   4255  ********************************************************/
   4256 
   4257 /* #include "fort_utils.h" */ /* Commented by amalgamation script */
   4258 #include <assert.h>
   4259 /* #include "properties.h" */ /* Commented by amalgamation script */
   4260 /* #include "vector.h" */ /* Commented by amalgamation script */
   4261 
   4262 #define FT_RESET_COLOR "\033[0m"
   4263 
   4264 static const char *fg_colors[] = {
   4265     "",
   4266     "\033[30m",
   4267     "\033[31m",
   4268     "\033[32m",
   4269     "\033[33m",
   4270     "\033[34m",
   4271     "\033[35m",
   4272     "\033[36m",
   4273     "\033[37m",
   4274     "\033[90m",
   4275     "\033[91m",
   4276     "\033[92m",
   4277     "\033[93m",
   4278     "\033[94m",
   4279     "\033[95m",
   4280     "\033[96m",
   4281     "\033[97m",
   4282 };
   4283 
   4284 static const char *bg_colors[] = {
   4285     "",
   4286     "\033[40m",
   4287     "\033[41m",
   4288     "\033[42m",
   4289     "\033[43m",
   4290     "\033[44m",
   4291     "\033[45m",
   4292     "\033[46m",
   4293     "\033[47m",
   4294     "\033[100m",
   4295     "\033[101m",
   4296     "\033[102m",
   4297     "\033[103m",
   4298     "\033[104m",
   4299     "\033[105m",
   4300     "\033[106m",
   4301     "\033[107m",
   4302 };
   4303 
   4304 static const char *text_styles[] = {
   4305     "",
   4306     "\033[1m",
   4307     "\033[2m",
   4308     "\033[3m",
   4309     "\033[4m",
   4310     "\033[5m",
   4311     "\033[7m",
   4312     "\033[8m",
   4313 };
   4314 
   4315 #define UNIVERSAL_RESET_TAG "\033[0m"
   4316 
   4317 static const size_t n_fg_colors = sizeof(fg_colors) / sizeof(fg_colors[0]);
   4318 static const size_t n_bg_colors = sizeof(bg_colors) / sizeof(bg_colors[0]);
   4319 static const size_t n_styles = sizeof(text_styles) / sizeof(text_styles[0]);
   4320 
   4321 void get_style_tag_for_cell(const f_table_properties_t *props,
   4322                             size_t row, size_t col, char *style_tag, size_t sz)
   4323 {
   4324     (void)sz;
   4325     size_t i = 0;
   4326 
   4327     unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_BG_COLOR);
   4328     unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_TEXT_STYLE);
   4329 
   4330     style_tag[0] = '\0';
   4331 
   4332     if (text_style < (1U << n_styles)) {
   4333         for (i = 0; i < n_styles; ++i) {
   4334             if (text_style & (1 << i)) {
   4335                 strcat(style_tag, text_styles[i]);
   4336             }
   4337         }
   4338     } else {
   4339         goto error;
   4340     }
   4341 
   4342     if (get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_BG_RGBCOLOR)) {
   4343   	char b[20];
   4344 
   4345 	#define BGTERMRGB "\x1b[48;2;"
   4346   	snprintf(b, sizeof(b), BGTERMRGB "%u;%u;%um", bg_color_number>>16, (bg_color_number&0xFF00)>>8, bg_color_number&0xFF);
   4347         strcat(style_tag, b);
   4348     }
   4349     else if (bg_color_number < n_bg_colors) {
   4350         strcat(style_tag, bg_colors[bg_color_number]);
   4351     } else {
   4352         goto error;
   4353     }
   4354 
   4355     return;
   4356 
   4357 error:
   4358     /* shouldn't be here */
   4359     assert(0);
   4360     style_tag[0] = '\0';
   4361     return;
   4362 }
   4363 
   4364 void get_reset_style_tag_for_cell(const f_table_properties_t *props,
   4365                                   size_t row, size_t col, char *reset_style_tag, size_t sz)
   4366 {
   4367     (void)sz;
   4368     size_t i = 0;
   4369 
   4370     unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_BG_COLOR);
   4371     unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_TEXT_STYLE);
   4372 
   4373     reset_style_tag[0] = '\0';
   4374 
   4375     if (text_style < (1U << n_styles)) {
   4376         for (i = 0; i < n_styles; ++i) {
   4377             if (text_style & (1 << i)) {
   4378                 if (i != 0) // FT_TSTYLE_DEFAULT
   4379                     goto reset_style;
   4380             }
   4381         }
   4382     } else {
   4383         goto error;
   4384     }
   4385 
   4386 reset_style:
   4387     strcat(reset_style_tag, UNIVERSAL_RESET_TAG);
   4388     return;
   4389 
   4390 error:
   4391     /* shouldn't be here */
   4392     assert(0);
   4393     reset_style_tag[0] = '\0';
   4394     return;
   4395 }
   4396 
   4397 
   4398 void get_style_tag_for_content(const f_table_properties_t *props,
   4399                                size_t row, size_t col, char *style_tag, size_t sz)
   4400 {
   4401     (void)sz;
   4402     size_t i = 0;
   4403 
   4404     unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_TEXT_STYLE);
   4405     unsigned fg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_FG_COLOR);
   4406     unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_BG_COLOR);
   4407 
   4408     style_tag[0] = '\0';
   4409 
   4410     if (text_style < (1U << n_styles)) {
   4411         for (i = 0; i < n_styles; ++i) {
   4412             if (text_style & (1 << i)) {
   4413                 strcat(style_tag, text_styles[i]);
   4414             }
   4415         }
   4416     } else {
   4417         goto error;
   4418     }
   4419 
   4420     if (fg_color_number < n_fg_colors) {
   4421         if (fg_color_number)
   4422             strcat(style_tag, fg_colors[fg_color_number]);
   4423     } else {
   4424         goto error;
   4425     }
   4426 
   4427     if (bg_color_number < n_bg_colors) {
   4428         strcat(style_tag, bg_colors[bg_color_number]);
   4429     } else {
   4430         goto error;
   4431     }
   4432 
   4433     return;
   4434 
   4435 error:
   4436     /* shouldn't be here */
   4437     assert(0);
   4438     style_tag[0] = '\0';
   4439     return;
   4440 }
   4441 
   4442 void get_reset_style_tag_for_content(const f_table_properties_t *props,
   4443                                      size_t row, size_t col, char *reset_style_tag, size_t sz)
   4444 {
   4445     (void)sz;
   4446     size_t i = 0;
   4447     size_t len = 0;
   4448 
   4449     unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_TEXT_STYLE);
   4450     unsigned fg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_FG_COLOR);
   4451     unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_BG_COLOR);
   4452 
   4453     reset_style_tag[0] = '\0';
   4454 
   4455     if (text_style < (1U << n_styles)) {
   4456         for (i = 0; i < n_styles; ++i) {
   4457             if (text_style & (1 << i)) {
   4458                 if (i != 0) // FT_TSTYLE_DEFAULT
   4459                     goto reset_style;
   4460             }
   4461         }
   4462     } else {
   4463         goto error;
   4464     }
   4465 
   4466     if (fg_color_number < n_fg_colors) {
   4467         if (fg_color_number)
   4468             goto reset_style;
   4469     } else {
   4470         goto error;
   4471     }
   4472 
   4473     if (bg_color_number < n_bg_colors) {
   4474         if (bg_color_number)
   4475             goto reset_style;
   4476     } else {
   4477         goto error;
   4478     }
   4479 
   4480     return;
   4481 
   4482 
   4483 reset_style:
   4484     strcat(reset_style_tag, UNIVERSAL_RESET_TAG);
   4485     len = strlen(reset_style_tag);
   4486     get_style_tag_for_cell(props, row, col, reset_style_tag + len, sz - len);
   4487     return;
   4488 
   4489 error:
   4490     /* shouldn't be here */
   4491     assert(0);
   4492     reset_style_tag[0] = '\0';
   4493     return;
   4494 }
   4495 
   4496 
   4497 static struct f_cell_props g_default_cell_properties = {
   4498     FT_ANY_ROW,    /* cell_row */
   4499     FT_ANY_COLUMN, /* cell_col */
   4500 
   4501     /* properties_flags */
   4502     FT_CPROP_MIN_WIDTH  | FT_CPROP_TEXT_ALIGN | FT_CPROP_TOP_PADDING
   4503     | FT_CPROP_BOTTOM_PADDING | FT_CPROP_LEFT_PADDING | FT_CPROP_RIGHT_PADDING
   4504     | FT_CPROP_EMPTY_STR_HEIGHT | FT_CPROP_CONT_FG_COLOR | FT_CPROP_CELL_BG_COLOR
   4505     | FT_CPROP_CONT_BG_COLOR | FT_CPROP_CELL_TEXT_STYLE | FT_CPROP_CONT_TEXT_STYLE,
   4506 
   4507     0,             /* col_min_width */
   4508     FT_ALIGNED_LEFT,  /* align */
   4509     0,      /* cell_padding_top         */
   4510     0,      /* cell_padding_bottom      */
   4511     1,      /* cell_padding_left        */
   4512     1,      /* cell_padding_right       */
   4513     1,      /* cell_empty_string_height */
   4514 
   4515     FT_ROW_COMMON, /* row_type */
   4516     FT_COLOR_DEFAULT, /* content_fg_color_number */
   4517     FT_COLOR_DEFAULT, /* content_bg_color_number */
   4518     FT_COLOR_DEFAULT, /* cell_bg_color_number */
   4519     FT_TSTYLE_DEFAULT, /* cell_text_style */
   4520     FT_TSTYLE_DEFAULT, /* content_text_style */
   4521     false,
   4522 };
   4523 
   4524 static int get_prop_value_if_exists_otherwise_default(const struct f_cell_props *cell_opts, uint32_t property)
   4525 {
   4526     if (cell_opts == NULL || !PROP_IS_SET(cell_opts->properties_flags, property)) {
   4527         cell_opts = &g_default_cell_properties;
   4528     }
   4529 
   4530     switch (property) {
   4531         case FT_CPROP_MIN_WIDTH:
   4532             return cell_opts->col_min_width;
   4533         case FT_CPROP_TEXT_ALIGN:
   4534             return cell_opts->align;
   4535         case FT_CPROP_TOP_PADDING:
   4536             return cell_opts->cell_padding_top;
   4537         case FT_CPROP_BOTTOM_PADDING:
   4538             return cell_opts->cell_padding_bottom;
   4539         case FT_CPROP_LEFT_PADDING:
   4540             return cell_opts->cell_padding_left;
   4541         case FT_CPROP_RIGHT_PADDING:
   4542             return cell_opts->cell_padding_right;
   4543         case FT_CPROP_EMPTY_STR_HEIGHT:
   4544             return cell_opts->cell_empty_string_height;
   4545         case FT_CPROP_ROW_TYPE:
   4546             return cell_opts->row_type;
   4547         case FT_CPROP_CONT_FG_COLOR:
   4548             return cell_opts->content_fg_color_number;
   4549         case FT_CPROP_CONT_BG_COLOR:
   4550             return cell_opts->content_bg_color_number;
   4551         case FT_CPROP_CELL_BG_COLOR:
   4552             return cell_opts->cell_bg_color_number;
   4553         case FT_CPROP_CELL_BG_RGBCOLOR:
   4554             return cell_opts->rgb;
   4555         case FT_CPROP_CELL_TEXT_STYLE:
   4556             return cell_opts->cell_text_style;
   4557         case FT_CPROP_CONT_TEXT_STYLE:
   4558             return cell_opts->content_text_style;
   4559         default:
   4560             /* todo: implement later */
   4561             exit(333);
   4562     }
   4563 }
   4564 
   4565 
   4566 FT_INTERNAL
   4567 f_cell_prop_container_t *create_cell_prop_container(void)
   4568 {
   4569     f_cell_prop_container_t *ret = create_vector(sizeof(f_cell_props_t), DEFAULT_VECTOR_CAPACITY);
   4570     return ret;
   4571 }
   4572 
   4573 
   4574 FT_INTERNAL
   4575 void destroy_cell_prop_container(f_cell_prop_container_t *cont)
   4576 {
   4577     if (cont)
   4578         destroy_vector(cont);
   4579 }
   4580 
   4581 
   4582 FT_INTERNAL
   4583 const f_cell_props_t *cget_cell_prop(const f_cell_prop_container_t *cont, size_t row, size_t col)
   4584 {
   4585     assert(cont);
   4586     size_t sz = vector_size(cont);
   4587     size_t i = 0;
   4588     for (i = 0; i < sz; ++i) {
   4589         const f_cell_props_t *opt = &VECTOR_AT_C(cont, i, const f_cell_props_t);
   4590         if (opt->cell_row == row && opt->cell_col == col)
   4591             return opt;
   4592     }
   4593     return NULL;
   4594 }
   4595 
   4596 
   4597 FT_INTERNAL
   4598 f_cell_props_t *get_cell_prop_and_create_if_not_exists(f_cell_prop_container_t *cont, size_t row, size_t col)
   4599 {
   4600     assert(cont);
   4601     size_t sz = vector_size(cont);
   4602     size_t i = 0;
   4603     for (i = 0; i < sz; ++i) {
   4604         f_cell_props_t *opt = &VECTOR_AT(cont, i, f_cell_props_t);
   4605         if (opt->cell_row == row && opt->cell_col == col)
   4606             return opt;
   4607     }
   4608 
   4609     f_cell_props_t opt;
   4610     if (row == FT_ANY_ROW && col == FT_ANY_COLUMN)
   4611         memcpy(&opt, &g_default_cell_properties, sizeof(f_cell_props_t));
   4612     else
   4613         memset(&opt, 0, sizeof(f_cell_props_t));
   4614 
   4615     opt.cell_row = row;
   4616     opt.cell_col = col;
   4617     if (FT_IS_SUCCESS(vector_push(cont, &opt))) {
   4618         return &VECTOR_AT(cont, sz, f_cell_props_t);
   4619     }
   4620 
   4621     return NULL;
   4622 }
   4623 
   4624 
   4625 FT_INTERNAL
   4626 int get_cell_property_hierarchically(const f_table_properties_t *propertiess, size_t row, size_t column, uint32_t property)
   4627 {
   4628     assert(propertiess);
   4629     size_t row_origin = row;
   4630 
   4631     const f_cell_props_t *opt = NULL;
   4632     if (propertiess->cell_properties != NULL) {
   4633         while (1) {
   4634             opt = cget_cell_prop(propertiess->cell_properties, row, column);
   4635             if (opt != NULL && PROP_IS_SET(opt->properties_flags, property))
   4636                 break;
   4637 
   4638             if (row != FT_ANY_ROW && column != FT_ANY_COLUMN) {
   4639                 row = FT_ANY_ROW;
   4640                 continue;
   4641             } else if (row == FT_ANY_ROW && column != FT_ANY_COLUMN) {
   4642                 row = row_origin;
   4643                 column = FT_ANY_COLUMN;
   4644                 continue;
   4645             } else if (row != FT_ANY_ROW  && column == FT_ANY_COLUMN) {
   4646                 row = FT_ANY_ROW;
   4647                 column = FT_ANY_COLUMN;
   4648                 continue;
   4649             }
   4650 
   4651             opt = NULL;
   4652             break;
   4653         }
   4654     }
   4655 
   4656     return get_prop_value_if_exists_otherwise_default(opt, property);
   4657 }
   4658 
   4659 
   4660 static f_status set_cell_property_impl(f_cell_props_t *opt, uint32_t property, int value)
   4661 {
   4662     assert(opt);
   4663 
   4664     PROP_SET(opt->properties_flags, property);
   4665     if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) {
   4666         CHECK_NOT_NEGATIVE(value);
   4667         opt->col_min_width = value;
   4668     } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) {
   4669         opt->align = (enum ft_text_alignment)value;
   4670     } else if (PROP_IS_SET(property, FT_CPROP_TOP_PADDING)) {
   4671         CHECK_NOT_NEGATIVE(value);
   4672         opt->cell_padding_top = value;
   4673     } else if (PROP_IS_SET(property, FT_CPROP_BOTTOM_PADDING)) {
   4674         CHECK_NOT_NEGATIVE(value);
   4675         opt->cell_padding_bottom = value;
   4676     } else if (PROP_IS_SET(property, FT_CPROP_LEFT_PADDING)) {
   4677         CHECK_NOT_NEGATIVE(value);
   4678         opt->cell_padding_left = value;
   4679     } else if (PROP_IS_SET(property, FT_CPROP_RIGHT_PADDING)) {
   4680         CHECK_NOT_NEGATIVE(value);
   4681         opt->cell_padding_right = value;
   4682     } else if (PROP_IS_SET(property, FT_CPROP_EMPTY_STR_HEIGHT)) {
   4683         CHECK_NOT_NEGATIVE(value);
   4684         opt->cell_empty_string_height = value;
   4685     } else if (PROP_IS_SET(property, FT_CPROP_ROW_TYPE)) {
   4686         opt->row_type = (enum ft_row_type)value;
   4687     } else if (PROP_IS_SET(property, FT_CPROP_CONT_FG_COLOR)) {
   4688         opt->content_fg_color_number = value;
   4689     } else if (PROP_IS_SET(property, FT_CPROP_CONT_BG_COLOR)) {
   4690         opt->content_bg_color_number = value;
   4691     } else if (PROP_IS_SET(property, FT_CPROP_CELL_BG_COLOR)) {
   4692         opt->cell_bg_color_number = value;
   4693     } else if (PROP_IS_SET(property, FT_CPROP_CELL_BG_RGBCOLOR)) {
   4694         opt->cell_bg_color_number = value;
   4695 	opt->rgb = true;
   4696     } else if (PROP_IS_SET(property, FT_CPROP_CELL_TEXT_STYLE)) {
   4697         enum ft_text_style v = (enum ft_text_style)value;
   4698         if (v == FT_TSTYLE_DEFAULT) {
   4699             opt->cell_text_style = FT_TSTYLE_DEFAULT;
   4700         } else {
   4701             opt->cell_text_style = (enum ft_text_style)(opt->cell_text_style | v);
   4702         }
   4703     } else if (PROP_IS_SET(property, FT_CPROP_CONT_TEXT_STYLE)) {
   4704         enum ft_text_style v = (enum ft_text_style)value;
   4705         if (v == FT_TSTYLE_DEFAULT) {
   4706             opt->content_text_style = v;
   4707         } else {
   4708             opt->content_text_style = (enum ft_text_style)(opt->content_text_style | v);
   4709         }
   4710     }
   4711 
   4712     return FT_SUCCESS;
   4713 
   4714 fort_fail:
   4715     return FT_EINVAL;
   4716 }
   4717 
   4718 
   4719 FT_INTERNAL
   4720 f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t col, uint32_t property, int value)
   4721 {
   4722     f_cell_props_t *opt = get_cell_prop_and_create_if_not_exists(cont, row, col);
   4723     if (opt == NULL)
   4724         return FT_GEN_ERROR;
   4725 
   4726     return set_cell_property_impl(opt, property, value);
   4727     /*
   4728     PROP_SET(opt->propertiess, property);
   4729     if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) {
   4730         opt->col_min_width = value;
   4731     } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) {
   4732         opt->align = value;
   4733     }
   4734 
   4735     return FT_SUCCESS;
   4736     */
   4737 }
   4738 
   4739 
   4740 FT_INTERNAL
   4741 f_status set_default_cell_property(uint32_t property, int value)
   4742 {
   4743     return set_cell_property_impl(&g_default_cell_properties, property, value);
   4744 }
   4745 
   4746 
   4747 #define BASIC_STYLE  {            \
   4748     /* border_chars */            \
   4749     {                             \
   4750      "+", "-", "+", "+",          \
   4751      "|", "|", "|",               \
   4752      "\0", "\0", "\0", "\0",      \
   4753      "+", "-", "+", "+",          \
   4754      "+", "+", "+", "+",          \
   4755     },                            \
   4756     /* header_border_chars */     \
   4757     {                             \
   4758     "+", "-", "+", "+",           \
   4759     "|", "|", "|",                \
   4760     "+", "-", "+", "+",           \
   4761     "+", "-", "+", "+",           \
   4762     "+", "+", "+", "+",           \
   4763     },                            \
   4764     /* separator_chars */         \
   4765     {                             \
   4766     "+", "-", "+", "+",           \
   4767     "+", "+",                     \
   4768     },                            \
   4769 }
   4770 
   4771 #define BASIC2_STYLE  {           \
   4772     /* border_chars */            \
   4773     {                             \
   4774      "+", "-", "+", "+",          \
   4775      "|", "|", "|",               \
   4776      "+", "-", "+", "+",          \
   4777      "+", "-", "+", "+",          \
   4778      "+", "+", "+", "+",          \
   4779     },                            \
   4780     /* header_border_chars */     \
   4781     {                             \
   4782     "+", "-", "+", "+",           \
   4783     "|", "|", "|",                \
   4784     "+", "-", "+", "+",           \
   4785     "+", "-", "+", "+",           \
   4786     "+", "+", "+", "+",           \
   4787     },                            \
   4788     /* separator_chars */         \
   4789     {                             \
   4790     "+", "-", "+", "+",           \
   4791     "+", "+",                     \
   4792     },                            \
   4793 }
   4794 
   4795 #define SIMPLE_STYLE  {           \
   4796     /* border_chars */            \
   4797     {                             \
   4798     "\0", "\0", "\0", "\0",       \
   4799     "\0", " ", "\0",              \
   4800     "\0", "\0", "\0", "\0",       \
   4801     "\0", "\0", "\0", "\0",       \
   4802     "\0", "\0", "\0", "\0",       \
   4803     },                            \
   4804     /* header_border_chars */     \
   4805     {                             \
   4806     "\0", "\0", "\0", "\0",       \
   4807     "\0", " ", "\0",              \
   4808     "\0", "─", " ", "\0",         \
   4809     "\0", " ", " ", "\0",         \
   4810     " ", "─", " ", "─",           \
   4811     },                            \
   4812     /* separator_chars */         \
   4813     {                             \
   4814     "\0", "─", " ", "\0",         \
   4815     " ", " ",                     \
   4816     },                            \
   4817 }
   4818 
   4819 #define PLAIN_STYLE  {            \
   4820     /* border_chars */            \
   4821     {                             \
   4822     "\0", "\0", "\0", "\0",       \
   4823     "\0", " ", "\0",              \
   4824     "\0", "\0", "\0", "\0",       \
   4825     "\0", "\0", "\0", "\0",       \
   4826     "\0", "\0", "\0", "\0",       \
   4827     },                            \
   4828     /* header_border_chars */     \
   4829     {                             \
   4830     "\0", "-", "-", "\0",         \
   4831     "\0", " ", "\0",              \
   4832     "\0", "-", "-", "\0",         \
   4833     "\0", "-", "-", "\0",         \
   4834     " ", "-", " ", "-",           \
   4835     },                            \
   4836     /* separator_chars */         \
   4837     {                             \
   4838     "\0", "-", "-", "\0",         \
   4839     "-", "-",                     \
   4840     },                            \
   4841 }
   4842 
   4843 #define DOT_STYLE  {              \
   4844     /* border_chars */            \
   4845     {                             \
   4846      ".", ".", ".", ".",          \
   4847      ":", ":", ":",               \
   4848      "\0", "\0", "\0", "\0",      \
   4849      ":", ".", ":", ":",          \
   4850      "+", ":", "+", ":",          \
   4851     },                            \
   4852     /* header_border_chars */     \
   4853     {                             \
   4854     ".", ".", ".", ".",           \
   4855     ":", ":", ":",                \
   4856     ":", ".", ":", ":",           \
   4857     ":", ".", ":", ":",           \
   4858     "+", ".", "+", ".",           \
   4859     },                            \
   4860     /* separator_chars */         \
   4861     {                             \
   4862     ":", ".", ":", ":",           \
   4863     ":", ":",                     \
   4864     },                            \
   4865 }
   4866 
   4867 #define EMPTY_STYLE  {            \
   4868     /* border_chars */            \
   4869     {                             \
   4870      "\0", "\0", "\0", "\0",      \
   4871      "\0", "\0", "\0",            \
   4872      "\0", "\0", "\0", "\0",      \
   4873      "\0", "\0", "\0", "\0",      \
   4874      "\0", "\0", "\0", "\0",      \
   4875     },                            \
   4876     /* header_border_chars */     \
   4877     {                             \
   4878     "\0", "\0", "\0", "\0",       \
   4879     "\0", "\0", "\0",             \
   4880     "\0", "\0", "\0", "\0",       \
   4881     "\0", "\0", "\0", "\0",       \
   4882     "\0", "\0", "\0", "\0",       \
   4883     },                            \
   4884     /* separator_chars */         \
   4885     {                             \
   4886     "\0", " ", "\0 ", "\0",       \
   4887     "\0", "\0",                   \
   4888     },                            \
   4889 }
   4890 
   4891 
   4892 #define EMPTY2_STYLE  {            \
   4893     /* border_chars */            \
   4894     {                             \
   4895      " ", " ", " ", " ",          \
   4896      " ", " ", " ",               \
   4897      "\0", "\0", "\0", "\0",      \
   4898      " ", " ", " ", " ",          \
   4899      " ", " ", " ", " ",          \
   4900     },                            \
   4901     /* header_border_chars */     \
   4902     {                             \
   4903     " ", " ", " ", " ",           \
   4904     " ", " ", " ",                \
   4905     "\0", "\0", "\0", "\0",       \
   4906     " ", " ", " ", " ",           \
   4907     " ", " ", " ", " ",           \
   4908     },                            \
   4909     /* separator_chars */         \
   4910     {                             \
   4911     " ", " ", " ", " ",           \
   4912     " ", " ",                     \
   4913     },                            \
   4914 }
   4915 
   4916 #define SOLID_STYLE  {            \
   4917     /* border_chars */            \
   4918     {                             \
   4919      "┌", "─", "┬", "┐",          \
   4920      "│", "│", "│",               \
   4921      "", "", "", "",              \
   4922      "└", "─", "┴", "┘",          \
   4923      "│", "─", "│", "─",          \
   4924     },                            \
   4925     /* header_border_chars */     \
   4926     {                             \
   4927     "┌", "─", "┬", "┐",           \
   4928     "│", "│", "│",                \
   4929     "├", "─", "┼", "┤",           \
   4930     "└", "─", "┴", "┘",           \
   4931     "┼", "┬", "┼", "┴",           \
   4932     },                            \
   4933     /* separator_chars */         \
   4934     {                             \
   4935     "├", "─", "┼", "┤",           \
   4936     "┬", "┴",                     \
   4937     },                            \
   4938 }
   4939 
   4940 #define SOLID_ROUND_STYLE  {      \
   4941     /* border_chars */            \
   4942     {                             \
   4943      "╭", "─", "┬", "╮",          \
   4944      "│", "│", "│",               \
   4945      "", "", "", "",              \
   4946      "╰", "─", "┴", "╯",          \
   4947      "│", "─", "│", "─",          \
   4948     },                            \
   4949     /* header_border_chars */     \
   4950     {                             \
   4951     "╭", "─", "┬", "╮",           \
   4952     "│", "│", "│",                \
   4953     "├", "─", "┼", "┤",           \
   4954     "╰", "─", "┴", "╯",           \
   4955     "┼", "┬", "┼", "┴",           \
   4956     },                            \
   4957     /* separator_chars */         \
   4958     {                             \
   4959     "├", "─", "┼", "┤",           \
   4960     "┬", "┴",                     \
   4961     },                            \
   4962 }
   4963 
   4964 #define NICE_STYLE  {             \
   4965     /* border_chars */            \
   4966     {                             \
   4967      "╔", "═", "╦", "╗",          \
   4968      "║", "║", "║",               \
   4969      "", "", "", "",              \
   4970      "╚", "═", "╩", "╝",          \
   4971      "┣", "┻", "┣", "┳",          \
   4972     },                            \
   4973     /* header_border_chars */     \
   4974     {                             \
   4975     "╔", "═", "╦", "╗",           \
   4976     "║", "║", "║",                \
   4977     "╠", "═", "╬", "╣",           \
   4978     "╚", "═", "╩", "╝",           \
   4979     "┣", "╦", "┣", "╩",           \
   4980     },                            \
   4981     /* separator_chars */         \
   4982     {                             \
   4983     "╟", "─", "╫", "╢",           \
   4984     "╥", "╨",                     \
   4985     },                            \
   4986 }
   4987 
   4988 #define DOUBLE_STYLE  {         \
   4989     /* border_chars */          \
   4990     {                           \
   4991      "╔", "═", "╦", "╗",        \
   4992      "║", "║", "║",             \
   4993      "", "", "", "",            \
   4994      "╚", "═", "╩", "╝",        \
   4995      "┣", "┻", "┣", "┳",        \
   4996     },                          \
   4997     /* header_border_chars */   \
   4998     {                           \
   4999     "╔", "═", "╦", "╗",         \
   5000     "║", "║", "║",              \
   5001     "╠", "═", "╬", "╣",         \
   5002     "╚", "═", "╩", "╝",         \
   5003     "┣", "╦", "┣", "╩",         \
   5004     },                          \
   5005     /* separator_chars */       \
   5006     {                           \
   5007     "╠", "═", "╬", "╣",         \
   5008     "╦", "╩",                   \
   5009     },                          \
   5010 }
   5011 
   5012 
   5013 
   5014 
   5015 #define DOUBLE2_STYLE  {          \
   5016     /* border_chars */            \
   5017     {                             \
   5018      "╔", "═", "╤", "╗",          \
   5019      "║", "│", "║",               \
   5020      "╟", "─", "┼", "╢",          \
   5021      "╚", "═", "╧", "╝",          \
   5022      "├", "┬", "┤", "┴",          \
   5023     },                            \
   5024     /* header_border_chars */     \
   5025     {                             \
   5026     "╔", "═", "╤", "╗",           \
   5027     "║", "│", "║",                \
   5028     "╠", "═", "╪", "╣",           \
   5029     "╚", "═", "╧", "╝",           \
   5030     "├", "╤", "┤", "╧",           \
   5031     },                            \
   5032     /* separator_chars */         \
   5033     {                             \
   5034     "╠", "═", "╪", "╣",           \
   5035     "╤", "╧",                     \
   5036     },                            \
   5037 }
   5038 
   5039 
   5040 #define BOLD_STYLE  {             \
   5041     /* border_chars */            \
   5042     {                             \
   5043      "┏", "━", "┳", "┓",          \
   5044      "┃", "┃", "┃",               \
   5045      "", "", "", "",              \
   5046      "┗", "━", "┻", "┛",          \
   5047      "┣", "┻", "┣", "┳",          \
   5048     },                            \
   5049     /* header_border_chars */     \
   5050     {                             \
   5051     "┏", "━", "┳", "┓",           \
   5052     "┃", "┃", "┃",                \
   5053     "┣", "━", "╋", "┫",           \
   5054     "┗", "━", "┻", "┛",           \
   5055     "┣", "┳", "┣", "┻",           \
   5056     },                            \
   5057     /* separator_chars */         \
   5058     {                             \
   5059     "┣", "━", "╋", "┫",           \
   5060     "┳", "┻",                     \
   5061     },                            \
   5062 }
   5063 
   5064 #define BOLD2_STYLE  {            \
   5065     /* border_chars */            \
   5066     {                             \
   5067      "┏", "━", "┯", "┓",          \
   5068      "┃", "│", "┃",               \
   5069      "┠", "─", "┼", "┨",          \
   5070      "┗", "━", "┷", "┛",          \
   5071      "┣", "┬", "┣", "┴",          \
   5072     },                            \
   5073     /* header_border_chars */     \
   5074     {                             \
   5075     "┏", "━", "┯", "┓",           \
   5076     "┃", "│", "┃",                \
   5077     "┣", "━", "┿", "┫",           \
   5078     "┗", "━", "┷", "┛",           \
   5079     "┣", "┯", "┣", "┷",           \
   5080     },                            \
   5081     /* separator_chars */         \
   5082     {                             \
   5083     "┣", "━", "┿", "┫",           \
   5084     "┯", "┷",                     \
   5085     },                            \
   5086 }
   5087 
   5088 #define FRAME_STYLE  {             \
   5089     /* border_chars */            \
   5090     {                             \
   5091      "▛", "▀", "▀", "▜",          \
   5092      "▌", "┃", "▐",               \
   5093      "", "", "", "",              \
   5094      "▙", "▄", "▄", "▟",          \
   5095      "┣", "━", "┣", "━"           \
   5096     },                            \
   5097     /* header_border_chars */     \
   5098     {                             \
   5099     "▛", "▀", "▀", "▜",           \
   5100     "▌", "┃", "▐",                \
   5101     "▌", "━", "╋", "▐",           \
   5102     "▙", "▄", "▄", "▟",           \
   5103     "┣", "━", "┣", "━",           \
   5104     },                            \
   5105     /* separator_chars */         \
   5106     {                             \
   5107     "▌", "━", "╋", "▐",           \
   5108     "╋", "╋",                     \
   5109     },                            \
   5110 }
   5111 
   5112 
   5113 struct fort_border_style FORT_BASIC_STYLE = BASIC_STYLE;
   5114 struct fort_border_style FORT_BASIC2_STYLE = BASIC2_STYLE;
   5115 struct fort_border_style FORT_SIMPLE_STYLE = SIMPLE_STYLE;
   5116 struct fort_border_style FORT_PLAIN_STYLE = PLAIN_STYLE;
   5117 struct fort_border_style FORT_DOT_STYLE = DOT_STYLE;
   5118 struct fort_border_style FORT_EMPTY_STYLE = EMPTY_STYLE;
   5119 struct fort_border_style FORT_EMPTY2_STYLE = EMPTY2_STYLE;
   5120 struct fort_border_style FORT_SOLID_STYLE = SOLID_STYLE;
   5121 struct fort_border_style FORT_SOLID_ROUND_STYLE = SOLID_ROUND_STYLE;
   5122 struct fort_border_style FORT_NICE_STYLE = NICE_STYLE;
   5123 struct fort_border_style FORT_DOUBLE_STYLE = DOUBLE_STYLE;
   5124 struct fort_border_style FORT_DOUBLE2_STYLE = DOUBLE2_STYLE;
   5125 struct fort_border_style FORT_BOLD_STYLE = BOLD_STYLE;
   5126 struct fort_border_style FORT_BOLD2_STYLE = BOLD2_STYLE;
   5127 struct fort_border_style FORT_FRAME_STYLE = FRAME_STYLE;
   5128 
   5129 
   5130 
   5131 fort_entire_table_properties_t g_entire_table_properties = {
   5132     0, /* left_margin */
   5133     0, /* top_margin */
   5134     0, /* right_margin */
   5135     0, /* bottom_margin */
   5136     FT_STRATEGY_REPLACE, /* add_strategy */
   5137 };
   5138 
   5139 static f_status set_entire_table_property_internal(fort_entire_table_properties_t *properties, uint32_t property, int value)
   5140 {
   5141     assert(properties);
   5142     CHECK_NOT_NEGATIVE(value);
   5143     if (PROP_IS_SET(property, FT_TPROP_LEFT_MARGIN)) {
   5144         properties->left_margin = value;
   5145     } else if (PROP_IS_SET(property, FT_TPROP_TOP_MARGIN)) {
   5146         properties->top_margin = value;
   5147     } else if (PROP_IS_SET(property, FT_TPROP_RIGHT_MARGIN)) {
   5148         properties->right_margin = value;
   5149     } else if (PROP_IS_SET(property, FT_TPROP_BOTTOM_MARGIN)) {
   5150         properties->bottom_margin = value;
   5151     } else if (PROP_IS_SET(property, FT_TPROP_ADDING_STRATEGY)) {
   5152         properties->add_strategy = (enum ft_adding_strategy)value;
   5153     } else {
   5154         return FT_EINVAL;
   5155     }
   5156     return FT_SUCCESS;
   5157 
   5158 fort_fail:
   5159     return FT_EINVAL;
   5160 }
   5161 
   5162 
   5163 FT_INTERNAL
   5164 f_status set_entire_table_property(f_table_properties_t *table_properties, uint32_t property, int value)
   5165 {
   5166     assert(table_properties);
   5167     return set_entire_table_property_internal(&table_properties->entire_table_properties, property, value);
   5168 }
   5169 
   5170 
   5171 FT_INTERNAL
   5172 f_status set_default_entire_table_property(uint32_t property, int value)
   5173 {
   5174     return set_entire_table_property_internal(&g_entire_table_properties, property, value);
   5175 }
   5176 
   5177 
   5178 FT_INTERNAL
   5179 size_t max_border_elem_strlen(struct f_table_properties *properties)
   5180 {
   5181     assert(properties);
   5182     size_t result = 1;
   5183     int i = 0;
   5184     for (i = 0; i < BORDER_ITEM_POS_SIZE; ++i) {
   5185         result = MAX(result, strlen(properties->border_style.border_chars[i]));
   5186     }
   5187 
   5188     for (i = 0; i < BORDER_ITEM_POS_SIZE; ++i) {
   5189         result = MAX(result, strlen(properties->border_style.header_border_chars[i]));
   5190     }
   5191 
   5192     for (i = 0; i < SEPARATOR_ITEM_POS_SIZE; ++i) {
   5193         result = MAX(result, strlen(properties->border_style.separator_chars[i]));
   5194     }
   5195     return result;
   5196 }
   5197 
   5198 
   5199 f_table_properties_t g_table_properties = {
   5200     /* border_style */
   5201     BASIC_STYLE,
   5202     NULL,     /* cell_properties */
   5203     /* entire_table_properties */
   5204     {
   5205         0, /* left_margin */
   5206         0, /* top_margin */
   5207         0, /* right_margin */
   5208         0,  /* bottom_margin */
   5209         FT_STRATEGY_REPLACE, /* add_strategy */
   5210     }
   5211 };
   5212 
   5213 
   5214 FT_INTERNAL
   5215 f_table_properties_t *create_table_properties(void)
   5216 {
   5217     f_table_properties_t *properties = (f_table_properties_t *)F_CALLOC(sizeof(f_table_properties_t), 1);
   5218     if (properties == NULL) {
   5219         return NULL;
   5220     }
   5221     memcpy(properties, &g_table_properties, sizeof(f_table_properties_t));
   5222     properties->cell_properties = create_cell_prop_container();
   5223     if (properties->cell_properties == NULL) {
   5224         destroy_table_properties(properties);
   5225         return NULL;
   5226     }
   5227     memcpy(&properties->entire_table_properties, &g_entire_table_properties, sizeof(fort_entire_table_properties_t));
   5228     return properties;
   5229 }
   5230 
   5231 FT_INTERNAL
   5232 void destroy_table_properties(f_table_properties_t *properties)
   5233 {
   5234     if (properties == NULL)
   5235         return;
   5236 
   5237     if (properties->cell_properties != NULL) {
   5238         destroy_cell_prop_container(properties->cell_properties);
   5239     }
   5240     F_FREE(properties);
   5241 }
   5242 
   5243 static
   5244 f_cell_prop_container_t *copy_cell_properties(f_cell_prop_container_t *cont)
   5245 {
   5246     f_cell_prop_container_t *result = create_cell_prop_container();
   5247     if (result == NULL)
   5248         return NULL;
   5249 
   5250     size_t i = 0;
   5251     size_t sz = vector_size(cont);
   5252     for (i = 0; i < sz; ++i) {
   5253         f_cell_props_t *opt = (f_cell_props_t *)vector_at(cont, i);
   5254         if (FT_IS_ERROR(vector_push(result, opt))) {
   5255             destroy_cell_prop_container(result);
   5256             return NULL;
   5257         }
   5258     }
   5259     return result;
   5260 }
   5261 
   5262 FT_INTERNAL
   5263 f_table_properties_t *copy_table_properties(const f_table_properties_t *properties)
   5264 {
   5265     f_table_properties_t *new_opt = create_table_properties();
   5266     if (new_opt == NULL)
   5267         return NULL;
   5268 
   5269     destroy_vector(new_opt->cell_properties);
   5270     new_opt->cell_properties = copy_cell_properties(properties->cell_properties);
   5271     if (new_opt->cell_properties == NULL) {
   5272         destroy_table_properties(new_opt);
   5273         return NULL;
   5274     }
   5275 
   5276     memcpy(&new_opt->border_style, &properties->border_style, sizeof(struct fort_border_style));
   5277     memcpy(&new_opt->entire_table_properties,
   5278            &properties->entire_table_properties, sizeof(fort_entire_table_properties_t));
   5279 
   5280     return new_opt;
   5281 }
   5282 
   5283 /********************************************************
   5284    End of file "properties.c"
   5285  ********************************************************/
   5286 
   5287 
   5288 /********************************************************
   5289    Begin of file "row.c"
   5290  ********************************************************/
   5291 
   5292 #include <assert.h>
   5293 #include <ctype.h>
   5294 /* #include "row.h" */ /* Commented by amalgamation script */
   5295 /* #include "cell.h" */ /* Commented by amalgamation script */
   5296 /* #include "string_buffer.h" */ /* Commented by amalgamation script */
   5297 /* #include "vector.h" */ /* Commented by amalgamation script */
   5298 
   5299 
   5300 struct f_row {
   5301     f_vector_t *cells;
   5302 };
   5303 
   5304 static
   5305 f_row_t *create_row_impl(f_vector_t *cells)
   5306 {
   5307     f_row_t *row = (f_row_t *)F_CALLOC(1, sizeof(f_row_t));
   5308     if (row == NULL)
   5309         return NULL;
   5310     if (cells) {
   5311         row->cells = cells;
   5312     } else {
   5313         row->cells = create_vector(sizeof(f_cell_t *), DEFAULT_VECTOR_CAPACITY);
   5314         if (row->cells == NULL) {
   5315             F_FREE(row);
   5316             return NULL;
   5317         }
   5318     }
   5319     return row;
   5320 }
   5321 
   5322 FT_INTERNAL
   5323 f_row_t *create_row(void)
   5324 {
   5325     return create_row_impl(NULL);
   5326 }
   5327 
   5328 static
   5329 void destroy_each_cell(f_vector_t *cells)
   5330 {
   5331     size_t i = 0;
   5332     size_t cells_n = vector_size(cells);
   5333     for (i = 0; i < cells_n; ++i) {
   5334         f_cell_t *cell = VECTOR_AT(cells, i, f_cell_t *);
   5335         destroy_cell(cell);
   5336     }
   5337 }
   5338 
   5339 FT_INTERNAL
   5340 void destroy_row(f_row_t *row)
   5341 {
   5342     if (row == NULL)
   5343         return;
   5344 
   5345     if (row->cells) {
   5346         destroy_each_cell(row->cells);
   5347         destroy_vector(row->cells);
   5348     }
   5349 
   5350     F_FREE(row);
   5351 }
   5352 
   5353 FT_INTERNAL
   5354 f_row_t *copy_row(f_row_t *row)
   5355 {
   5356     assert(row);
   5357     f_row_t *result = create_row();
   5358     if (result == NULL)
   5359         return NULL;
   5360 
   5361     size_t i = 0;
   5362     size_t cols_n = vector_size(row->cells);
   5363     for (i = 0; i < cols_n; ++i) {
   5364         f_cell_t *cell = VECTOR_AT(row->cells, i, f_cell_t *);
   5365         f_cell_t *new_cell = copy_cell(cell);
   5366         if (new_cell == NULL) {
   5367             destroy_row(result);
   5368             return NULL;
   5369         }
   5370         vector_push(result->cells, &new_cell);
   5371     }
   5372 
   5373     return result;
   5374 }
   5375 
   5376 FT_INTERNAL
   5377 f_row_t *split_row(f_row_t *row, size_t pos)
   5378 {
   5379     assert(row);
   5380 
   5381     f_vector_t *cells = vector_split(row->cells, pos);
   5382     if (!cells)
   5383         return NULL;
   5384     f_row_t *tail = create_row_impl(cells);
   5385     if (!tail) {
   5386         destroy_each_cell(cells);
   5387         destroy_vector(cells);
   5388     }
   5389     return tail;
   5390 }
   5391 
   5392 FT_INTERNAL
   5393 int ft_row_erase_range(f_row_t *row, size_t left, size_t right)
   5394 {
   5395     assert(row);
   5396     size_t cols_n = vector_size(row->cells);
   5397     if (cols_n == 0 || (right < left))
   5398         return FT_SUCCESS;
   5399 
   5400     f_cell_t *cell = NULL;
   5401     size_t i = left;
   5402     while (i < cols_n && i <= right) {
   5403         cell = VECTOR_AT(row->cells, i, f_cell_t *);
   5404         destroy_cell(cell);
   5405         ++i;
   5406     }
   5407     size_t n_destroy = MIN(cols_n - 1, right) - left + 1;
   5408     while (n_destroy--) {
   5409         vector_erase(row->cells, left);
   5410     }
   5411     return FT_SUCCESS;
   5412 }
   5413 
   5414 FT_INTERNAL
   5415 size_t columns_in_row(const f_row_t *row)
   5416 {
   5417     if (row == NULL || row->cells == NULL)
   5418         return 0;
   5419 
   5420     return vector_size(row->cells);
   5421 }
   5422 
   5423 
   5424 static
   5425 f_cell_t *get_cell_impl(f_row_t *row, size_t col, enum f_get_policy policy)
   5426 {
   5427     if (row == NULL || row->cells == NULL) {
   5428         return NULL;
   5429     }
   5430 
   5431     switch (policy) {
   5432         case DONT_CREATE_ON_NULL:
   5433             if (col < columns_in_row(row)) {
   5434                 return VECTOR_AT(row->cells, col, f_cell_t *);
   5435             }
   5436             return NULL;
   5437         case CREATE_ON_NULL:
   5438             while (col >= columns_in_row(row)) {
   5439                 f_cell_t *new_cell = create_cell();
   5440                 if (new_cell == NULL)
   5441                     return NULL;
   5442                 if (FT_IS_ERROR(vector_push(row->cells, &new_cell))) {
   5443                     destroy_cell(new_cell);
   5444                     return NULL;
   5445                 }
   5446             }
   5447             return VECTOR_AT(row->cells, col, f_cell_t *);
   5448     }
   5449 
   5450     assert(0 && "Shouldn't be here!");
   5451     return NULL;
   5452 }
   5453 
   5454 
   5455 FT_INTERNAL
   5456 f_cell_t *get_cell(f_row_t *row, size_t col)
   5457 {
   5458     return get_cell_impl(row, col, DONT_CREATE_ON_NULL);
   5459 }
   5460 
   5461 
   5462 FT_INTERNAL
   5463 const f_cell_t *get_cell_c(const f_row_t *row, size_t col)
   5464 {
   5465     return get_cell((f_row_t *)row, col);
   5466 }
   5467 
   5468 
   5469 FT_INTERNAL
   5470 f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col)
   5471 {
   5472     return get_cell_impl(row, col, CREATE_ON_NULL);
   5473 }
   5474 
   5475 FT_INTERNAL
   5476 f_cell_t *create_cell_in_position(f_row_t *row, size_t col)
   5477 {
   5478     if (row == NULL || row->cells == NULL) {
   5479         return NULL;
   5480     }
   5481 
   5482     f_cell_t *new_cell = create_cell();
   5483     if (new_cell == NULL)
   5484         return NULL;
   5485     if (FT_IS_ERROR(vector_insert(row->cells, &new_cell, col))) {
   5486         destroy_cell(new_cell);
   5487         return NULL;
   5488     }
   5489     return VECTOR_AT(row->cells, col, f_cell_t *);
   5490 }
   5491 
   5492 
   5493 FT_INTERNAL
   5494 f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
   5495 {
   5496     assert(cur_row);
   5497     assert(ins_row);
   5498     size_t cur_sz = vector_size(cur_row->cells);
   5499     if (cur_sz == 0 && pos == 0) {
   5500         f_row_t tmp;
   5501         memcpy(&tmp, cur_row, sizeof(f_row_t));
   5502         memcpy(cur_row, ins_row, sizeof(f_row_t));
   5503         memcpy(ins_row, &tmp, sizeof(f_row_t));
   5504         return FT_SUCCESS;
   5505     }
   5506 
   5507     // Append empty cells to `cur_row` if needed.
   5508     while (vector_size(cur_row->cells) < pos) {
   5509         create_cell_in_position(cur_row, vector_size(cur_row->cells));
   5510     }
   5511 
   5512     return vector_swap(cur_row->cells, ins_row->cells, pos);
   5513 }
   5514 
   5515 /* Ownership of cells of `ins_row` is passed to `cur_row`. */
   5516 FT_INTERNAL
   5517 f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos)
   5518 {
   5519     assert(cur_row);
   5520     assert(ins_row);
   5521 
   5522     while (vector_size(cur_row->cells) < pos) {
   5523         f_cell_t *new_cell = create_cell();
   5524         if (!new_cell)
   5525             return FT_GEN_ERROR;
   5526         vector_push(cur_row->cells, &new_cell);
   5527     }
   5528 
   5529     size_t sz = vector_size(ins_row->cells);
   5530     size_t i = 0;
   5531     for (i = 0; i < sz; ++i) {
   5532         f_cell_t *cell = VECTOR_AT(ins_row->cells, i, f_cell_t *);
   5533         if (FT_IS_ERROR(vector_insert(cur_row->cells, &cell, pos + i))) {
   5534             /* clean up what we have inserted */
   5535             while (i--) {
   5536                 vector_erase(cur_row->cells, pos);
   5537             }
   5538             return FT_GEN_ERROR;
   5539         }
   5540     }
   5541     /* Clear cells so that it will be safe to destroy this row */
   5542     vector_clear(ins_row->cells);
   5543     return FT_SUCCESS;
   5544 }
   5545 
   5546 
   5547 FT_INTERNAL
   5548 size_t group_cell_number(const f_row_t *row, size_t master_cell_col)
   5549 {
   5550     assert(row);
   5551     const f_cell_t *master_cell = get_cell_c(row, master_cell_col);
   5552     if (master_cell == NULL)
   5553         return 0;
   5554 
   5555     if (get_cell_type(master_cell) != GROUP_MASTER_CELL)
   5556         return 1;
   5557 
   5558     size_t total_cols = vector_size(row->cells);
   5559     size_t slave_col = master_cell_col + 1;
   5560     while (slave_col < total_cols) {
   5561         const f_cell_t *cell = get_cell_c(row, slave_col);
   5562         if (cell && get_cell_type(cell) == GROUP_SLAVE_CELL) {
   5563             ++slave_col;
   5564         } else {
   5565             break;
   5566         }
   5567     }
   5568     return slave_col - master_cell_col;
   5569 }
   5570 
   5571 
   5572 FT_INTERNAL
   5573 int get_row_cell_types(const f_row_t *row, enum f_cell_type *types, size_t types_sz)
   5574 {
   5575     assert(row);
   5576     assert(types);
   5577     size_t i = 0;
   5578     for (i = 0; i < types_sz; ++i) {
   5579         const f_cell_t *cell = get_cell_c(row, i);
   5580         if (cell) {
   5581             types[i] = get_cell_type(cell);
   5582         } else {
   5583             types[i] = COMMON_CELL;
   5584         }
   5585     }
   5586     return FT_SUCCESS;
   5587 }
   5588 
   5589 
   5590 FT_INTERNAL
   5591 f_status row_set_cell_span(f_row_t *row, size_t cell_column, size_t hor_span)
   5592 {
   5593     assert(row);
   5594 
   5595     if (hor_span < 2)
   5596         return FT_EINVAL;
   5597 
   5598     f_cell_t *main_cell = get_cell_and_create_if_not_exists(row, cell_column);
   5599     if (main_cell == NULL) {
   5600         return FT_GEN_ERROR;
   5601     }
   5602     set_cell_type(main_cell, GROUP_MASTER_CELL);
   5603     --hor_span;
   5604     ++cell_column;
   5605 
   5606     while (hor_span) {
   5607         f_cell_t *slave_cell = get_cell_and_create_if_not_exists(row, cell_column);
   5608         if (slave_cell == NULL) {
   5609             return FT_GEN_ERROR;
   5610         }
   5611         set_cell_type(slave_cell, GROUP_SLAVE_CELL);
   5612         --hor_span;
   5613         ++cell_column;
   5614     }
   5615 
   5616     return FT_SUCCESS;
   5617 }
   5618 
   5619 static
   5620 int print_row_separator_impl(f_conv_context_t *cntx,
   5621                              const size_t *col_width_arr, size_t cols,
   5622                              const f_row_t *upper_row, const f_row_t *lower_row,
   5623                              enum f_hor_separator_pos separatorPos,
   5624                              const f_separator_t *sep)
   5625 {
   5626     assert(cntx);
   5627 
   5628     int status = FT_GEN_ERROR;
   5629 
   5630     const f_context_t *context = cntx->cntx;
   5631 
   5632     /* Get cell types
   5633      *
   5634      * Regions above top row and below bottom row areconsidered full of virtual
   5635      * GROUP_SLAVE_CELL cells
   5636      */
   5637     enum f_cell_type *top_row_types = (enum f_cell_type *)F_MALLOC(sizeof(enum f_cell_type) * cols * 2);
   5638     if (top_row_types == NULL) {
   5639         return FT_MEMORY_ERROR;
   5640     }
   5641     enum f_cell_type *bottom_row_types = top_row_types + cols;
   5642     if (upper_row) {
   5643         get_row_cell_types(upper_row, top_row_types, cols);
   5644     } else {
   5645         size_t i = 0;
   5646         for (i = 0; i < cols; ++i)
   5647             top_row_types[i] = GROUP_SLAVE_CELL;
   5648     }
   5649     if (lower_row) {
   5650         get_row_cell_types(lower_row, bottom_row_types, cols);
   5651     } else {
   5652         size_t i = 0;
   5653         for (i = 0; i < cols; ++i)
   5654             bottom_row_types[i] = GROUP_SLAVE_CELL;
   5655     }
   5656 
   5657 
   5658     f_table_properties_t *properties = context->table_properties;
   5659     fort_entire_table_properties_t *entire_tprops = &properties->entire_table_properties;
   5660 
   5661     size_t written = 0;
   5662     int tmp = 0;
   5663 
   5664     enum ft_row_type lower_row_type = FT_ROW_COMMON;
   5665     if (lower_row != NULL) {
   5666         lower_row_type = (enum ft_row_type)get_cell_property_hierarchically(properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE);
   5667     }
   5668     enum ft_row_type upper_row_type = FT_ROW_COMMON;
   5669     if (upper_row != NULL) {
   5670         upper_row_type = (enum ft_row_type)get_cell_property_hierarchically(properties, context->row - 1, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE);
   5671     }
   5672 
   5673     /* Row separator anatomy
   5674      *
   5675      *  |      C11    |   C12         C13      |      C14           C15         |
   5676      *  L  I  I  I   IV  I   I   IT  I  I  I  IB    I    I     II    I    I     R
   5677      *  |      C21    |   C22     |   C23             C24           C25         |
   5678     */
   5679     const char **L = NULL;
   5680     const char **I = NULL;
   5681     const char **IV = NULL;
   5682     const char **R = NULL;
   5683     const char **IT = NULL;
   5684     const char **IB = NULL;
   5685     const char **II = NULL;
   5686 
   5687     struct fort_border_style *border_style = &properties->border_style;
   5688 
   5689     typedef const char *(*border_chars_point_t)[BORDER_ITEM_POS_SIZE];
   5690     const char *(*border_chars)[BORDER_ITEM_POS_SIZE] = NULL;
   5691     border_chars = (border_chars_point_t)&border_style->border_chars;
   5692     if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) {
   5693         border_chars = (border_chars_point_t)&border_style->header_border_chars;
   5694     }
   5695 
   5696     if (sep && sep->enabled) {
   5697         L = &(border_style->separator_chars[LH_sip]);
   5698         I = &(border_style->separator_chars[IH_sip]);
   5699         IV = &(border_style->separator_chars[II_sip]);
   5700         R = &(border_style->separator_chars[RH_sip]);
   5701 
   5702         IT = &(border_style->separator_chars[TI_sip]);
   5703         IB = &(border_style->separator_chars[BI_sip]);
   5704         II = &(border_style->separator_chars[IH_sip]);
   5705 
   5706         if (lower_row == NULL) {
   5707             L = &(*border_chars)[BL_bip];
   5708             R = &(*border_chars)[BR_bip];
   5709         } else if (upper_row == NULL) {
   5710             L = &(*border_chars)[TL_bip];
   5711             R = &(*border_chars)[TR_bip];
   5712         }
   5713     } else {
   5714         switch (separatorPos) {
   5715             case TOP_SEPARATOR:
   5716                 L = &(*border_chars)[TL_bip];
   5717                 I = &(*border_chars)[TT_bip];
   5718                 IV = &(*border_chars)[TV_bip];
   5719                 R = &(*border_chars)[TR_bip];
   5720 
   5721                 IT = &(*border_chars)[TV_bip];
   5722                 IB = &(*border_chars)[TV_bip];
   5723                 II = &(*border_chars)[TT_bip];
   5724                 break;
   5725             case INSIDE_SEPARATOR:
   5726                 L = &(*border_chars)[LH_bip];
   5727                 I = &(*border_chars)[IH_bip];
   5728                 IV = &(*border_chars)[II_bip];
   5729                 R = &(*border_chars)[RH_bip];
   5730 
   5731                 IT = &(*border_chars)[TI_bip];
   5732                 IB = &(*border_chars)[BI_bip];
   5733                 II = &(*border_chars)[IH_bip];
   5734                 break;
   5735             case BOTTOM_SEPARATOR:
   5736                 L = &(*border_chars)[BL_bip];
   5737                 I = &(*border_chars)[BB_bip];
   5738                 IV = &(*border_chars)[BV_bip];
   5739                 R = &(*border_chars)[BR_bip];
   5740 
   5741                 IT = &(*border_chars)[BV_bip];
   5742                 IB = &(*border_chars)[BV_bip];
   5743                 II = &(*border_chars)[BB_bip];
   5744                 break;
   5745             default:
   5746                 break;
   5747         }
   5748     }
   5749 
   5750     size_t i = 0;
   5751 
   5752     /* If all chars are not printable, skip line separator */
   5753     /* NOTE: argument of `isprint` should be explicitly converted to
   5754      * unsigned char according to
   5755      * https://en.cppreference.com/w/c/string/byte/isprint
   5756      */
   5757     if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint((unsigned char) **L)))
   5758         && (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint((unsigned char) **I)))
   5759         && (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint((unsigned char) **IV)))
   5760         && (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint((unsigned char) **R)))) {
   5761         status = 0;
   5762         goto clear;
   5763     }
   5764 
   5765     /* Print left margin */
   5766     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->left_margin, FT_SPACE));
   5767 
   5768     for (i = 0; i < cols; ++i) {
   5769         if (i == 0) {
   5770             CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L));
   5771         } else {
   5772             if ((top_row_types[i] == COMMON_CELL || top_row_types[i] == GROUP_MASTER_CELL)
   5773                 && (bottom_row_types[i] == COMMON_CELL || bottom_row_types[i] == GROUP_MASTER_CELL)) {
   5774                 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV));
   5775             } else if (top_row_types[i] == GROUP_SLAVE_CELL && bottom_row_types[i] == GROUP_SLAVE_CELL) {
   5776                 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *II));
   5777             } else if (top_row_types[i] == GROUP_SLAVE_CELL) {
   5778                 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IT));
   5779             } else {
   5780                 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IB));
   5781             }
   5782         }
   5783         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[i], *I));
   5784     }
   5785     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R));
   5786 
   5787     /* Print right margin */
   5788     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->right_margin, FT_SPACE));
   5789 
   5790     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, FT_NEWLINE));
   5791 
   5792     status = (int)written;
   5793 
   5794 clear:
   5795     F_FREE(top_row_types);
   5796     return status;
   5797 }
   5798 
   5799 FT_INTERNAL
   5800 int print_row_separator(f_conv_context_t *cntx,
   5801                         const size_t *col_width_arr, size_t cols,
   5802                         const f_row_t *upper_row, const f_row_t *lower_row,
   5803                         enum f_hor_separator_pos separatorPos, const f_separator_t *sep)
   5804 {
   5805     return print_row_separator_impl(cntx, col_width_arr, cols, upper_row, lower_row,
   5806                                     separatorPos, sep);
   5807 }
   5808 
   5809 FT_INTERNAL
   5810 f_row_t *create_row_from_string(const char *str)
   5811 {
   5812     typedef char char_type;
   5813     char_type *(*strdup_)(const char_type * str) = F_STRDUP;
   5814     const char_type zero_char = '\0';
   5815     f_status(*fill_cell_from_string_)(f_cell_t *cell, const char *str) = fill_cell_from_string;
   5816     const char_type *const zero_string = "";
   5817 #define STRCHR strchr
   5818 
   5819     char_type *pos = NULL;
   5820     char_type *base_pos = NULL;
   5821     size_t number_of_separators = 0;
   5822 
   5823     f_row_t *row = create_row();
   5824     if (row == NULL)
   5825         return NULL;
   5826 
   5827     if (str == NULL)
   5828         return row;
   5829 
   5830     char_type *str_copy = strdup_(str);
   5831     if (str_copy == NULL)
   5832         goto clear;
   5833 
   5834     pos = str_copy;
   5835     base_pos = str_copy;
   5836     number_of_separators = 0;
   5837     while (*pos) {
   5838         pos = STRCHR(pos, g_col_separator);
   5839         if (pos != NULL) {
   5840             *(pos) = zero_char;
   5841             ++pos;
   5842             number_of_separators++;
   5843         }
   5844 
   5845         f_cell_t *cell = create_cell();
   5846         if (cell == NULL)
   5847             goto clear;
   5848 
   5849         int status = fill_cell_from_string_(cell, base_pos);
   5850         if (FT_IS_ERROR(status)) {
   5851             destroy_cell(cell);
   5852             goto clear;
   5853         }
   5854 
   5855         status = vector_push(row->cells, &cell);
   5856         if (FT_IS_ERROR(status)) {
   5857             destroy_cell(cell);
   5858             goto clear;
   5859         }
   5860 
   5861         if (pos == NULL)
   5862             break;
   5863         base_pos = pos;
   5864     }
   5865 
   5866     /* special case if in format string last cell is empty */
   5867     while (vector_size(row->cells) < (number_of_separators + 1)) {
   5868         f_cell_t *cell = create_cell();
   5869         if (cell == NULL)
   5870             goto clear;
   5871 
   5872         int status = fill_cell_from_string_(cell, zero_string);
   5873         if (FT_IS_ERROR(status)) {
   5874             destroy_cell(cell);
   5875             goto clear;
   5876         }
   5877 
   5878         status = vector_push(row->cells, &cell);
   5879         if (FT_IS_ERROR(status)) {
   5880             destroy_cell(cell);
   5881             goto clear;
   5882         }
   5883     }
   5884 
   5885     F_FREE(str_copy);
   5886     return row;
   5887 
   5888 clear:
   5889     destroy_row(row);
   5890     F_FREE(str_copy);
   5891     return NULL;
   5892 
   5893 #undef STRCHR
   5894 }
   5895 
   5896 
   5897 #ifdef FT_HAVE_WCHAR
   5898 FT_INTERNAL
   5899 f_row_t *create_row_from_wstring(const wchar_t *str)
   5900 {
   5901     typedef wchar_t char_type;
   5902     char_type *(*strdup_)(const char_type * str) = F_WCSDUP;
   5903     const char_type zero_char = L'\0';
   5904     f_status(*fill_cell_from_string_)(f_cell_t *cell, const wchar_t *str) = fill_cell_from_wstring;
   5905     const char_type *const zero_string = L"";
   5906 #define STRCHR wcschr
   5907 
   5908     char_type *pos = NULL;
   5909     char_type *base_pos = NULL;
   5910     size_t number_of_separators = 0;
   5911 
   5912     f_row_t *row = create_row();
   5913     if (row == NULL)
   5914         return NULL;
   5915 
   5916     if (str == NULL)
   5917         return row;
   5918 
   5919     char_type *str_copy = strdup_(str);
   5920     if (str_copy == NULL)
   5921         goto clear;
   5922 
   5923     pos = str_copy;
   5924     base_pos = str_copy;
   5925     number_of_separators = 0;
   5926     while (*pos) {
   5927         pos = STRCHR(pos, g_col_separator);
   5928         if (pos != NULL) {
   5929             *(pos) = zero_char;
   5930             ++pos;
   5931             number_of_separators++;
   5932         }
   5933 
   5934         f_cell_t *cell = create_cell();
   5935         if (cell == NULL)
   5936             goto clear;
   5937 
   5938         int status = fill_cell_from_string_(cell, base_pos);
   5939         if (FT_IS_ERROR(status)) {
   5940             destroy_cell(cell);
   5941             goto clear;
   5942         }
   5943 
   5944         status = vector_push(row->cells, &cell);
   5945         if (FT_IS_ERROR(status)) {
   5946             destroy_cell(cell);
   5947             goto clear;
   5948         }
   5949 
   5950         if (pos == NULL)
   5951             break;
   5952         base_pos = pos;
   5953     }
   5954 
   5955     /* special case if in format string last cell is empty */
   5956     while (vector_size(row->cells) < (number_of_separators + 1)) {
   5957         f_cell_t *cell = create_cell();
   5958         if (cell == NULL)
   5959             goto clear;
   5960 
   5961         int status = fill_cell_from_string_(cell, zero_string);
   5962         if (FT_IS_ERROR(status)) {
   5963             destroy_cell(cell);
   5964             goto clear;
   5965         }
   5966 
   5967         status = vector_push(row->cells, &cell);
   5968         if (FT_IS_ERROR(status)) {
   5969             destroy_cell(cell);
   5970             goto clear;
   5971         }
   5972     }
   5973 
   5974     F_FREE(str_copy);
   5975     return row;
   5976 
   5977 clear:
   5978     destroy_row(row);
   5979     F_FREE(str_copy);
   5980     return NULL;
   5981 #undef STRCHR
   5982 }
   5983 #endif
   5984 
   5985 FT_INTERNAL
   5986 f_row_t *create_row_from_buffer(const f_string_buffer_t *buffer)
   5987 {
   5988     switch (buffer->type) {
   5989         case CHAR_BUF:
   5990             return create_row_from_string(buffer->str.cstr);
   5991 #ifdef FT_HAVE_WCHAR
   5992         case W_CHAR_BUF:
   5993             return create_row_from_wstring(buffer->str.wstr);
   5994 #endif /* FT_HAVE_WCHAR */
   5995 #ifdef FT_HAVE_UTF8
   5996         case UTF8_BUF:
   5997             return create_row_from_string((const char *)buffer->str.u8str);
   5998 #endif /* FT_HAVE_UTF8 */
   5999         default:
   6000             assert(0);
   6001             return NULL;
   6002     }
   6003 }
   6004 
   6005 static int
   6006 vsnprintf_buffer(f_string_buffer_t *buffer, const struct f_string_view *fmt,
   6007                  va_list *va)
   6008 {
   6009     /* Disable compiler diagnostic (format string is not a string literal) */
   6010 #if defined(FT_CLANG_COMPILER)
   6011 #pragma clang diagnostic push
   6012 #pragma clang diagnostic ignored "-Wformat-nonliteral"
   6013 #endif
   6014 #if defined(FT_GCC_COMPILER)
   6015 #pragma GCC diagnostic push
   6016 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
   6017 #endif
   6018     size_t width_capacity = string_buffer_width_capacity(buffer);
   6019     switch (buffer->type) {
   6020         case CHAR_BUF:
   6021             return vsnprintf(buffer->str.cstr, width_capacity, fmt->u.cstr, *va);
   6022 #ifdef FT_HAVE_WCHAR
   6023         case W_CHAR_BUF:
   6024             return vswprintf(buffer->str.wstr, width_capacity, fmt->u.wstr, *va);
   6025 #endif
   6026 #ifdef FT_HAVE_UTF8
   6027         case UTF8_BUF:
   6028             return vsnprintf(buffer->str.cstr, width_capacity, fmt->u.cstr, *va);
   6029 #endif
   6030         default:
   6031             assert(0);
   6032             return 0;
   6033     }
   6034 #if defined(FT_CLANG_COMPILER)
   6035 #pragma clang diagnostic pop
   6036 #endif
   6037 #if defined(FT_GCC_COMPILER)
   6038 #pragma GCC diagnostic pop
   6039 #endif
   6040 }
   6041 
   6042 FT_INTERNAL
   6043 f_row_t *create_row_from_fmt_string(const struct f_string_view  *fmt, va_list *va_args)
   6044 {
   6045     f_string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, fmt->type);
   6046     if (buffer == NULL)
   6047         return NULL;
   6048 
   6049     size_t cols_origin = number_of_columns_in_format_string(fmt);
   6050     size_t cols = 0;
   6051 
   6052     while (1) {
   6053         va_list va;
   6054         va_copy(va, *va_args);
   6055         int virtual_sz = vsnprintf_buffer(buffer, fmt, &va);
   6056         va_end(va);
   6057         /* If error encountered */
   6058         if (virtual_sz < 0)
   6059             goto clear;
   6060 
   6061         /* Successful write */
   6062         if ((size_t)virtual_sz < string_buffer_width_capacity(buffer))
   6063             break;
   6064 
   6065         /* Otherwise buffer was too small, so incr. buffer size ant try again. */
   6066         if (!FT_IS_SUCCESS(realloc_string_buffer_without_copy(buffer)))
   6067             goto clear;
   6068     }
   6069 
   6070     cols = number_of_columns_in_format_buffer(buffer);
   6071     if (cols == cols_origin) {
   6072         f_row_t *row = create_row_from_buffer(buffer);
   6073         if (row == NULL) {
   6074             goto clear;
   6075         }
   6076 
   6077         destroy_string_buffer(buffer);
   6078         return row;
   6079     }
   6080 
   6081     if (cols_origin == 1) {
   6082         f_row_t *row = create_row();
   6083         if (row == NULL) {
   6084             goto clear;
   6085         }
   6086 
   6087         f_cell_t *cell = get_cell_and_create_if_not_exists(row, 0);
   6088         if (cell == NULL) {
   6089             destroy_row(row);
   6090             goto clear;
   6091         }
   6092 
   6093         f_status result = fill_cell_from_buffer(cell, buffer);
   6094         if (FT_IS_ERROR(result)) {
   6095             destroy_row(row);
   6096             goto clear;
   6097         }
   6098 
   6099         destroy_string_buffer(buffer);
   6100         return row;
   6101     }
   6102 
   6103     /*
   6104      * todo: add processing of cols != cols_origin in a general way
   6105      * (when cols_origin != 1).
   6106      */
   6107 
   6108 clear:
   6109     destroy_string_buffer(buffer);
   6110     return NULL;
   6111 }
   6112 
   6113 
   6114 FT_INTERNAL
   6115 int snprintf_row(const f_row_t *row, f_conv_context_t *cntx, size_t *col_width_arr, size_t col_width_arr_sz,
   6116                  size_t row_height)
   6117 {
   6118     const f_context_t *context = cntx->cntx;
   6119     assert(context);
   6120 
   6121     if (row == NULL)
   6122         return -1;
   6123 
   6124     size_t cols_in_row = columns_in_row(row);
   6125     if (cols_in_row > col_width_arr_sz)
   6126         return -1;
   6127 
   6128     /*  Row separator anatomy
   6129      *
   6130      *  L    data    IV    data   IV   data    R
   6131      */
   6132     f_table_properties_t *properties = context->table_properties;
   6133 
   6134     typedef const char *(*border_chars_point_t)[BORDER_ITEM_POS_SIZE];
   6135     enum ft_row_type row_type = (enum ft_row_type)get_cell_property_hierarchically(properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE);
   6136     const char *(*bord_chars)[BORDER_ITEM_POS_SIZE] = (row_type == FT_ROW_HEADER)
   6137             ? (border_chars_point_t)(&properties->border_style.header_border_chars)
   6138             : (border_chars_point_t)(&properties->border_style.border_chars);
   6139     const char **L = &(*bord_chars)[LL_bip];
   6140     const char **IV = &(*bord_chars)[IV_bip];
   6141     const char **R = &(*bord_chars)[RR_bip];
   6142 
   6143 
   6144     size_t written = 0;
   6145     int tmp = 0;
   6146     size_t i = 0;
   6147     fort_entire_table_properties_t *entire_tprops = &context->table_properties->entire_table_properties;
   6148     for (i = 0; i < row_height; ++i) {
   6149         /* Print left margin */
   6150         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->left_margin, FT_SPACE));
   6151 
   6152         /* Print left table boundary */
   6153         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L));
   6154         size_t j = 0;
   6155         while (j < col_width_arr_sz) {
   6156             if (j < cols_in_row) {
   6157                 ((f_context_t *)context)->column = j;
   6158                 f_cell_t *cell = VECTOR_AT(row->cells, j, f_cell_t *);
   6159                 size_t cell_vis_width = 0;
   6160 
   6161                 size_t group_slave_sz = group_cell_number(row, j);
   6162                 cell_vis_width = col_width_arr[j];
   6163                 size_t slave_j = 0;
   6164                 size_t master_j = j;
   6165                 for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) {
   6166                     cell_vis_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH;
   6167                     ++j;
   6168                 }
   6169 
   6170                 CHCK_RSLT_ADD_TO_WRITTEN(cell_printf(cell, i, cntx, cell_vis_width));
   6171             } else {
   6172                 /* Print empty cell */
   6173                 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[j], FT_SPACE));
   6174             }
   6175 
   6176             /* Print boundary between cells */
   6177             if (j < col_width_arr_sz - 1)
   6178                 CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV));
   6179 
   6180             ++j;
   6181         }
   6182 
   6183         /* Print right table boundary */
   6184         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R));
   6185 
   6186         /* Print right margin */
   6187         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->right_margin, FT_SPACE));
   6188 
   6189         /* Print new line character */
   6190         CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, FT_NEWLINE));
   6191     }
   6192     return (int)written;
   6193 
   6194 clear:
   6195     return -1;
   6196 }
   6197 
   6198 /********************************************************
   6199    End of file "row.c"
   6200  ********************************************************/
   6201 
   6202 
   6203 /********************************************************
   6204    Begin of file "string_buffer.c"
   6205  ********************************************************/
   6206 
   6207 /* #include "string_buffer.h" */ /* Commented by amalgamation script */
   6208 /* #include "properties.h" */ /* Commented by amalgamation script */
   6209 /* #include "wcwidth.h" */ /* Commented by amalgamation script */
   6210 #include <assert.h>
   6211 #include <stddef.h>
   6212 #ifdef FT_HAVE_WCHAR
   6213 #include <wchar.h>
   6214 #endif
   6215 #if defined(FT_HAVE_UTF8)
   6216 /* #include "utf8.h" */ /* Commented by amalgamation script */
   6217 #endif
   6218 
   6219 static ptrdiff_t str_iter_width(const char *beg, const char *end)
   6220 {
   6221     assert(end >= beg);
   6222     return (end - beg);
   6223 }
   6224 
   6225 
   6226 #ifdef FT_HAVE_WCHAR
   6227 static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end)
   6228 {
   6229     assert(end >= beg);
   6230     return mk_wcswidth(beg, (size_t)(end - beg));
   6231 }
   6232 #endif /* FT_HAVE_WCHAR */
   6233 
   6234 
   6235 static size_t buf_str_len(const f_string_buffer_t *buf)
   6236 {
   6237     assert(buf);
   6238 
   6239     switch (buf->type) {
   6240         case CHAR_BUF:
   6241             return strlen(buf->str.cstr);
   6242 #ifdef FT_HAVE_WCHAR
   6243         case W_CHAR_BUF:
   6244             return wcslen(buf->str.wstr);
   6245 #endif
   6246 #ifdef FT_HAVE_UTF8
   6247         case UTF8_BUF:
   6248             return utf8len(buf->str.u8str);
   6249 #endif
   6250     }
   6251 
   6252     assert(0);
   6253     return 0;
   6254 }
   6255 
   6256 
   6257 FT_INTERNAL
   6258 size_t strchr_count(const char *str, char ch)
   6259 {
   6260     if (str == NULL)
   6261         return 0;
   6262 
   6263     size_t count = 0;
   6264     str = strchr(str, ch);
   6265     while (str) {
   6266         count++;
   6267         str++;
   6268         str = strchr(str, ch);
   6269     }
   6270     return count;
   6271 }
   6272 
   6273 #ifdef FT_HAVE_WCHAR
   6274 FT_INTERNAL
   6275 size_t wstrchr_count(const wchar_t *str, wchar_t ch)
   6276 {
   6277     if (str == NULL)
   6278         return 0;
   6279 
   6280     size_t count = 0;
   6281     str = wcschr(str, ch);
   6282     while (str) {
   6283         count++;
   6284         str++;
   6285         str = wcschr(str, ch);
   6286     }
   6287     return count;
   6288 }
   6289 #endif
   6290 
   6291 
   6292 #if defined(FT_HAVE_UTF8)
   6293 /* todo: do something with code below!!! */
   6294 FT_INTERNAL
   6295 void *ut8next(const void *str)
   6296 {
   6297     utf8_int32_t out_codepoint;
   6298     return utf8codepoint(str, &out_codepoint);
   6299 }
   6300 
   6301 FT_INTERNAL
   6302 size_t utf8chr_count(const void *str, utf8_int32_t ch)
   6303 {
   6304     if (str == NULL)
   6305         return 0;
   6306 
   6307     size_t count = 0;
   6308     str = utf8chr(str, ch);
   6309     while (str) {
   6310         count++;
   6311         str = ut8next(str);
   6312         str = utf8chr(str, ch);
   6313     }
   6314     return count;
   6315 }
   6316 #endif /* FT_HAVE_UTF8 */
   6317 
   6318 
   6319 FT_INTERNAL
   6320 const char *str_n_substring_beg(const char *str, char ch_separator, size_t n)
   6321 {
   6322     if (str == NULL)
   6323         return NULL;
   6324 
   6325     if (n == 0)
   6326         return str;
   6327 
   6328     str = strchr(str, ch_separator);
   6329     --n;
   6330     while (n > 0) {
   6331         if (str == NULL)
   6332             return NULL;
   6333         --n;
   6334         str++;
   6335         str = strchr(str, ch_separator);
   6336     }
   6337     return str ? (str + 1) : NULL;
   6338 }
   6339 
   6340 
   6341 #ifdef FT_HAVE_WCHAR
   6342 FT_INTERNAL
   6343 const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, size_t n)
   6344 {
   6345     if (str == NULL)
   6346         return NULL;
   6347 
   6348     if (n == 0)
   6349         return str;
   6350 
   6351     str = wcschr(str, ch_separator);
   6352     --n;
   6353     while (n > 0) {
   6354         if (str == NULL)
   6355             return NULL;
   6356         --n;
   6357         str++;
   6358         str = wcschr(str, ch_separator);
   6359     }
   6360     return str ? (str + 1) : NULL;
   6361 }
   6362 #endif /* FT_HAVE_WCHAR */
   6363 
   6364 #if defined(FT_HAVE_UTF8)
   6365 FT_INTERNAL
   6366 const void *utf8_n_substring_beg(const void *str, utf8_int32_t ch_separator, size_t n)
   6367 {
   6368     if (str == NULL)
   6369         return NULL;
   6370 
   6371     if (n == 0)
   6372         return str;
   6373 
   6374     str = utf8chr(str, ch_separator);
   6375     --n;
   6376     while (n > 0) {
   6377         if (str == NULL)
   6378             return NULL;
   6379         --n;
   6380         str = ut8next(str);
   6381         str = utf8chr(str, ch_separator);
   6382     }
   6383     return str ? (ut8next(str)) : NULL;
   6384 }
   6385 #endif
   6386 
   6387 
   6388 FT_INTERNAL
   6389 void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end)
   6390 {
   6391     const char *beg = str_n_substring_beg(str, ch_separator, n);
   6392     if (beg == NULL) {
   6393         *begin = NULL;
   6394         *end = NULL;
   6395         return;
   6396     }
   6397 
   6398     const char *en = strchr(beg, ch_separator);
   6399     if (en == NULL) {
   6400         en = str + strlen(str);
   6401     }
   6402 
   6403     *begin = beg;
   6404     *end = en;
   6405     return;
   6406 }
   6407 
   6408 
   6409 #ifdef FT_HAVE_WCHAR
   6410 FT_INTERNAL
   6411 void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const wchar_t **begin, const wchar_t **end)
   6412 {
   6413     const wchar_t *beg = wstr_n_substring_beg(str, ch_separator, n);
   6414     if (beg == NULL) {
   6415         *begin = NULL;
   6416         *end = NULL;
   6417         return;
   6418     }
   6419 
   6420     const wchar_t *en = wcschr(beg, ch_separator);
   6421     if (en == NULL) {
   6422         en = str + wcslen(str);
   6423     }
   6424 
   6425     *begin = beg;
   6426     *end = en;
   6427     return;
   6428 }
   6429 #endif /* FT_HAVE_WCHAR */
   6430 
   6431 #if defined(FT_HAVE_UTF8)
   6432 FT_INTERNAL
   6433 void utf8_n_substring(const void *str, utf8_int32_t ch_separator, size_t n, const void **begin, const void **end)
   6434 {
   6435     const char *beg = (const char *)utf8_n_substring_beg(str, ch_separator, n);
   6436     if (beg == NULL) {
   6437         *begin = NULL;
   6438         *end = NULL;
   6439         return;
   6440     }
   6441 
   6442     const char *en = (const char *)utf8chr(beg, ch_separator);
   6443     if (en == NULL) {
   6444         en = (const char *)str + strlen((const char *)str);
   6445     }
   6446 
   6447     *begin = beg;
   6448     *end = en;
   6449     return;
   6450 }
   6451 #endif /* FT_HAVE_UTF8 */
   6452 
   6453 
   6454 
   6455 FT_INTERNAL
   6456 f_string_buffer_t *create_string_buffer(size_t n_chars, enum f_string_type type)
   6457 {
   6458     size_t char_sz = 0;
   6459     switch (type) {
   6460         case CHAR_BUF:
   6461             char_sz = 1;
   6462             break;
   6463 #ifdef FT_HAVE_WCHAR
   6464         case W_CHAR_BUF:
   6465             char_sz = sizeof(wchar_t);
   6466             break;
   6467 #endif
   6468 #ifdef FT_HAVE_UTF8
   6469         case UTF8_BUF:
   6470             char_sz = 4;
   6471             break;
   6472 #endif
   6473     }
   6474 
   6475     size_t sz = n_chars * char_sz;
   6476     f_string_buffer_t *result = (f_string_buffer_t *)F_MALLOC(sizeof(f_string_buffer_t));
   6477     if (result == NULL)
   6478         return NULL;
   6479     result->str.data = F_MALLOC(sz);
   6480     if (result->str.data == NULL) {
   6481         F_FREE(result);
   6482         return NULL;
   6483     }
   6484     result->data_sz = sz;
   6485     result->type = type;
   6486 
   6487     if (sz) {
   6488         switch (type) {
   6489             case CHAR_BUF:
   6490                 result->str.cstr[0] = '\0';
   6491                 break;
   6492 #ifdef FT_HAVE_WCHAR
   6493             case W_CHAR_BUF:
   6494                 result->str.wstr[0] = L'\0';
   6495                 break;
   6496 #endif
   6497 #ifdef FT_HAVE_UTF8
   6498             case UTF8_BUF:
   6499                 result->str.cstr[0] = '\0';
   6500                 break;
   6501 #endif
   6502         }
   6503     }
   6504 
   6505     return result;
   6506 }
   6507 
   6508 
   6509 FT_INTERNAL
   6510 void destroy_string_buffer(f_string_buffer_t *buffer)
   6511 {
   6512     if (buffer == NULL)
   6513         return;
   6514     F_FREE(buffer->str.data);
   6515     buffer->str.data = NULL;
   6516     F_FREE(buffer);
   6517 }
   6518 
   6519 FT_INTERNAL
   6520 f_string_buffer_t *copy_string_buffer(const f_string_buffer_t *buffer)
   6521 {
   6522     assert(buffer);
   6523     f_string_buffer_t *result = create_string_buffer(buffer->data_sz, buffer->type);
   6524     if (result == NULL)
   6525         return NULL;
   6526     switch (buffer->type) {
   6527         case CHAR_BUF:
   6528             if (FT_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) {
   6529                 destroy_string_buffer(result);
   6530                 return NULL;
   6531             }
   6532             break;
   6533 #ifdef FT_HAVE_WCHAR
   6534         case W_CHAR_BUF:
   6535             if (FT_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) {
   6536                 destroy_string_buffer(result);
   6537                 return NULL;
   6538             }
   6539             break;
   6540 #endif /* FT_HAVE_WCHAR */
   6541         default:
   6542             destroy_string_buffer(result);
   6543             return NULL;
   6544     }
   6545     return result;
   6546 }
   6547 
   6548 FT_INTERNAL
   6549 f_status realloc_string_buffer_without_copy(f_string_buffer_t *buffer)
   6550 {
   6551     assert(buffer);
   6552     char *new_str = (char *)F_MALLOC(buffer->data_sz * 2);
   6553     if (new_str == NULL) {
   6554         return FT_MEMORY_ERROR;
   6555     }
   6556     F_FREE(buffer->str.data);
   6557     buffer->str.data = new_str;
   6558     buffer->data_sz *= 2;
   6559     return FT_SUCCESS;
   6560 }
   6561 
   6562 
   6563 FT_INTERNAL
   6564 f_status fill_buffer_from_string(f_string_buffer_t *buffer, const char *str)
   6565 {
   6566     assert(buffer);
   6567     assert(str);
   6568 
   6569     char *copy = F_STRDUP(str);
   6570     if (copy == NULL)
   6571         return FT_MEMORY_ERROR;
   6572 
   6573     F_FREE(buffer->str.data);
   6574     buffer->str.cstr = copy;
   6575     buffer->type = CHAR_BUF;
   6576 
   6577     return FT_SUCCESS;
   6578 }
   6579 
   6580 
   6581 #ifdef FT_HAVE_WCHAR
   6582 FT_INTERNAL
   6583 f_status fill_buffer_from_wstring(f_string_buffer_t *buffer, const wchar_t *str)
   6584 {
   6585     assert(buffer);
   6586     assert(str);
   6587 
   6588     wchar_t *copy = F_WCSDUP(str);
   6589     if (copy == NULL)
   6590         return FT_MEMORY_ERROR;
   6591 
   6592     F_FREE(buffer->str.data);
   6593     buffer->str.wstr = copy;
   6594     buffer->type = W_CHAR_BUF;
   6595 
   6596     return FT_SUCCESS;
   6597 }
   6598 #endif /* FT_HAVE_WCHAR */
   6599 
   6600 #ifdef FT_HAVE_UTF8
   6601 FT_INTERNAL
   6602 f_status fill_buffer_from_u8string(f_string_buffer_t *buffer, const void *str)
   6603 {
   6604     assert(buffer);
   6605     assert(str);
   6606 
   6607     void *copy = F_UTF8DUP(str);
   6608     if (copy == NULL)
   6609         return FT_MEMORY_ERROR;
   6610 
   6611     F_FREE(buffer->str.u8str);
   6612     buffer->str.u8str = copy;
   6613     buffer->type = UTF8_BUF;
   6614 
   6615     return FT_SUCCESS;
   6616 }
   6617 #endif /* FT_HAVE_UTF8 */
   6618 
   6619 FT_INTERNAL
   6620 size_t buffer_text_visible_height(const f_string_buffer_t *buffer)
   6621 {
   6622     if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) {
   6623         return 0;
   6624     }
   6625     if (buffer->type == CHAR_BUF)
   6626         return 1 + strchr_count(buffer->str.cstr, '\n');
   6627 #ifdef FT_HAVE_WCHAR
   6628     else if (buffer->type == W_CHAR_BUF)
   6629         return 1 + wstrchr_count(buffer->str.wstr, L'\n');
   6630 #endif /* FT_HAVE_WCHAR */
   6631 #ifdef FT_HAVE_UTF8
   6632     else if (buffer->type == UTF8_BUF)
   6633         return 1 + utf8chr_count(buffer->str.u8str, '\n');
   6634 #endif /* FT_HAVE_WCHAR */
   6635 
   6636     assert(0);
   6637     return 0;
   6638 }
   6639 
   6640 FT_INTERNAL
   6641 size_t string_buffer_cod_width_capacity(const f_string_buffer_t *buffer)
   6642 {
   6643     return string_buffer_width_capacity(buffer);
   6644 }
   6645 
   6646 FT_INTERNAL
   6647 size_t string_buffer_raw_capacity(const f_string_buffer_t *buffer)
   6648 {
   6649     return buffer->data_sz;
   6650 }
   6651 
   6652 #ifdef FT_HAVE_UTF8
   6653 /* User provided function to compute utf8 string visible width */
   6654 static int (*_custom_u8strwid)(const void *beg, const void *end, size_t *width) = NULL;
   6655 
   6656 FT_INTERNAL
   6657 void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width))
   6658 {
   6659     _custom_u8strwid = u8strwid;
   6660 }
   6661 
   6662 static
   6663 size_t utf8_width(const void *beg, const void *end)
   6664 {
   6665     if (_custom_u8strwid) {
   6666         size_t width = 0;
   6667         if (!_custom_u8strwid(beg, end, &width))
   6668             return width;
   6669     }
   6670 
   6671     size_t sz = (size_t)((const char *)end - (const char *)beg);
   6672     char *tmp = (char *)F_MALLOC(sizeof(char) * (sz + 1));
   6673     // @todo: add check to tmp
   6674     assert(tmp);
   6675 
   6676     memcpy(tmp, beg, sz);
   6677     tmp[sz] = '\0';
   6678     size_t result = utf8width(tmp);
   6679     F_FREE(tmp);
   6680     return result;
   6681 }
   6682 #endif /* FT_HAVE_WCHAR */
   6683 
   6684 FT_INTERNAL
   6685 size_t buffer_text_visible_width(const f_string_buffer_t *buffer)
   6686 {
   6687     size_t max_length = 0;
   6688     if (buffer->type == CHAR_BUF) {
   6689         size_t n = 0;
   6690         while (1) {
   6691             const char *beg = NULL;
   6692             const char *end = NULL;
   6693             str_n_substring(buffer->str.cstr, '\n', n, &beg, &end);
   6694             if (beg == NULL || end == NULL)
   6695                 return max_length;
   6696 
   6697             max_length = MAX(max_length, (size_t)(end - beg));
   6698             ++n;
   6699         }
   6700 #ifdef FT_HAVE_WCHAR
   6701     } else if (buffer->type == W_CHAR_BUF) {
   6702         size_t n = 0;
   6703         while (1) {
   6704             const wchar_t *beg = NULL;
   6705             const wchar_t *end = NULL;
   6706             wstr_n_substring(buffer->str.wstr, L'\n', n, &beg, &end);
   6707             if (beg == NULL || end == NULL)
   6708                 return max_length;
   6709 
   6710             int line_width = mk_wcswidth(beg, (size_t)(end - beg));
   6711             if (line_width < 0) /* For safety */
   6712                 line_width = 0;
   6713             max_length = MAX(max_length, (size_t)line_width);
   6714 
   6715             ++n;
   6716         }
   6717 #endif /* FT_HAVE_WCHAR */
   6718 #ifdef FT_HAVE_UTF8
   6719     } else if (buffer->type == UTF8_BUF) {
   6720         size_t n = 0;
   6721         while (1) {
   6722             const void *beg = NULL;
   6723             const void *end = NULL;
   6724             utf8_n_substring(buffer->str.u8str, '\n', n, &beg, &end);
   6725             if (beg == NULL || end == NULL)
   6726                 return max_length;
   6727 
   6728             max_length = MAX(max_length, (size_t)utf8_width(beg, end));
   6729             ++n;
   6730         }
   6731 #endif /* FT_HAVE_WCHAR */
   6732     }
   6733 
   6734     return max_length; /* shouldn't be here */
   6735 }
   6736 
   6737 
   6738 static void
   6739 buffer_substring(const f_string_buffer_t *buffer, size_t buffer_row, const void **begin, const void **end,  ptrdiff_t *str_it_width)
   6740 {
   6741     switch (buffer->type) {
   6742         case CHAR_BUF:
   6743             str_n_substring(buffer->str.cstr, '\n', buffer_row, (const char **)begin, (const char **)end);
   6744             if ((*(const char **)begin) && (*(const char **)end))
   6745                 *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end);
   6746             break;
   6747 #ifdef FT_HAVE_WCHAR
   6748         case W_CHAR_BUF:
   6749             wstr_n_substring(buffer->str.wstr, L'\n', buffer_row, (const wchar_t **)begin, (const wchar_t **)end);
   6750             if ((*(const wchar_t **)begin) && (*(const wchar_t **)end))
   6751                 *str_it_width = wcs_iter_width(*(const wchar_t **)begin, *(const wchar_t **)end);
   6752             break;
   6753 #endif /* FT_HAVE_WCHAR */
   6754 #ifdef FT_HAVE_UTF8
   6755         case UTF8_BUF:
   6756             utf8_n_substring(buffer->str.u8str, '\n', buffer_row, begin, end);
   6757             if ((*(const char **)begin) && (*(const char **)end))
   6758                 *str_it_width = utf8_width(*begin, *end);
   6759             break;
   6760 #endif /* FT_HAVE_UTF8 */
   6761         default:
   6762             assert(0);
   6763     }
   6764 }
   6765 
   6766 
   6767 static int
   6768 buffer_print_range(f_conv_context_t *cntx, const void *beg, const void *end)
   6769 {
   6770     size_t len;
   6771     switch (cntx->b_type) {
   6772         case CHAR_BUF:
   6773             len = (size_t)((const char *)end - (const char *)beg);
   6774             return ft_nprint(cntx, (const char *)beg, len);
   6775 #ifdef FT_HAVE_WCHAR
   6776         case W_CHAR_BUF:
   6777             len = (size_t)((const wchar_t *)end - (const wchar_t *)beg);
   6778             return ft_nwprint(cntx, (const wchar_t *)beg, len);
   6779 #endif /* FT_HAVE_WCHAR */
   6780 #ifdef FT_HAVE_UTF8
   6781         case UTF8_BUF:
   6782             return ft_nu8print(cntx, beg, end);
   6783 #endif /* FT_HAVE_UTF8 */
   6784         default:
   6785             assert(0);
   6786             return -1;
   6787     }
   6788 }
   6789 
   6790 
   6791 FT_INTERNAL
   6792 int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t *cntx, size_t vis_width,
   6793                   const char *content_style_tag, const char *reset_content_style_tag)
   6794 {
   6795     const f_context_t *context = cntx->cntx;
   6796     f_table_properties_t *props = context->table_properties;
   6797     size_t row = context->row;
   6798     size_t column = context->column;
   6799 
   6800     if (buffer == NULL || buffer->str.data == NULL
   6801         || buffer_row >= buffer_text_visible_height(buffer)) {
   6802         return -1;
   6803     }
   6804 
   6805     size_t content_width = buffer_text_visible_width(buffer);
   6806     if (vis_width < content_width)
   6807         return -1;
   6808 
   6809     size_t left = 0;
   6810     size_t right = 0;
   6811     switch (get_cell_property_hierarchically(props, row, column, FT_CPROP_TEXT_ALIGN)) {
   6812         case FT_ALIGNED_LEFT:
   6813             left = 0;
   6814             right = (vis_width) - content_width;
   6815             break;
   6816         case FT_ALIGNED_CENTER:
   6817             left = ((vis_width) - content_width) / 2;
   6818             right = ((vis_width) - content_width) - left;
   6819             break;
   6820         case FT_ALIGNED_RIGHT:
   6821             left = (vis_width) - content_width;
   6822             right = 0;
   6823             break;
   6824         default:
   6825             assert(0);
   6826             break;
   6827     }
   6828 
   6829     size_t  written = 0;
   6830     int tmp = 0;
   6831     ptrdiff_t str_it_width = 0;
   6832     const void *beg = NULL;
   6833     const void *end = NULL;
   6834     buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width);
   6835     if (beg == NULL || end == NULL)
   6836         return -1;
   6837     if (str_it_width < 0 || content_width < (size_t)str_it_width)
   6838         return -1;
   6839 
   6840     size_t padding = content_width - (size_t)str_it_width;
   6841 
   6842     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE));
   6843     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, content_style_tag));
   6844     CHCK_RSLT_ADD_TO_WRITTEN(buffer_print_range(cntx, beg, end));
   6845     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag));
   6846     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, padding, FT_SPACE));
   6847     CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, right, FT_SPACE));
   6848     return (int)written;
   6849 
   6850 clear:
   6851     return -1;
   6852 }
   6853 
   6854 FT_INTERNAL
   6855 size_t string_buffer_width_capacity(const f_string_buffer_t *buffer)
   6856 {
   6857     assert(buffer);
   6858     switch (buffer->type) {
   6859         case CHAR_BUF:
   6860             return buffer->data_sz;
   6861 #ifdef FT_HAVE_WCHAR
   6862         case W_CHAR_BUF:
   6863             return buffer->data_sz / sizeof(wchar_t);
   6864 #endif
   6865 #ifdef FT_HAVE_UTF8
   6866         case UTF8_BUF:
   6867             return buffer->data_sz / 4;
   6868 #endif
   6869         default:
   6870             assert(0);
   6871             return 0;
   6872     }
   6873 }
   6874 
   6875 
   6876 FT_INTERNAL
   6877 void *buffer_get_data(f_string_buffer_t *buffer)
   6878 {
   6879     assert(buffer);
   6880     return buffer->str.data;
   6881 }
   6882 
   6883 FT_INTERNAL
   6884 int buffer_check_align(f_string_buffer_t *buffer)
   6885 {
   6886     assert(buffer);
   6887     assert(buffer->str.data);
   6888 
   6889     switch (buffer->type) {
   6890         case CHAR_BUF:
   6891             return 1;
   6892 #ifdef FT_HAVE_WCHAR
   6893         case W_CHAR_BUF:
   6894             return (((uintptr_t)buffer->str.data) & (sizeof(wchar_t) - 1)) == 0;
   6895 #endif
   6896 #ifdef FT_HAVE_UTF8
   6897         case UTF8_BUF:
   6898             return 1;
   6899 #endif
   6900         default:
   6901             assert(0);
   6902             return 0;
   6903     }
   6904 }
   6905 
   6906 /********************************************************
   6907    End of file "string_buffer.c"
   6908  ********************************************************/
   6909 
   6910 
   6911 /********************************************************
   6912    Begin of file "table.c"
   6913  ********************************************************/
   6914 
   6915 /* #include "table.h" */ /* Commented by amalgamation script */
   6916 /* #include "string_buffer.h" */ /* Commented by amalgamation script */
   6917 /* #include "cell.h" */ /* Commented by amalgamation script */
   6918 /* #include "vector.h" */ /* Commented by amalgamation script */
   6919 /* #include "row.h" */ /* Commented by amalgamation script */
   6920 
   6921 FT_INTERNAL
   6922 f_separator_t *create_separator(int enabled)
   6923 {
   6924     f_separator_t *res = (f_separator_t *)F_CALLOC(1, sizeof(f_separator_t));
   6925     if (res == NULL)
   6926         return NULL;
   6927     res->enabled = enabled;
   6928     return res;
   6929 }
   6930 
   6931 
   6932 FT_INTERNAL
   6933 void destroy_separator(f_separator_t *sep)
   6934 {
   6935     F_FREE(sep);
   6936 }
   6937 
   6938 
   6939 FT_INTERNAL
   6940 f_separator_t *copy_separator(f_separator_t *sep)
   6941 {
   6942     assert(sep);
   6943     return create_separator(sep->enabled);
   6944 }
   6945 
   6946 
   6947 static
   6948 f_row_t *get_row_impl(ft_table_t *table, size_t row, enum f_get_policy policy)
   6949 {
   6950     if (table == NULL || table->rows == NULL) {
   6951         return NULL;
   6952     }
   6953 
   6954     switch (policy) {
   6955         case DONT_CREATE_ON_NULL:
   6956             if (row < vector_size(table->rows)) {
   6957                 return VECTOR_AT(table->rows, row, f_row_t *);
   6958             }
   6959             return NULL;
   6960         case CREATE_ON_NULL:
   6961             while (row >= vector_size(table->rows)) {
   6962                 f_row_t *new_row = create_row();
   6963                 if (new_row == NULL)
   6964                     return NULL;
   6965                 if (FT_IS_ERROR(vector_push(table->rows, &new_row))) {
   6966                     destroy_row(new_row);
   6967                     return NULL;
   6968                 }
   6969             }
   6970             return VECTOR_AT(table->rows, row, f_row_t *);
   6971     }
   6972 
   6973     assert(0 && "Shouldn't be here!");
   6974     return NULL;
   6975 }
   6976 
   6977 
   6978 FT_INTERNAL
   6979 f_row_t *get_row(ft_table_t *table, size_t row)
   6980 {
   6981     return get_row_impl(table, row, DONT_CREATE_ON_NULL);
   6982 }
   6983 
   6984 
   6985 FT_INTERNAL
   6986 const f_row_t *get_row_c(const ft_table_t *table, size_t row)
   6987 {
   6988     return get_row((ft_table_t *)table, row);
   6989 }
   6990 
   6991 
   6992 FT_INTERNAL
   6993 f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row)
   6994 {
   6995     return get_row_impl(table, row, CREATE_ON_NULL);
   6996 }
   6997 
   6998 FT_INTERNAL
   6999 f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table)
   7000 {
   7001     assert(table);
   7002 
   7003     f_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row);
   7004     if (row == NULL)
   7005         return NULL;
   7006 
   7007     f_cell_t *cell = NULL;
   7008     fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties;
   7009     switch (table_props->add_strategy) {
   7010         case FT_STRATEGY_INSERT:
   7011             cell = create_cell_in_position(row, table->cur_col);
   7012             break;
   7013         case FT_STRATEGY_REPLACE:
   7014             cell = get_cell_and_create_if_not_exists(row, table->cur_col);
   7015             break;
   7016         default:
   7017             assert(0 && "Unexpected situation inside libfort");
   7018             break;
   7019     }
   7020 
   7021     if (cell == NULL)
   7022         return NULL;
   7023 
   7024     return cell_get_string_buffer(cell);
   7025 }
   7026 
   7027 
   7028 /*
   7029  * Returns number of cells (rows * cols)
   7030  */
   7031 FT_INTERNAL
   7032 f_status get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols)
   7033 {
   7034     *rows = 0;
   7035     *cols = 0;
   7036     if (table && table->rows) {
   7037         *rows = vector_size(table->rows);
   7038         size_t row_index = 0;
   7039         for (row_index = 0; row_index < vector_size(table->rows); ++row_index) {
   7040             f_row_t *row = VECTOR_AT(table->rows, row_index, f_row_t *);
   7041             size_t cols_in_row = columns_in_row(row);
   7042             if (cols_in_row > *cols)
   7043                 *cols = cols_in_row;
   7044         }
   7045     }
   7046     return FT_SUCCESS;
   7047 }
   7048 
   7049 
   7050 FT_INTERNAL
   7051 f_status table_rows_and_cols_geometry(const ft_table_t *table,
   7052                                       size_t **col_width_arr_p, size_t *col_width_arr_sz,
   7053                                       size_t **row_height_arr_p, size_t *row_height_arr_sz,
   7054                                       enum f_geometry_type geom)
   7055 {
   7056     if (table == NULL) {
   7057         return FT_GEN_ERROR;
   7058     }
   7059 
   7060     size_t max_invis_codepoints = 0;
   7061     size_t cols = 0;
   7062     size_t rows = 0;
   7063     int status = get_table_sizes(table, &rows, &cols);
   7064     if (FT_IS_ERROR(status))
   7065         return status;
   7066 
   7067     size_t *col_width_arr = (size_t *)F_CALLOC(cols, sizeof(size_t));
   7068     size_t *row_height_arr = (size_t *)F_CALLOC(rows, sizeof(size_t));
   7069     if (col_width_arr == NULL || row_height_arr == NULL) {
   7070         F_FREE(col_width_arr);
   7071         F_FREE(row_height_arr);
   7072         return FT_GEN_ERROR;
   7073     }
   7074 
   7075     int combined_cells_found = 0;
   7076     f_context_t context;
   7077     context.table_properties = (table->properties ? table->properties : &g_table_properties);
   7078     size_t col = 0;
   7079     for (col = 0; col < cols; ++col) {
   7080         col_width_arr[col] = 0;
   7081         size_t row = 0;
   7082         for (row = 0; row < rows; ++row) {
   7083             const f_row_t *row_p = get_row_c(table, row);
   7084             const f_cell_t *cell = get_cell_c(row_p, col);
   7085             context.column = col;
   7086             context.row = row;
   7087             if (cell) {
   7088                 switch (get_cell_type(cell)) {
   7089                     case COMMON_CELL:
   7090                         col_width_arr[col] = MAX(col_width_arr[col], cell_vis_width(cell, &context));
   7091                         break;
   7092                     case GROUP_MASTER_CELL:
   7093                         combined_cells_found = 1;
   7094                         break;
   7095                     case GROUP_SLAVE_CELL:
   7096                         ; /* Do nothing */
   7097                         break;
   7098                 }
   7099                 row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, &context));
   7100             } else {
   7101                 size_t cell_empty_string_height = get_cell_property_hierarchically(context.table_properties, context.row, context.column, FT_CPROP_EMPTY_STR_HEIGHT);
   7102                 if (cell_empty_string_height) {
   7103                     size_t cell_top_padding = get_cell_property_hierarchically(context.table_properties, context.row, context.column, FT_CPROP_TOP_PADDING);
   7104                     size_t cell_bottom_padding = get_cell_property_hierarchically(context.table_properties, context.row, context.column, FT_CPROP_BOTTOM_PADDING);
   7105                     row_height_arr[row] = MAX(row_height_arr[row], cell_empty_string_height + cell_top_padding + cell_bottom_padding);
   7106                 }
   7107             }
   7108         }
   7109 
   7110         if (geom == INTERN_REPR_GEOMETRY) {
   7111             max_invis_codepoints = 0;
   7112             for (row = 0; row < rows; ++row) {
   7113                 const f_row_t *row_p = get_row_c(table, row);
   7114                 const f_cell_t *cell = get_cell_c(row_p, col);
   7115                 if (!cell)
   7116                     continue;
   7117                 context.column = col;
   7118                 context.row = row;
   7119                 size_t inv_codepoints = cell_invis_codes_width(cell, &context);
   7120                 max_invis_codepoints = MAX(max_invis_codepoints, inv_codepoints);
   7121             }
   7122             col_width_arr[col] += max_invis_codepoints;
   7123         }
   7124     }
   7125 
   7126     if (combined_cells_found) {
   7127         for (col = 0; col < cols; ++col) {
   7128             size_t row = 0;
   7129             for (row = 0; row < rows; ++row) {
   7130                 const f_row_t *row_p = get_row_c(table, row);
   7131                 const f_cell_t *cell = get_cell_c(row_p, col);
   7132                 context.column = col;
   7133                 context.row = row;
   7134                 if (cell) {
   7135                     if (get_cell_type(cell) == GROUP_MASTER_CELL) {
   7136                         size_t hint_width = cell_vis_width(cell, &context);
   7137                         if (geom == INTERN_REPR_GEOMETRY) {
   7138                             hint_width += cell_invis_codes_width(cell, &context);
   7139                         }
   7140                         size_t slave_col = col + group_cell_number(row_p, col);
   7141                         size_t cur_adj_col = col;
   7142                         size_t group_width = col_width_arr[col];
   7143                         size_t i;
   7144                         for (i = col + 1; i < slave_col; ++i)
   7145                             group_width += col_width_arr[i] + FORT_COL_SEPARATOR_LENGTH;
   7146                         /* adjust col. widths */
   7147                         while (1) {
   7148                             if (group_width >= hint_width)
   7149                                 break;
   7150                             col_width_arr[cur_adj_col] += 1;
   7151                             group_width++;
   7152                             cur_adj_col++;
   7153                             if (cur_adj_col == slave_col)
   7154                                 cur_adj_col = col;
   7155                         }
   7156                     }
   7157                 }
   7158             }
   7159         }
   7160     }
   7161 
   7162     /* todo: Maybe it is better to move min width checking to a particular cell
   7163      * width checking. At the moment min width includes paddings. Maybe it is
   7164      * better that min width weren't include paddings but be min width of the
   7165      * cell content without padding
   7166      */
   7167     /*
   7168     if (table->properties) {
   7169         for (size_t i = 0; i < cols; ++i) {
   7170             col_width_arr[i] = MAX((int)col_width_arr[i], fort_props_column_width(table->properties, i));
   7171         }
   7172     }
   7173     */
   7174 
   7175     *col_width_arr_p = col_width_arr;
   7176     *col_width_arr_sz = cols;
   7177     *row_height_arr_p = row_height_arr;
   7178     *row_height_arr_sz = rows;
   7179     return FT_SUCCESS;
   7180 }
   7181 
   7182 
   7183 /*
   7184  * Returns geometry in characters
   7185  */
   7186 FT_INTERNAL
   7187 f_status table_geometry(const ft_table_t *table, size_t *height, size_t *width)
   7188 {
   7189     if (table == NULL)
   7190         return FT_GEN_ERROR;
   7191 
   7192     *height = 0;
   7193     *width = 0;
   7194     size_t cols = 0;
   7195     size_t rows = 0;
   7196     size_t *col_width_arr = NULL;
   7197     size_t *row_height_arr = NULL;
   7198 
   7199     int status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, INTERN_REPR_GEOMETRY);
   7200     if (FT_IS_ERROR(status))
   7201         return status;
   7202 
   7203     *width = 1 + (cols == 0 ? 1 : cols) + 1; /* for boundaries (that take 1 symbol) + newline   */
   7204     size_t i = 0;
   7205     for (i = 0; i < cols; ++i) {
   7206         *width += col_width_arr[i];
   7207     }
   7208 
   7209     /* todo: add check for non printable horizontal row separators */
   7210     *height = 1 + (rows == 0 ? 1 : rows); /* for boundaries (that take 1 symbol)  */
   7211     for (i = 0; i < rows; ++i) {
   7212         *height += row_height_arr[i];
   7213     }
   7214     F_FREE(col_width_arr);
   7215     F_FREE(row_height_arr);
   7216 
   7217     f_table_properties_t *properties = table->properties;
   7218     if (properties) {
   7219         *height += properties->entire_table_properties.top_margin;
   7220         *height += properties->entire_table_properties.bottom_margin;
   7221         *width += properties->entire_table_properties.left_margin;
   7222         *width += properties->entire_table_properties.right_margin;
   7223     }
   7224 
   7225     /* Take into account that border elements can be more than one byte long */
   7226     f_table_properties_t *table_properties = properties ? properties : &g_table_properties;
   7227     size_t max_border_elem_len = max_border_elem_strlen(table_properties);
   7228     *width *= max_border_elem_len;
   7229 
   7230     return FT_SUCCESS;
   7231 }
   7232 
   7233 FT_INTERNAL
   7234 f_status table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width)
   7235 {
   7236     return table_geometry(table, height, width);
   7237 }
   7238 
   7239 /********************************************************
   7240    End of file "table.c"
   7241  ********************************************************/
   7242 
   7243 
   7244 /********************************************************
   7245    Begin of file "vector.c"
   7246  ********************************************************/
   7247 
   7248 /* #include "vector.h" */ /* Commented by amalgamation script */
   7249 #include <assert.h>
   7250 #include <string.h>
   7251 
   7252 struct f_vector {
   7253     size_t m_size;
   7254     void  *m_data;
   7255     size_t m_capacity;
   7256     size_t m_item_size;
   7257 };
   7258 
   7259 
   7260 static int vector_reallocate_(f_vector_t *vector, size_t new_capacity)
   7261 {
   7262     assert(vector);
   7263     assert(new_capacity > vector->m_capacity);
   7264 
   7265     size_t new_size = new_capacity * vector->m_item_size;
   7266     vector->m_data = F_REALLOC(vector->m_data, new_size);
   7267     if (vector->m_data == NULL)
   7268         return -1;
   7269     return 0;
   7270 }
   7271 
   7272 
   7273 FT_INTERNAL
   7274 f_vector_t *create_vector(size_t item_size, size_t capacity)
   7275 {
   7276     f_vector_t *vector = (f_vector_t *)F_MALLOC(sizeof(f_vector_t));
   7277     if (vector == NULL) {
   7278         return NULL;
   7279     }
   7280 
   7281     size_t init_size = MAX(item_size * capacity, 1);
   7282     vector->m_data = F_MALLOC(init_size);
   7283     if (vector->m_data == NULL) {
   7284         F_FREE(vector);
   7285         return NULL;
   7286     }
   7287 
   7288     vector->m_size      = 0;
   7289     vector->m_capacity  = capacity;
   7290     vector->m_item_size = item_size;
   7291 
   7292     return vector;
   7293 }
   7294 
   7295 
   7296 FT_INTERNAL
   7297 void destroy_vector(f_vector_t *vector)
   7298 {
   7299     assert(vector);
   7300     F_FREE(vector->m_data);
   7301     F_FREE(vector);
   7302 }
   7303 
   7304 
   7305 FT_INTERNAL
   7306 size_t vector_size(const f_vector_t *vector)
   7307 {
   7308     assert(vector);
   7309     return vector->m_size;
   7310 }
   7311 
   7312 
   7313 FT_INTERNAL
   7314 size_t vector_capacity(const f_vector_t *vector)
   7315 {
   7316     assert(vector);
   7317     return vector->m_capacity;
   7318 }
   7319 
   7320 
   7321 FT_INTERNAL
   7322 int vector_push(f_vector_t *vector, const void *item)
   7323 {
   7324     assert(vector);
   7325     assert(item);
   7326 
   7327     if (vector->m_size == vector->m_capacity) {
   7328         if (vector_reallocate_(vector, vector->m_capacity * 2) == -1)
   7329             return FT_GEN_ERROR;
   7330         vector->m_capacity = vector->m_capacity * 2;
   7331     }
   7332 
   7333     size_t offset = vector->m_size * vector->m_item_size;
   7334     memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
   7335 
   7336     ++(vector->m_size);
   7337 
   7338     return FT_SUCCESS;
   7339 }
   7340 
   7341 FT_INTERNAL
   7342 int vector_insert(f_vector_t *vector, const void *item, size_t pos)
   7343 {
   7344     assert(vector);
   7345     assert(item);
   7346     size_t needed_capacity = MAX(pos + 1, vector->m_size + 1);
   7347     if (vector->m_capacity < needed_capacity) {
   7348         if (vector_reallocate_(vector, needed_capacity) == -1)
   7349             return FT_GEN_ERROR;
   7350         vector->m_capacity = needed_capacity;
   7351     }
   7352     size_t offset = pos * vector->m_item_size;
   7353     if (pos >= vector->m_size) {
   7354         /* Data in the middle are not initialized */
   7355         memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
   7356         vector->m_size = pos + 1;
   7357         return FT_SUCCESS;
   7358     } else {
   7359         /* Shift following data by one position */
   7360         memmove((char *)vector->m_data + offset + vector->m_item_size,
   7361                 (char *)vector->m_data + offset,
   7362                 vector->m_item_size * (vector->m_size - pos));
   7363         memcpy((char *)vector->m_data + offset, item, vector->m_item_size);
   7364         ++(vector->m_size);
   7365         return FT_SUCCESS;
   7366     }
   7367 }
   7368 
   7369 FT_INTERNAL
   7370 f_vector_t *vector_split(f_vector_t *vector, size_t pos)
   7371 {
   7372     size_t trailing_sz = vector->m_size > pos ? vector->m_size - pos : 0;
   7373     f_vector_t *new_vector = create_vector(vector->m_item_size, trailing_sz);
   7374     if (!new_vector)
   7375         return new_vector;
   7376     if (new_vector->m_capacity < trailing_sz) {
   7377         destroy_vector(new_vector);
   7378         return NULL;
   7379     }
   7380 
   7381     if (trailing_sz == 0)
   7382         return new_vector;
   7383 
   7384     size_t offset = vector->m_item_size * pos;
   7385     memcpy(new_vector->m_data, (char *)vector->m_data + offset,
   7386            trailing_sz * vector->m_item_size);
   7387     new_vector->m_size = trailing_sz;
   7388     vector->m_size = pos;
   7389     return new_vector;
   7390 }
   7391 
   7392 FT_INTERNAL
   7393 const void *vector_at_c(const f_vector_t *vector, size_t index)
   7394 {
   7395     if (index >= vector->m_size)
   7396         return NULL;
   7397 
   7398     return (char *)vector->m_data + index * vector->m_item_size;
   7399 }
   7400 
   7401 
   7402 FT_INTERNAL
   7403 void *vector_at(f_vector_t *vector, size_t index)
   7404 {
   7405     if (index >= vector->m_size)
   7406         return NULL;
   7407 
   7408     return (char *)vector->m_data + index * vector->m_item_size;
   7409 }
   7410 
   7411 
   7412 FT_INTERNAL
   7413 f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos)
   7414 {
   7415     assert(cur_vec);
   7416     assert(mv_vec);
   7417     assert(cur_vec != mv_vec);
   7418     assert(cur_vec->m_item_size == mv_vec->m_item_size);
   7419 
   7420     size_t cur_sz = vector_size(cur_vec);
   7421     size_t mv_sz = vector_size(mv_vec);
   7422     if (mv_sz == 0) {
   7423         return FT_SUCCESS;
   7424     }
   7425 
   7426     size_t min_targ_size = pos + mv_sz;
   7427     if (vector_capacity(cur_vec) < min_targ_size) {
   7428         if (vector_reallocate_(cur_vec, min_targ_size) == -1)
   7429             return FT_GEN_ERROR;
   7430         cur_vec->m_capacity = min_targ_size;
   7431     }
   7432 
   7433     size_t offset = pos * cur_vec->m_item_size;
   7434     void *tmp = NULL;
   7435     size_t new_mv_sz = 0;
   7436     if (cur_sz > pos) {
   7437         new_mv_sz = MIN(cur_sz - pos, mv_sz);
   7438         tmp = F_MALLOC(cur_vec->m_item_size * new_mv_sz);
   7439         if (tmp == NULL) {
   7440             return FT_MEMORY_ERROR;
   7441         }
   7442     }
   7443 
   7444     if (tmp) {
   7445         memcpy(tmp,
   7446                (char *)cur_vec->m_data + offset,
   7447                cur_vec->m_item_size * new_mv_sz);
   7448     }
   7449 
   7450     memcpy((char *)cur_vec->m_data + offset,
   7451            mv_vec->m_data,
   7452            cur_vec->m_item_size * mv_sz);
   7453 
   7454     if (tmp) {
   7455         memcpy(mv_vec->m_data,
   7456                tmp,
   7457                cur_vec->m_item_size * new_mv_sz);
   7458     }
   7459 
   7460     cur_vec->m_size = MAX(cur_vec->m_size, min_targ_size);
   7461     mv_vec->m_size = new_mv_sz;
   7462     F_FREE(tmp);
   7463     return FT_SUCCESS;
   7464 }
   7465 
   7466 FT_INTERNAL
   7467 void vector_clear(f_vector_t *vector)
   7468 {
   7469     vector->m_size = 0;
   7470 }
   7471 
   7472 FT_INTERNAL
   7473 int vector_erase(f_vector_t *vector, size_t index)
   7474 {
   7475     assert(vector);
   7476 
   7477     if (vector->m_size == 0 || index >= vector->m_size)
   7478         return FT_GEN_ERROR;
   7479 
   7480     memmove((char *)vector->m_data + vector->m_item_size * index,
   7481             (char *)vector->m_data + vector->m_item_size * (index + 1),
   7482             (vector->m_size - 1 - index) * vector->m_item_size);
   7483     vector->m_size--;
   7484     return FT_SUCCESS;
   7485 }
   7486 
   7487 #ifdef FT_TEST_BUILD
   7488 
   7489 f_vector_t *copy_vector(f_vector_t *v)
   7490 {
   7491     if (v == NULL)
   7492         return NULL;
   7493 
   7494     f_vector_t *new_vector = create_vector(v->m_item_size, v->m_capacity);
   7495     if (new_vector == NULL)
   7496         return NULL;
   7497 
   7498     memcpy(new_vector->m_data, v->m_data, v->m_item_size * v->m_size);
   7499     new_vector->m_size      = v->m_size ;
   7500     new_vector->m_item_size = v->m_item_size ;
   7501     return new_vector;
   7502 }
   7503 
   7504 size_t vector_index_of(const f_vector_t *vector, const void *item)
   7505 {
   7506     assert(vector);
   7507     assert(item);
   7508 
   7509     size_t i = 0;
   7510     for (i = 0; i < vector->m_size; ++i) {
   7511         void *data_pos = (char *)vector->m_data + i * vector->m_item_size;
   7512         if (memcmp(data_pos, item, vector->m_item_size) == 0) {
   7513             return i;
   7514         }
   7515     }
   7516     return INVALID_VEC_INDEX;
   7517 }
   7518 
   7519 #endif
   7520 
   7521 /********************************************************
   7522    End of file "vector.c"
   7523  ********************************************************/
   7524 
   7525 
   7526 /********************************************************
   7527    Begin of file "wcwidth.c"
   7528  ********************************************************/
   7529 
   7530 /*
   7531  * This is an implementation of wcwidth() and wcswidth() (defined in
   7532  * IEEE Std 1002.1-2001) for Unicode.
   7533  *
   7534  * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
   7535  * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
   7536  *
   7537  * In fixed-width output devices, Latin characters all occupy a single
   7538  * "cell" position of equal width, whereas ideographic CJK characters
   7539  * occupy two such cells. Interoperability between terminal-line
   7540  * applications and (teletype-style) character terminals using the
   7541  * UTF-8 encoding requires agreement on which character should advance
   7542  * the cursor by how many cell positions. No established formal
   7543  * standards exist at present on which Unicode character shall occupy
   7544  * how many cell positions on character terminals. These routines are
   7545  * a first attempt of defining such behavior based on simple rules
   7546  * applied to data provided by the Unicode Consortium.
   7547  *
   7548  * For some graphical characters, the Unicode standard explicitly
   7549  * defines a character-cell width via the definition of the East Asian
   7550  * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
   7551  * In all these cases, there is no ambiguity about which width a
   7552  * terminal shall use. For characters in the East Asian Ambiguous (A)
   7553  * class, the width choice depends purely on a preference of backward
   7554  * compatibility with either historic CJK or Western practice.
   7555  * Choosing single-width for these characters is easy to justify as
   7556  * the appropriate long-term solution, as the CJK practice of
   7557  * displaying these characters as double-width comes from historic
   7558  * implementation simplicity (8-bit encoded characters were displayed
   7559  * single-width and 16-bit ones double-width, even for Greek,
   7560  * Cyrillic, etc.) and not any typographic considerations.
   7561  *
   7562  * Much less clear is the choice of width for the Not East Asian
   7563  * (Neutral) class. Existing practice does not dictate a width for any
   7564  * of these characters. It would nevertheless make sense
   7565  * typographically to allocate two character cells to characters such
   7566  * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
   7567  * represented adequately with a single-width glyph. The following
   7568  * routines at present merely assign a single-cell width to all
   7569  * neutral characters, in the interest of simplicity. This is not
   7570  * entirely satisfactory and should be reconsidered before
   7571  * establishing a formal standard in this area. At the moment, the
   7572  * decision which Not East Asian (Neutral) characters should be
   7573  * represented by double-width glyphs cannot yet be answered by
   7574  * applying a simple rule from the Unicode database content. Setting
   7575  * up a proper standard for the behavior of UTF-8 character terminals
   7576  * will require a careful analysis not only of each Unicode character,
   7577  * but also of each presentation form, something the author of these
   7578  * routines has avoided to do so far.
   7579  *
   7580  * http://www.unicode.org/unicode/reports/tr11/
   7581  *
   7582  * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
   7583  *
   7584  * Permission to use, copy, modify, and distribute this software
   7585  * for any purpose and without fee is hereby granted. The author
   7586  * disclaims all warranties with regard to this software.
   7587  *
   7588  * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
   7589  */
   7590 
   7591 /* #include "wcwidth.h" */ /* Commented by amalgamation script */
   7592 
   7593 #ifdef FT_HAVE_WCHAR
   7594 
   7595 
   7596 struct interval {
   7597     int32_t first;
   7598     int32_t last;
   7599 };
   7600 
   7601 /* auxiliary function for binary search in interval table */
   7602 static int bisearch(int32_t ucs, const struct interval *table, int max)
   7603 {
   7604     int min = 0;
   7605 
   7606     if (ucs < table[0].first || ucs > table[max].last)
   7607         return 0;
   7608     while (max >= min) {
   7609         int mid = (min + max) / 2;
   7610         if (ucs > table[mid].last)
   7611             min = mid + 1;
   7612         else if (ucs < table[mid].first)
   7613             max = mid - 1;
   7614         else
   7615             return 1;
   7616     }
   7617 
   7618     return 0;
   7619 }
   7620 
   7621 
   7622 /* The following two functions define the column width of an ISO 10646
   7623  * character as follows:
   7624  *
   7625  *    - The null character (U+0000) has a column width of 0.
   7626  *
   7627  *    - Other C0/C1 control characters and DEL will lead to a return
   7628  *      value of -1.
   7629  *
   7630  *    - Non-spacing and enclosing combining characters (general
   7631  *      category code Mn or Me in the Unicode database) have a
   7632  *      column width of 0.
   7633  *
   7634  *    - SOFT HYPHEN (U+00AD) has a column width of 1.
   7635  *
   7636  *    - Other format characters (general category code Cf in the Unicode
   7637  *      database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
   7638  *
   7639  *    - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
   7640  *      have a column width of 0.
   7641  *
   7642  *    - Spacing characters in the East Asian Wide (W) or East Asian
   7643  *      Full-width (F) category as defined in Unicode Technical
   7644  *      Report #11 have a column width of 2.
   7645  *
   7646  *    - All remaining characters (including all printable
   7647  *      ISO 8859-1 and WGL4 characters, Unicode control characters,
   7648  *      etc.) have a column width of 1.
   7649  *
   7650  * This implementation assumes that wchar_t characters are encoded
   7651  * in ISO 10646.
   7652  */
   7653 
   7654 static int mk_wcwidth(wchar_t wcs)
   7655 {
   7656     /* sorted list of non-overlapping intervals of non-spacing characters */
   7657     /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
   7658     static const struct interval combining[] = {
   7659         { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
   7660         { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
   7661         { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
   7662         { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
   7663         { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
   7664         { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
   7665         { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
   7666         { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
   7667         { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
   7668         { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
   7669         { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
   7670         { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
   7671         { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
   7672         { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
   7673         { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
   7674         { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
   7675         { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
   7676         { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
   7677         { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
   7678         { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
   7679         { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
   7680         { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
   7681         { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
   7682         { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
   7683         { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
   7684         { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
   7685         { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
   7686         { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
   7687         { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
   7688         { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
   7689         { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
   7690         { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
   7691         { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
   7692         { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
   7693         { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
   7694         { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
   7695         { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
   7696         { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
   7697         { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
   7698         { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
   7699         { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
   7700         { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
   7701         { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
   7702         { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
   7703         { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
   7704         { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
   7705         { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
   7706         { 0xE0100, 0xE01EF }
   7707     };
   7708 
   7709     /* We convert wchar_t to int32_t to avoid compiler warnings
   7710      * about implicit integer conversions
   7711      * https://github.com/seleznevae/libfort/issues/20
   7712      *
   7713      * note: didn't test if we can do it
   7714      */
   7715     int32_t ucs = (int32_t)wcs;
   7716 
   7717     /* test for 8-bit control characters */
   7718     if (ucs == 0)
   7719         return 0;
   7720     if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
   7721         return -1;
   7722 
   7723     /* binary search in table of non-spacing characters */
   7724     if (bisearch(ucs, combining,
   7725                  sizeof(combining) / sizeof(struct interval) - 1))
   7726         return 0;
   7727 
   7728     /* if we arrive here, ucs is not a combining or C0/C1 control character */
   7729 
   7730     return 1 +
   7731            (ucs >= 0x1100 &&
   7732             (ucs <= 0x115f ||                    /* Hangul Jamo init. consonants */
   7733              ucs == 0x2329 || ucs == 0x232a ||
   7734              (ucs >= 0x2e80 && ucs <= 0xa4cf &&
   7735               ucs != 0x303f) ||                  /* CJK ... Yi */
   7736              (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
   7737              (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
   7738              (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
   7739              (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
   7740              (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
   7741              (ucs >= 0xffe0 && ucs <= 0xffe6) ||
   7742              (ucs >= 0x20000 && ucs <= 0x2fffd) ||
   7743              (ucs >= 0x30000 && ucs <= 0x3fffd)));
   7744 }
   7745 
   7746 
   7747 FT_INTERNAL
   7748 int mk_wcswidth(const wchar_t *pwcs, size_t n)
   7749 {
   7750     int width = 0;
   7751 
   7752     for (; *pwcs && n-- > 0; pwcs++) {
   7753         int w;
   7754         if ((w = mk_wcwidth(*pwcs)) < 0)
   7755             return -1;
   7756         else
   7757             width += w;
   7758     }
   7759 
   7760     return width;
   7761 }
   7762 #endif /* FT_HAVE_WCHAR */
   7763 
   7764 /********************************************************
   7765    End of file "wcwidth.c"
   7766  ********************************************************/
   7767