💾 Archived View for gmi.noulin.net › gitRepositories › csvFmt › file › fort.c.gmi captured on 2024-08-18 at 17:54:07. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

csvFmt

Log

Files

Refs

README

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