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