csvFmt

Display csv file with formating
git clone https://noulin.net/git/csvFmt.git
Log | Files | Refs | README

commit 7c3fb279dcf09679558fe2eb972dc0dc1733c488
Author: Remy Noulin <loader2x@gmail.com>
Date:   Mon,  3 Oct 2022 12:21:26 +0200

show csv files with formating

.gitignore  |   63 +
README.md   |   14 +
csvFmt.c    |   94 +
fort.c      | 7767 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fort.h      | 1057 ++++++++
main.c      |   82 +
package.yml |   37 +
phones.csv  |   17 +
styles.txt  |  116 +
9 files changed, 9247 insertions(+)

Diffstat:
A.gitignore | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 14++++++++++++++
AcsvFmt.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afort.c | 7767+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afort.h | 1057+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amain.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.yml | 37+++++++++++++++++++++++++++++++++++++
Aphones.csv | 17+++++++++++++++++
Astyles.txt | 116+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 9247 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,63 @@ +# Vim +*.sw* + +# Debug +.gdb_history + +# Coverage +*.gcov +*.gcda +*.gcno + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/README.md b/README.md @@ -0,0 +1,14 @@ +# csvFmt + +Display a csv file (';' separated) with formating. + +``` +./csvFmt.c phones.csv +``` + +It uses libfort: + +[libfort](https://github.com/seleznevae/libfort) +From commit 4123716 + +I added the property FT_CPROP_CELL_BG_RGBCOLOR to set cell background color to a 24bit color value. diff --git a/csvFmt.c b/csvFmt.c @@ -0,0 +1,94 @@ +#! /usr/bin/env sheepy +/* or direct path to sheepy: #! /usr/local/bin/sheepy */ + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ +#include "libsheepyObject.h" +#include "fort.h" + +/* enable/disable logging */ +/* #undef pLog */ +/* #define pLog(...) */ + +int main(int ARGC, char** ARGV) { + + initLibsheepy(ARGV[0]); + setLogMode(LOG_FUNC); + + if (ARGC < 2) { + logE("Missing csv file argument"); + ret 1; + } + + + cleanListP(list) = NULL; + void **csv = NULL; + + // load file.csv lines in list + list = readText(ARGV[1]); + + if (!list) { + logE("Error loading %s", ARGV[1]); + ret 1; + } + + int rows = 0; + int cols = 0; + + // split lines and store in csv list + forEachCharP(list, l) { + char **spl = split(*l, ";"); + pErrorNULL(listPush(&csv, spl)); + cols = maxV(cols, lenG(spl)); + inc rows; + } + + char **t = malloc(rows * cols * sizeof(char*)); + int j = 0; + forEachType(void, csv, L) { + char **l = (char**) (*L); + int i = 0; + forEachS(l, c) { + *(t + j * cols + i) = c; + inc i; + } + if (i < cols) { + while (i < cols) { + *(t + j * cols + i) = ""; + inc i; + } + } + inc j; + } + + ft_table_t *table2 = ft_create_table(); + + + ft_set_border_style(table2, FT_SIMPLE_STYLE); + //ft_set_border_style(table2, FT_DOUBLE_STYLE); + + ft_table_write(table2, rows, cols, (const char **) t); + + ft_set_cell_prop(table2, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + range(i, cols) { + ft_set_cell_prop(table2, 0, i, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); + } + range(i, rows) { + ft_set_cell_prop(table2, i, FT_ANY_COLUMN, FT_CPROP_CELL_BG_COLOR, FT_COLOR_DEFAULT); + ft_set_cell_prop(table2, i, FT_ANY_COLUMN, FT_CPROP_CELL_BG_RGBCOLOR, i & 1 ? 0x2f2f2f : 0x1f1f1f); + } + ft_set_cell_prop(table2, 0, FT_ANY_COLUMN, FT_CPROP_CELL_BG_RGBCOLOR, 0x4867ff); + + // 0x4042f + // 0x2f2f2f + printf("%s\n", ft_to_string(table2)); + ft_destroy_table(table2); + + free(t); + // free csv list + forEachType(void, csv, l) { + listFreeS(*l); + } + free(csv); + ret 0; +} +// vim: set expandtab ts=2 sw=2: diff --git a/fort.c b/fort.c @@ -0,0 +1,7767 @@ +/* +libfort + +MIT License + +Copyright (c) 2017 - 2020 Seleznev Anton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* The file was GENERATED by an amalgamation script.*/ +/* DO NOT EDIT BY HAND!!! */ + + +#define FT_AMALGAMED_SOURCE /* Macros to make internal libfort functions static */ + + +/******************************************************** + Begin of file "fort_utils.h" + ********************************************************/ + +#ifndef FORT_IMPL_H +#define FORT_IMPL_H + +#if defined(_MSC_VER) +#define _CRT_SECURE_NO_WARNINGS /* To disable warnings for unsafe functions */ +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include <stdbool.h> +#include "fort.h" + +/* Define FT_INTERNAL to make internal libfort functions static + * in the result amalgamed source file. + */ +#ifdef FT_AMALGAMED_SOURCE +#define FT_INTERNAL static +#else +#define FT_INTERNAL +#endif /* FT_AMALGAMED_SORCE */ + + +#define FORT_DEFAULT_COL_SEPARATOR '|' +extern char g_col_separator; + +#define FORT_COL_SEPARATOR_LENGTH 1 + +#define FORT_UNUSED __attribute__((unused)) + +#define F_MALLOC fort_malloc +#define F_FREE fort_free +#define F_CALLOC fort_calloc +#define F_REALLOC fort_realloc +#define F_STRDUP fort_strdup +#define F_WCSDUP fort_wcsdup +/* @todo: replace with custom impl !!!*/ +#define F_UTF8DUP utf8dup + +#define F_CREATE(type) ((type *)F_CALLOC(sizeof(type), 1)) + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +#define FT_NEWLINE "\n" +#define FT_SPACE " " + +/***************************************************************************** + * DEFAULT_SIZES + * ***************************************************************************/ +#define DEFAULT_STR_BUF_SIZE 1024 +#define DEFAULT_VECTOR_CAPACITY 10 + +/***************************************************************************** + * DATA TYPES + * ***************************************************************************/ + +enum f_get_policy { + CREATE_ON_NULL, + DONT_CREATE_ON_NULL +}; + +enum f_bool { + F_FALSE = 0, + F_TRUE = 1 +}; + +enum f_cell_type { + COMMON_CELL, + GROUP_MASTER_CELL, + GROUP_SLAVE_CELL +}; + +enum f_geometry_type { + VISIBLE_GEOMETRY, + INTERN_REPR_GEOMETRY +}; + +enum f_string_type { + CHAR_BUF, +#ifdef FT_HAVE_WCHAR + W_CHAR_BUF, +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + UTF8_BUF, +#endif /* FT_HAVE_WCHAR */ +}; + +struct f_string_view { + union { + const char *cstr; +#ifdef FT_HAVE_WCHAR + const wchar_t *wstr; +#endif +#ifdef FT_HAVE_UTF8 + const void *u8str; +#endif + const void *data; + } u; + enum f_string_type type; +}; +typedef struct f_string_view f_string_view_t; + + +#define FT_STR_2_CAT_(arg1, arg2) \ + arg1##arg2 +#define FT_STR_2_CAT(arg1, arg2) \ + FT_STR_2_CAT_(arg1, arg2) + +#define UNIQUE_NAME_(prefix) \ + FT_STR_2_CAT(prefix,__COUNTER__) +#define UNIQUE_NAME(prefix) \ + UNIQUE_NAME_(prefix) + +typedef int f_status; + + + + +struct f_table_properties; +struct f_row; +struct f_vector; +struct f_cell; +struct f_string_buffer; +struct f_separator { + int enabled; +}; + +typedef struct f_table_properties f_table_properties_t; +typedef struct f_vector f_vector_t; +typedef struct f_cell f_cell_t; +typedef struct f_string_buffer f_string_buffer_t; +typedef struct f_row f_row_t; +typedef struct f_separator f_separator_t; + +struct f_context { + f_table_properties_t *table_properties; + size_t row; + size_t column; +}; +typedef struct f_context f_context_t; + +struct f_conv_context { + union { + char *buf; +#ifdef FT_HAVE_WCHAR + wchar_t *wbuf; +#endif +#ifdef FT_HAVE_UTF8 + const void *u8str; +#endif + } u; + size_t raw_avail; + struct f_context *cntx; + enum f_string_type b_type; +}; +typedef struct f_conv_context f_conv_context_t; + + +/***************************************************************************** + * LIBFORT helpers + *****************************************************************************/ + +extern void *(*fort_malloc)(size_t size); +extern void (*fort_free)(void *ptr); +extern void *(*fort_calloc)(size_t nmemb, size_t size); +extern void *(*fort_realloc)(void *ptr, size_t size); + +FT_INTERNAL +void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)); + +FT_INTERNAL +char *fort_strdup(const char *str); + + + +FT_INTERNAL +size_t number_of_columns_in_format_string(const f_string_view_t *fmt); + +FT_INTERNAL +size_t number_of_columns_in_format_buffer(const f_string_buffer_t *fmt); + +#if defined(FT_HAVE_WCHAR) +FT_INTERNAL +wchar_t *fort_wcsdup(const wchar_t *str); +#endif + + + +FT_INTERNAL +int print_n_strings(f_conv_context_t *cntx, size_t n, const char *str); + + +FT_INTERNAL +int ft_nprint(f_conv_context_t *cntx, const char *str, size_t strlen); +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +int ft_nwprint(f_conv_context_t *cntx, const wchar_t *str, size_t strlen); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +int ft_nu8print(f_conv_context_t *cntx, const void *beg, const void *end); +#endif /* FT_HAVE_UTF8 */ + + +/*#define PRINT_DEBUG_INFO fprintf(stderr, "error in %s(%s:%d)\n", __FUNCTION__, __FILE__, __LINE__);*/ +#define PRINT_DEBUG_INFO + +#define FT_CHECK(statement) \ + do { \ + tmp = statement; \ + if (tmp < 0) {\ + PRINT_DEBUG_INFO \ + goto clear; \ + } \ + } while(0) + +#define CHCK_RSLT_ADD_TO_WRITTEN(statement) \ + do { \ + tmp = statement; \ + if (tmp < 0) {\ + PRINT_DEBUG_INFO \ + goto clear; \ + } \ + written += (size_t)tmp; \ + } while(0) + +#define CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(statement) \ + do { \ + tmp = statement; \ + if (tmp < 0) {\ + PRINT_DEBUG_INFO \ + goto clear; \ + } \ + invisible_written += (size_t)tmp; \ + } while(0) + + +#define CHECK_NOT_NEGATIVE(x) \ + do { if ((x) < 0) goto fort_fail; } while (0) + +#endif /* FORT_IMPL_H */ + +/******************************************************** + End of file "fort_utils.h" + ********************************************************/ + + +/******************************************************** + Begin of file "vector.h" + ********************************************************/ + +#ifndef VECTOR_H +#define VECTOR_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ + + +#define INVALID_VEC_INDEX ((size_t) -1) + +FT_INTERNAL +f_vector_t *create_vector(size_t item_size, size_t capacity); + +FT_INTERNAL +void destroy_vector(f_vector_t *); + +FT_INTERNAL +size_t vector_size(const f_vector_t *); + +FT_INTERNAL +size_t vector_capacity(const f_vector_t *); + +FT_INTERNAL +int vector_push(f_vector_t *, const void *item); + +FT_INTERNAL +int vector_insert(f_vector_t *, const void *item, size_t pos); + +FT_INTERNAL +f_vector_t *vector_split(f_vector_t *, size_t pos); + +FT_INTERNAL +const void *vector_at_c(const f_vector_t *vector, size_t index); + +FT_INTERNAL +void *vector_at(f_vector_t *, size_t index); + +FT_INTERNAL +f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos); + +FT_INTERNAL +void vector_clear(f_vector_t *); + +FT_INTERNAL +int vector_erase(f_vector_t *, size_t index); + +#ifdef FT_TEST_BUILD +f_vector_t *copy_vector(f_vector_t *); +size_t vector_index_of(const f_vector_t *, const void *item); +#endif + +#define VECTOR_AT(vector, pos, data_type) \ + *(data_type *)vector_at((vector), (pos)) + +#define VECTOR_AT_C(vector, pos, const_data_type) \ + *(const_data_type *)vector_at_c((vector), (pos)) + +#endif /* VECTOR_H */ + +/******************************************************** + End of file "vector.h" + ********************************************************/ + + +/******************************************************** + Begin of file "wcwidth.h" + ********************************************************/ + +#ifndef WCWIDTH_H +#define WCWIDTH_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ + +#ifdef FT_HAVE_WCHAR +#include <wchar.h> + +FT_INTERNAL +int mk_wcswidth(const wchar_t *pwcs, size_t n); + +#endif /* FT_HAVE_WCHAR */ + +#endif /* WCWIDTH_H */ + +/******************************************************** + End of file "wcwidth.h" + ********************************************************/ + + +/******************************************************** + Begin of file "utf8.h" + ********************************************************/ + +// The latest version of this library is available on GitHub; +// https://github.com/sheredom/utf8.h + +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to <http://unlicense.org/> + +#ifndef SHEREDOM_UTF8_H_INCLUDED +#define SHEREDOM_UTF8_H_INCLUDED + +#if defined(_MSC_VER) +#pragma warning(push) + +// disable 'bytes padding added after construct' warning +#pragma warning(disable : 4820) +#endif + +#include <stddef.h> +#include <stdlib.h> + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#if defined(_MSC_VER) +typedef __int32 utf8_int32_t; +#else +#include <stdint.h> +typedef int32_t utf8_int32_t; +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wcast-qual" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__clang__) || defined(__GNUC__) +#define utf8_nonnull __attribute__((nonnull)) +#define utf8_pure __attribute__((pure)) +#define utf8_restrict __restrict__ +#define utf8_weak __attribute__((weak)) +#elif defined(_MSC_VER) +#define utf8_nonnull +#define utf8_pure +#define utf8_restrict __restrict +#define utf8_weak __inline +#else +#define utf8_nonnull +#define utf8_pure +#define utf8_restrict +#define utf8_weak inline +#endif + +#ifdef __cplusplus +#define utf8_null NULL +#else +#define utf8_null 0 +#endif + +// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 > +// src2 respectively, case insensitive. +utf8_nonnull utf8_pure utf8_weak int utf8casecmp(const void *src1, + const void *src2); + +// Append the utf8 string src onto the utf8 string dst. +utf8_nonnull utf8_weak void *utf8cat(void *utf8_restrict dst, + const void *utf8_restrict src); + +// Find the first match of the utf8 codepoint chr in the utf8 string src. +utf8_nonnull utf8_pure utf8_weak void *utf8chr(const void *src, + utf8_int32_t chr); + +// Return less than 0, 0, greater than 0 if src1 < src2, +// src1 == src2, src1 > src2 respectively. +utf8_nonnull utf8_pure utf8_weak int utf8cmp(const void *src1, + const void *src2); + +// Copy the utf8 string src onto the memory allocated in dst. +utf8_nonnull utf8_weak void *utf8cpy(void *utf8_restrict dst, + const void *utf8_restrict src); + +// Number of utf8 codepoints in the utf8 string src that consists entirely +// of utf8 codepoints not from the utf8 string reject. +utf8_nonnull utf8_pure utf8_weak size_t utf8cspn(const void *src, + const void *reject); + +// Duplicate the utf8 string src by getting its size, malloc'ing a new buffer +// copying over the data, and returning that. Or 0 if malloc failed. +utf8_nonnull utf8_weak void *utf8dup(const void *src); + +// Number of utf8 codepoints in the utf8 string str, +// excluding the null terminating byte. +utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str); + +// Visible width of utf8string. +utf8_nonnull utf8_pure utf8_weak size_t utf8width(const void *str); + +// Visible width of codepoint. +utf8_nonnull utf8_pure utf8_weak int utf8cwidth(utf8_int32_t c); + +// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 > +// src2 respectively, case insensitive. Checking at most n bytes of each utf8 +// string. +utf8_nonnull utf8_pure utf8_weak int utf8ncasecmp(const void *src1, + const void *src2, size_t n); + +// Append the utf8 string src onto the utf8 string dst, +// writing at most n+1 bytes. Can produce an invalid utf8 +// string if n falls partway through a utf8 codepoint. +utf8_nonnull utf8_weak void *utf8ncat(void *utf8_restrict dst, + const void *utf8_restrict src, size_t n); + +// Return less than 0, 0, greater than 0 if src1 < src2, +// src1 == src2, src1 > src2 respectively. Checking at most n +// bytes of each utf8 string. +utf8_nonnull utf8_pure utf8_weak int utf8ncmp(const void *src1, + const void *src2, size_t n); + +// Copy the utf8 string src onto the memory allocated in dst. +// Copies at most n bytes. If there is no terminating null byte in +// the first n bytes of src, the string placed into dst will not be +// null-terminated. If the size (in bytes) of src is less than n, +// extra null terminating bytes are appended to dst such that at +// total of n bytes are written. Can produce an invalid utf8 +// string if n falls partway through a utf8 codepoint. +utf8_nonnull utf8_weak void *utf8ncpy(void *utf8_restrict dst, + const void *utf8_restrict src, size_t n); + +// Similar to utf8dup, except that at most n bytes of src are copied. If src is +// longer than n, only n bytes are copied and a null byte is added. +// +// Returns a new string if successful, 0 otherwise +utf8_nonnull utf8_weak void *utf8ndup(const void *src, size_t n); + +// Locates the first occurence in the utf8 string str of any byte in the +// utf8 string accept, or 0 if no match was found. +utf8_nonnull utf8_pure utf8_weak void *utf8pbrk(const void *str, + const void *accept); + +// Find the last match of the utf8 codepoint chr in the utf8 string src. +utf8_nonnull utf8_pure utf8_weak void *utf8rchr(const void *src, int chr); + +// Number of bytes in the utf8 string str, +// including the null terminating byte. +utf8_nonnull utf8_pure utf8_weak size_t utf8size(const void *str); + +// Number of utf8 codepoints in the utf8 string src that consists entirely +// of utf8 codepoints from the utf8 string accept. +utf8_nonnull utf8_pure utf8_weak size_t utf8spn(const void *src, + const void *accept); + +// The position of the utf8 string needle in the utf8 string haystack. +utf8_nonnull utf8_pure utf8_weak void *utf8str(const void *haystack, + const void *needle); + +// The position of the utf8 string needle in the utf8 string haystack, case +// insensitive. +utf8_nonnull utf8_pure utf8_weak void *utf8casestr(const void *haystack, + const void *needle); + +// Return 0 on success, or the position of the invalid +// utf8 codepoint on failure. +utf8_nonnull utf8_pure utf8_weak void *utf8valid(const void *str); + +// Sets out_codepoint to the next utf8 codepoint in str, and returns the address +// of the utf8 codepoint after the current one in str. +utf8_nonnull utf8_weak void * +utf8codepoint(const void *utf8_restrict str, + utf8_int32_t *utf8_restrict out_codepoint); + +// Returns the size of the given codepoint in bytes. +utf8_weak size_t utf8codepointsize(utf8_int32_t chr); + +// Write a codepoint to the given string, and return the address to the next +// place after the written codepoint. Pass how many bytes left in the buffer to +// n. If there is not enough space for the codepoint, this function returns +// null. +utf8_nonnull utf8_weak void *utf8catcodepoint(void *utf8_restrict str, + utf8_int32_t chr, size_t n); + +// Returns 1 if the given character is lowercase, or 0 if it is not. +utf8_weak int utf8islower(utf8_int32_t chr); + +// Returns 1 if the given character is uppercase, or 0 if it is not. +utf8_weak int utf8isupper(utf8_int32_t chr); + +// Transform the given string into all lowercase codepoints. +utf8_nonnull utf8_weak void utf8lwr(void *utf8_restrict str); + +// Transform the given string into all uppercase codepoints. +utf8_nonnull utf8_weak void utf8upr(void *utf8_restrict str); + +// Make a codepoint lower case if possible. +utf8_weak utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp); + +// Make a codepoint upper case if possible. +utf8_weak utf8_int32_t utf8uprcodepoint(utf8_int32_t cp); + +#undef utf8_weak +#undef utf8_pure +#undef utf8_nonnull + +int utf8casecmp(const void *src1, const void *src2) +{ + utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; + + for (;;) { + src1 = utf8codepoint(src1, &src1_cp); + src2 = utf8codepoint(src2, &src2_cp); + + // Take a copy of src1 & src2 + src1_orig_cp = src1_cp; + src2_orig_cp = src2_cp; + + // Lower the srcs if required + src1_cp = utf8lwrcodepoint(src1_cp); + src2_cp = utf8lwrcodepoint(src2_cp); + + // Check if the lowered codepoints match + if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) { + return 0; + } else if (src1_cp == src2_cp) { + continue; + } + + // If they don't match, then we return which of the original's are less + if (src1_orig_cp < src2_orig_cp) { + return -1; + } else if (src1_orig_cp > src2_orig_cp) { + return 1; + } + } +} + +void *utf8cat(void *utf8_restrict dst, const void *utf8_restrict src) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + + // find the null terminating byte in dst + while ('\0' != *d) { + d++; + } + + // overwriting the null terminating byte in dst, append src byte-by-byte + while ('\0' != *s) { + *d++ = *s++; + } + + // write out a new null terminating byte into dst + *d = '\0'; + + return dst; +} + +void *utf8chr(const void *src, utf8_int32_t chr) +{ + char c[5] = {'\0', '\0', '\0', '\0', '\0'}; + + if (0 == chr) { + // being asked to return position of null terminating byte, so + // just run s to the end, and return! + const char *s = (const char *)src; + while ('\0' != *s) { + s++; + } + return (void *)s; + } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + c[0] = (char)chr; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + c[0] = 0xc0 | (char)(chr >> 6); + c[1] = 0x80 | (char)(chr & 0x3f); + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xe0 | (char)(chr >> 12); + c[1] = 0x80 | (char)((chr >> 6) & 0x3f); + c[2] = 0x80 | (char)(chr & 0x3f); + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xf0 | (char)(chr >> 18); + c[1] = 0x80 | (char)((chr >> 12) & 0x3f); + c[2] = 0x80 | (char)((chr >> 6) & 0x3f); + c[3] = 0x80 | (char)(chr & 0x3f); + } + + // we've made c into a 2 utf8 codepoint string, one for the chr we are + // seeking, another for the null terminating byte. Now use utf8str to + // search + return utf8str(src, c); +} + +int utf8cmp(const void *src1, const void *src2) +{ + const unsigned char *s1 = (const unsigned char *)src1; + const unsigned char *s2 = (const unsigned char *)src2; + + while (('\0' != *s1) || ('\0' != *s2)) { + if (*s1 < *s2) { + return -1; + } else if (*s1 > *s2) { + return 1; + } + + s1++; + s2++; + } + + // both utf8 strings matched + return 0; +} + +int utf8coll(const void *src1, const void *src2); + +void *utf8cpy(void *utf8_restrict dst, const void *utf8_restrict src) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + + // overwriting anything previously in dst, write byte-by-byte + // from src + while ('\0' != *s) { + *d++ = *s++; + } + + // append null terminating byte + *d = '\0'; + + return dst; +} + +size_t utf8cspn(const void *src, const void *reject) +{ + const char *s = (const char *)src; + size_t chars = 0; + + while ('\0' != *s) { + const char *r = (const char *)reject; + size_t offset = 0; + + while ('\0' != *r) { + // checking that if *r is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *r)) && (0 < offset)) { + return chars; + } else { + if (*r == s[offset]) { + // part of a utf8 codepoint matched, so move our checking + // onwards to the next byte + offset++; + r++; + } else { + // r could be in the middle of an unmatching utf8 code point, + // so we need to march it on to the next character beginning, + + do { + r++; + } while (0x80 == (0xc0 & *r)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // the current utf8 codepoint in src did not match reject, but src + // could have been partway through a utf8 codepoint, so we need to + // march it onto the next utf8 codepoint starting byte + do { + s++; + } while ((0x80 == (0xc0 & *s))); + chars++; + } + + return chars; +} + +size_t utf8size(const void *str); + +void *utf8dup(const void *src) +{ + const char *s = (const char *)src; + char *n = utf8_null; + + // figure out how many bytes (including the terminator) we need to copy first + size_t bytes = utf8size(src); + + n = (char *)malloc(bytes); + + if (utf8_null == n) { + // out of memory so we bail + return utf8_null; + } else { + bytes = 0; + + // copy src byte-by-byte into our new utf8 string + while ('\0' != s[bytes]) { + n[bytes] = s[bytes]; + bytes++; + } + + // append null terminating byte + n[bytes] = '\0'; + return n; + } +} + +void *utf8fry(const void *str); + +size_t utf8len(const void *str) +{ + const unsigned char *s = (const unsigned char *)str; + size_t length = 0; + + while ('\0' != *s) { + if (0xf0 == (0xf8 & *s)) { + // 4-byte utf8 code point (began with 0b11110xxx) + s += 4; + } else if (0xe0 == (0xf0 & *s)) { + // 3-byte utf8 code point (began with 0b1110xxxx) + s += 3; + } else if (0xc0 == (0xe0 & *s)) { + // 2-byte utf8 code point (began with 0b110xxxxx) + s += 2; + } else { // if (0x00 == (0x80 & *s)) { + // 1-byte ascii (began with 0b0xxxxxxx) + s += 1; + } + + // no matter the bytes we marched s forward by, it was + // only 1 utf8 codepoint + length++; + } + + return length; +} + +// See +// https://unicode.org/Public/UNIDATA/EastAsianWidth.txt +// http://www.unicode.org/reports/tr11/tr11-33.html +int utf8cwidth(utf8_int32_t c) +{ + // TODO: add non printable characters check + if (c == 0) + return 0; + + if (c < 0x1100) + return 1; + + // Fullwidth + if ((0x3000 == c) || + (0xFF01 <= c && c <= 0xFF60) || + (0xFFE0 <= c && c <= 0xFFE6)) { + return 2; + } + + // Wide + if ((0x1100 <= c && c <= 0x115F) || + (0x11A3 <= c && c <= 0x11A7) || + (0x11FA <= c && c <= 0x11FF) || + (0x2329 <= c && c <= 0x232A) || + (0x2E80 <= c && c <= 0x2E99) || + (0x2E9B <= c && c <= 0x2EF3) || + (0x2F00 <= c && c <= 0x2FD5) || + (0x2FF0 <= c && c <= 0x2FFB) || + (0x3001 <= c && c <= 0x303E) || + (0x3041 <= c && c <= 0x3096) || + (0x3099 <= c && c <= 0x30FF) || + (0x3105 <= c && c <= 0x312D) || + (0x3131 <= c && c <= 0x318E) || + (0x3190 <= c && c <= 0x31BA) || + (0x31C0 <= c && c <= 0x31E3) || + (0x31F0 <= c && c <= 0x321E) || + (0x3220 <= c && c <= 0x3247) || + (0x3250 <= c && c <= 0x32FE) || + (0x3300 <= c && c <= 0x4DBF) || + (0x4E00 <= c && c <= 0xA48C) || + (0xA490 <= c && c <= 0xA4C6) || + (0xA960 <= c && c <= 0xA97C) || + (0xAC00 <= c && c <= 0xD7A3) || + (0xD7B0 <= c && c <= 0xD7C6) || + (0xD7CB <= c && c <= 0xD7FB) || + (0xF900 <= c && c <= 0xFAFF) || + (0xFE10 <= c && c <= 0xFE19) || + (0xFE30 <= c && c <= 0xFE52) || + (0xFE54 <= c && c <= 0xFE66) || + (0xFE68 <= c && c <= 0xFE6B) || + (0x1B000 <= c && c <= 0x1B001) || + (0x1F200 <= c && c <= 0x1F202) || + (0x1F210 <= c && c <= 0x1F23A) || + (0x1F240 <= c && c <= 0x1F248) || + (0x1F250 <= c && c <= 0x1F251) || + (0x20000 <= c && c <= 0x2F73F) || + (0x2B740 <= c && c <= 0x2FFFD) || + (0x30000 <= c && c <= 0x3FFFD)) { + return 2; + } + + return 1; +} + +size_t utf8width(const void *str) +{ + size_t length = 0; + utf8_int32_t c = 0; + + str = utf8codepoint(str, &c); + while (c != 0) { + length += utf8cwidth(c); + str = utf8codepoint(str, &c); + } + return length; +} + +int utf8ncasecmp(const void *src1, const void *src2, size_t n) +{ + utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp; + + do { + const unsigned char *const s1 = (const unsigned char *)src1; + const unsigned char *const s2 = (const unsigned char *)src2; + + // first check that we have enough bytes left in n to contain an entire + // codepoint + if (0 == n) { + return 0; + } + + if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) { + const utf8_int32_t c1 = (0xe0 & *s1); + const utf8_int32_t c2 = (0xe0 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) { + const utf8_int32_t c1 = (0xf0 & *s1); + const utf8_int32_t c2 = (0xf0 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) { + const utf8_int32_t c1 = (0xf8 & *s1); + const utf8_int32_t c2 = (0xf8 & *s2); + + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } else { + return 0; + } + } + + src1 = utf8codepoint(src1, &src1_cp); + src2 = utf8codepoint(src2, &src2_cp); + n -= utf8codepointsize(src1_cp); + + // Take a copy of src1 & src2 + src1_orig_cp = src1_cp; + src2_orig_cp = src2_cp; + + // Lower srcs if required + src1_cp = utf8lwrcodepoint(src1_cp); + src2_cp = utf8lwrcodepoint(src2_cp); + + // Check if the lowered codepoints match + if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) { + return 0; + } else if (src1_cp == src2_cp) { + continue; + } + + // If they don't match, then we return which of the original's are less + if (src1_orig_cp < src2_orig_cp) { + return -1; + } else if (src1_orig_cp > src2_orig_cp) { + return 1; + } + } while (0 < n); + + // both utf8 strings matched + return 0; +} + +void *utf8ncat(void *utf8_restrict dst, const void *utf8_restrict src, + size_t n) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + + // find the null terminating byte in dst + while ('\0' != *d) { + d++; + } + + // overwriting the null terminating byte in dst, append src byte-by-byte + // stopping if we run out of space + do { + *d++ = *s++; + } while (('\0' != *s) && (0 != --n)); + + // write out a new null terminating byte into dst + *d = '\0'; + + return dst; +} + +int utf8ncmp(const void *src1, const void *src2, size_t n) +{ + const unsigned char *s1 = (const unsigned char *)src1; + const unsigned char *s2 = (const unsigned char *)src2; + + while ((0 != n--) && (('\0' != *s1) || ('\0' != *s2))) { + if (*s1 < *s2) { + return -1; + } else if (*s1 > *s2) { + return 1; + } + + s1++; + s2++; + } + + // both utf8 strings matched + return 0; +} + +void *utf8ncpy(void *utf8_restrict dst, const void *utf8_restrict src, + size_t n) +{ + char *d = (char *)dst; + const char *s = (const char *)src; + size_t index; + + // overwriting anything previously in dst, write byte-by-byte + // from src + for (index = 0; index < n; index++) { + d[index] = s[index]; + if ('\0' == s[index]) { + break; + } + } + + // append null terminating byte + for (; index < n; index++) { + d[index] = 0; + } + + return dst; +} + +void *utf8ndup(const void *src, size_t n) +{ + const char *s = (const char *)src; + char *c = utf8_null; + size_t bytes = 0; + + // Find the end of the string or stop when n is reached + while ('\0' != s[bytes] && bytes < n) { + bytes++; + } + + // In case bytes is actually less than n, we need to set it + // to be used later in the copy byte by byte. + n = bytes; + + c = (char *)malloc(bytes + 1); + if (utf8_null == c) { + // out of memory so we bail + return utf8_null; + } + + bytes = 0; + + // copy src byte-by-byte into our new utf8 string + while ('\0' != s[bytes] && bytes < n) { + c[bytes] = s[bytes]; + bytes++; + } + + // append null terminating byte + c[bytes] = '\0'; + return c; +} + +void *utf8rchr(const void *src, int chr) +{ + const char *s = (const char *)src; + const char *match = utf8_null; + char c[5] = {'\0', '\0', '\0', '\0', '\0'}; + + if (0 == chr) { + // being asked to return position of null terminating byte, so + // just run s to the end, and return! + while ('\0' != *s) { + s++; + } + return (void *)s; + } else if (0 == ((int)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + c[0] = (char)chr; + } else if (0 == ((int)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + c[0] = 0xc0 | (char)(chr >> 6); + c[1] = 0x80 | (char)(chr & 0x3f); + } else if (0 == ((int)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xe0 | (char)(chr >> 12); + c[1] = 0x80 | (char)((chr >> 6) & 0x3f); + c[2] = 0x80 | (char)(chr & 0x3f); + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + c[0] = 0xf0 | (char)(chr >> 18); + c[1] = 0x80 | (char)((chr >> 12) & 0x3f); + c[2] = 0x80 | (char)((chr >> 6) & 0x3f); + c[3] = 0x80 | (char)(chr & 0x3f); + } + + // we've created a 2 utf8 codepoint string in c that is + // the utf8 character asked for by chr, and a null + // terminating byte + + while ('\0' != *s) { + size_t offset = 0; + + while (s[offset] == c[offset]) { + offset++; + } + + if ('\0' == c[offset]) { + // we found a matching utf8 code point + match = s; + s += offset; + } else { + s += offset; + + // need to march s along to next utf8 codepoint start + // (the next byte that doesn't match 0b10xxxxxx) + if ('\0' != *s) { + do { + s++; + } while (0x80 == (0xc0 & *s)); + } + } + } + + // return the last match we found (or 0 if no match was found) + return (void *)match; +} + +void *utf8pbrk(const void *str, const void *accept) +{ + const char *s = (const char *)str; + + while ('\0' != *s) { + const char *a = (const char *)accept; + size_t offset = 0; + + while ('\0' != *a) { + // checking that if *a is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *a)) && (0 < offset)) { + return (void *)s; + } else { + if (*a == s[offset]) { + // part of a utf8 codepoint matched, so move our checking + // onwards to the next byte + offset++; + a++; + } else { + // r could be in the middle of an unmatching utf8 code point, + // so we need to march it on to the next character beginning, + + do { + a++; + } while (0x80 == (0xc0 & *a)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // we found a match on the last utf8 codepoint + if (0 < offset) { + return (void *)s; + } + + // the current utf8 codepoint in src did not match accept, but src + // could have been partway through a utf8 codepoint, so we need to + // march it onto the next utf8 codepoint starting byte + do { + s++; + } while ((0x80 == (0xc0 & *s))); + } + + return utf8_null; +} + +size_t utf8size(const void *str) +{ + const char *s = (const char *)str; + size_t size = 0; + while ('\0' != s[size]) { + size++; + } + + // we are including the null terminating byte in the size calculation + size++; + return size; +} + +size_t utf8spn(const void *src, const void *accept) +{ + const char *s = (const char *)src; + size_t chars = 0; + + while ('\0' != *s) { + const char *a = (const char *)accept; + size_t offset = 0; + + while ('\0' != *a) { + // checking that if *r is the start of a utf8 codepoint + // (it is not 0b10xxxxxx) and we have successfully matched + // a previous character (0 < offset) - we found a match + if ((0x80 != (0xc0 & *a)) && (0 < offset)) { + // found a match, so increment the number of utf8 codepoints + // that have matched and stop checking whether any other utf8 + // codepoints in a match + chars++; + s += offset; + break; + } else { + if (*a == s[offset]) { + offset++; + a++; + } else { + // a could be in the middle of an unmatching utf8 codepoint, + // so we need to march it on to the next character beginning, + do { + a++; + } while (0x80 == (0xc0 & *a)); + + // reset offset too as we found a mismatch + offset = 0; + } + } + } + + // if a got to its terminating null byte, then we didn't find a match. + // Return the current number of matched utf8 codepoints + if ('\0' == *a) { + return chars; + } + } + + return chars; +} + +void *utf8str(const void *haystack, const void *needle) +{ + const char *h = (const char *)haystack; + utf8_int32_t throwaway_codepoint; + + // if needle has no utf8 codepoints before the null terminating + // byte then return haystack + if ('\0' == *((const char *)needle)) { + return (void *)haystack; + } + + while ('\0' != *h) { + const char *maybeMatch = h; + const char *n = (const char *)needle; + + while (*h == *n && (*h != '\0' && *n != '\0')) { + n++; + h++; + } + + if ('\0' == *n) { + // we found the whole utf8 string for needle in haystack at + // maybeMatch, so return it + return (void *)maybeMatch; + } else { + // h could be in the middle of an unmatching utf8 codepoint, + // so we need to march it on to the next character beginning + // starting from the current character + h = (const char *)utf8codepoint(maybeMatch, &throwaway_codepoint); + } + } + + // no match + return utf8_null; +} + +void *utf8casestr(const void *haystack, const void *needle) +{ + const void *h = haystack; + + // if needle has no utf8 codepoints before the null terminating + // byte then return haystack + if ('\0' == *((const char *)needle)) { + return (void *)haystack; + } + + for (;;) { + const void *maybeMatch = h; + const void *n = needle; + utf8_int32_t h_cp, n_cp; + + // Get the next code point and track it + const void *nextH = h = utf8codepoint(h, &h_cp); + n = utf8codepoint(n, &n_cp); + + while ((0 != h_cp) && (0 != n_cp)) { + h_cp = utf8lwrcodepoint(h_cp); + n_cp = utf8lwrcodepoint(n_cp); + + // if we find a mismatch, bail out! + if (h_cp != n_cp) { + break; + } + + h = utf8codepoint(h, &h_cp); + n = utf8codepoint(n, &n_cp); + } + + if (0 == n_cp) { + // we found the whole utf8 string for needle in haystack at + // maybeMatch, so return it + return (void *)maybeMatch; + } + + if (0 == h_cp) { + // no match + return utf8_null; + } + + // Roll back to the next code point in the haystack to test + h = nextH; + } +} + +void *utf8valid(const void *str) +{ + const char *s = (const char *)str; + + while ('\0' != *s) { + if (0xf0 == (0xf8 & *s)) { + // ensure each of the 3 following bytes in this 4-byte + // utf8 codepoint began with 0b10xxxxxx + if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2])) || + (0x80 != (0xc0 & s[3]))) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 4 bytes + if (0x80 == (0xc0 & s[4])) { + return (void *)s; + } + + // ensure that the top 5 bits of this 4-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if ((0 == (0x07 & s[0])) && (0 == (0x30 & s[1]))) { + return (void *)s; + } + + // 4-byte utf8 code point (began with 0b11110xxx) + s += 4; + } else if (0xe0 == (0xf0 & *s)) { + // ensure each of the 2 following bytes in this 3-byte + // utf8 codepoint began with 0b10xxxxxx + if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2]))) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 3 bytes + if (0x80 == (0xc0 & s[3])) { + return (void *)s; + } + + // ensure that the top 5 bits of this 3-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if ((0 == (0x0f & s[0])) && (0 == (0x20 & s[1]))) { + return (void *)s; + } + + // 3-byte utf8 code point (began with 0b1110xxxx) + s += 3; + } else if (0xc0 == (0xe0 & *s)) { + // ensure the 1 following byte in this 2-byte + // utf8 codepoint began with 0b10xxxxxx + if (0x80 != (0xc0 & s[1])) { + return (void *)s; + } + + // ensure that our utf8 codepoint ended after 2 bytes + if (0x80 == (0xc0 & s[2])) { + return (void *)s; + } + + // ensure that the top 4 bits of this 2-byte utf8 + // codepoint were not 0, as then we could have used + // one of the smaller encodings + if (0 == (0x1e & s[0])) { + return (void *)s; + } + + // 2-byte utf8 code point (began with 0b110xxxxx) + s += 2; + } else if (0x00 == (0x80 & *s)) { + // 1-byte ascii (began with 0b0xxxxxxx) + s += 1; + } else { + // we have an invalid 0b1xxxxxxx utf8 code point entry + return (void *)s; + } + } + + return utf8_null; +} + +void *utf8codepoint(const void *utf8_restrict str, + utf8_int32_t *utf8_restrict out_codepoint) +{ + const char *s = (const char *)str; + + if (0xf0 == (0xf8 & s[0])) { + // 4 byte utf8 codepoint + *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | + ((0x3f & s[2]) << 6) | (0x3f & s[3]); + s += 4; + } else if (0xe0 == (0xf0 & s[0])) { + // 3 byte utf8 codepoint + *out_codepoint = + ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]); + s += 3; + } else if (0xc0 == (0xe0 & s[0])) { + // 2 byte utf8 codepoint + *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]); + s += 2; + } else { + // 1 byte utf8 codepoint otherwise + *out_codepoint = s[0]; + s += 1; + } + + return (void *)s; +} + +size_t utf8codepointsize(utf8_int32_t chr) +{ + if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + return 1; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + return 2; + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + return 3; + } else { // if (0 == ((int)0xffe00000 & chr)) { + return 4; + } +} + +void *utf8catcodepoint(void *utf8_restrict str, utf8_int32_t chr, size_t n) +{ + char *s = (char *)str; + + if (0 == ((utf8_int32_t)0xffffff80 & chr)) { + // 1-byte/7-bit ascii + // (0b0xxxxxxx) + if (n < 1) { + return utf8_null; + } + s[0] = (char)chr; + s += 1; + } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) { + // 2-byte/11-bit utf8 code point + // (0b110xxxxx 0b10xxxxxx) + if (n < 2) { + return utf8_null; + } + s[0] = 0xc0 | (char)(chr >> 6); + s[1] = 0x80 | (char)(chr & 0x3f); + s += 2; + } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) { + // 3-byte/16-bit utf8 code point + // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx) + if (n < 3) { + return utf8_null; + } + s[0] = 0xe0 | (char)(chr >> 12); + s[1] = 0x80 | (char)((chr >> 6) & 0x3f); + s[2] = 0x80 | (char)(chr & 0x3f); + s += 3; + } else { // if (0 == ((int)0xffe00000 & chr)) { + // 4-byte/21-bit utf8 code point + // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx) + if (n < 4) { + return utf8_null; + } + s[0] = 0xf0 | (char)(chr >> 18); + s[1] = 0x80 | (char)((chr >> 12) & 0x3f); + s[2] = 0x80 | (char)((chr >> 6) & 0x3f); + s[3] = 0x80 | (char)(chr & 0x3f); + s += 4; + } + + return s; +} + +int utf8islower(utf8_int32_t chr) { return chr != utf8uprcodepoint(chr); } + +int utf8isupper(utf8_int32_t chr) { return chr != utf8lwrcodepoint(chr); } + +void utf8lwr(void *utf8_restrict str) +{ + void *p, *pn; + utf8_int32_t cp; + + p = (char *)str; + pn = utf8codepoint(p, &cp); + + while (cp != 0) { + const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp); + const size_t size = utf8codepointsize(lwr_cp); + + if (lwr_cp != cp) { + utf8catcodepoint(p, lwr_cp, size); + } + + p = pn; + pn = utf8codepoint(p, &cp); + } +} + +void utf8upr(void *utf8_restrict str) +{ + void *p, *pn; + utf8_int32_t cp; + + p = (char *)str; + pn = utf8codepoint(p, &cp); + + while (cp != 0) { + const utf8_int32_t lwr_cp = utf8uprcodepoint(cp); + const size_t size = utf8codepointsize(lwr_cp); + + if (lwr_cp != cp) { + utf8catcodepoint(p, lwr_cp, size); + } + + p = pn; + pn = utf8codepoint(p, &cp); + } +} + +utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp) +{ + if (((0x0041 <= cp) && (0x005a >= cp)) || + ((0x00c0 <= cp) && (0x00d6 >= cp)) || + ((0x00d8 <= cp) && (0x00de >= cp)) || + ((0x0391 <= cp) && (0x03a1 >= cp)) || + ((0x03a3 <= cp) && (0x03ab >= cp))) { + cp += 32; + } else if (((0x0100 <= cp) && (0x012f >= cp)) || + ((0x0132 <= cp) && (0x0137 >= cp)) || + ((0x014a <= cp) && (0x0177 >= cp)) || + ((0x0182 <= cp) && (0x0185 >= cp)) || + ((0x01a0 <= cp) && (0x01a5 >= cp)) || + ((0x01de <= cp) && (0x01ef >= cp)) || + ((0x01f8 <= cp) && (0x021f >= cp)) || + ((0x0222 <= cp) && (0x0233 >= cp)) || + ((0x0246 <= cp) && (0x024f >= cp)) || + ((0x03d8 <= cp) && (0x03ef >= cp))) { + cp |= 0x1; + } else if (((0x0139 <= cp) && (0x0148 >= cp)) || + ((0x0179 <= cp) && (0x017e >= cp)) || + ((0x01af <= cp) && (0x01b0 >= cp)) || + ((0x01b3 <= cp) && (0x01b6 >= cp)) || + ((0x01cd <= cp) && (0x01dc >= cp))) { + cp += 1; + cp &= ~0x1; + } else { + switch (cp) { + default: break; + case 0x0178: cp = 0x00ff; break; + case 0x0243: cp = 0x0180; break; + case 0x018e: cp = 0x01dd; break; + case 0x023d: cp = 0x019a; break; + case 0x0220: cp = 0x019e; break; + case 0x01b7: cp = 0x0292; break; + case 0x01c4: cp = 0x01c6; break; + case 0x01c7: cp = 0x01c9; break; + case 0x01ca: cp = 0x01cc; break; + case 0x01f1: cp = 0x01f3; break; + case 0x01f7: cp = 0x01bf; break; + case 0x0187: cp = 0x0188; break; + case 0x018b: cp = 0x018c; break; + case 0x0191: cp = 0x0192; break; + case 0x0198: cp = 0x0199; break; + case 0x01a7: cp = 0x01a8; break; + case 0x01ac: cp = 0x01ad; break; + case 0x01af: cp = 0x01b0; break; + case 0x01b8: cp = 0x01b9; break; + case 0x01bc: cp = 0x01bd; break; + case 0x01f4: cp = 0x01f5; break; + case 0x023b: cp = 0x023c; break; + case 0x0241: cp = 0x0242; break; + case 0x03fd: cp = 0x037b; break; + case 0x03fe: cp = 0x037c; break; + case 0x03ff: cp = 0x037d; break; + case 0x037f: cp = 0x03f3; break; + case 0x0386: cp = 0x03ac; break; + case 0x0388: cp = 0x03ad; break; + case 0x0389: cp = 0x03ae; break; + case 0x038a: cp = 0x03af; break; + case 0x038c: cp = 0x03cc; break; + case 0x038e: cp = 0x03cd; break; + case 0x038f: cp = 0x03ce; break; + case 0x0370: cp = 0x0371; break; + case 0x0372: cp = 0x0373; break; + case 0x0376: cp = 0x0377; break; + case 0x03f4: cp = 0x03d1; break; + case 0x03cf: cp = 0x03d7; break; + case 0x03f9: cp = 0x03f2; break; + case 0x03f7: cp = 0x03f8; break; + case 0x03fa: cp = 0x03fb; break; + }; + } + + return cp; +} + +utf8_int32_t utf8uprcodepoint(utf8_int32_t cp) +{ + if (((0x0061 <= cp) && (0x007a >= cp)) || + ((0x00e0 <= cp) && (0x00f6 >= cp)) || + ((0x00f8 <= cp) && (0x00fe >= cp)) || + ((0x03b1 <= cp) && (0x03c1 >= cp)) || + ((0x03c3 <= cp) && (0x03cb >= cp))) { + cp -= 32; + } else if (((0x0100 <= cp) && (0x012f >= cp)) || + ((0x0132 <= cp) && (0x0137 >= cp)) || + ((0x014a <= cp) && (0x0177 >= cp)) || + ((0x0182 <= cp) && (0x0185 >= cp)) || + ((0x01a0 <= cp) && (0x01a5 >= cp)) || + ((0x01de <= cp) && (0x01ef >= cp)) || + ((0x01f8 <= cp) && (0x021f >= cp)) || + ((0x0222 <= cp) && (0x0233 >= cp)) || + ((0x0246 <= cp) && (0x024f >= cp)) || + ((0x03d8 <= cp) && (0x03ef >= cp))) { + cp &= ~0x1; + } else if (((0x0139 <= cp) && (0x0148 >= cp)) || + ((0x0179 <= cp) && (0x017e >= cp)) || + ((0x01af <= cp) && (0x01b0 >= cp)) || + ((0x01b3 <= cp) && (0x01b6 >= cp)) || + ((0x01cd <= cp) && (0x01dc >= cp))) { + cp -= 1; + cp |= 0x1; + } else { + switch (cp) { + default: break; + case 0x00ff: cp = 0x0178; break; + case 0x0180: cp = 0x0243; break; + case 0x01dd: cp = 0x018e; break; + case 0x019a: cp = 0x023d; break; + case 0x019e: cp = 0x0220; break; + case 0x0292: cp = 0x01b7; break; + case 0x01c6: cp = 0x01c4; break; + case 0x01c9: cp = 0x01c7; break; + case 0x01cc: cp = 0x01ca; break; + case 0x01f3: cp = 0x01f1; break; + case 0x01bf: cp = 0x01f7; break; + case 0x0188: cp = 0x0187; break; + case 0x018c: cp = 0x018b; break; + case 0x0192: cp = 0x0191; break; + case 0x0199: cp = 0x0198; break; + case 0x01a8: cp = 0x01a7; break; + case 0x01ad: cp = 0x01ac; break; + case 0x01b0: cp = 0x01af; break; + case 0x01b9: cp = 0x01b8; break; + case 0x01bd: cp = 0x01bc; break; + case 0x01f5: cp = 0x01f4; break; + case 0x023c: cp = 0x023b; break; + case 0x0242: cp = 0x0241; break; + case 0x037b: cp = 0x03fd; break; + case 0x037c: cp = 0x03fe; break; + case 0x037d: cp = 0x03ff; break; + case 0x03f3: cp = 0x037f; break; + case 0x03ac: cp = 0x0386; break; + case 0x03ad: cp = 0x0388; break; + case 0x03ae: cp = 0x0389; break; + case 0x03af: cp = 0x038a; break; + case 0x03cc: cp = 0x038c; break; + case 0x03cd: cp = 0x038e; break; + case 0x03ce: cp = 0x038f; break; + case 0x0371: cp = 0x0370; break; + case 0x0373: cp = 0x0372; break; + case 0x0377: cp = 0x0376; break; + case 0x03d1: cp = 0x03f4; break; + case 0x03d7: cp = 0x03cf; break; + case 0x03f2: cp = 0x03f9; break; + case 0x03f8: cp = 0x03f7; break; + case 0x03fb: cp = 0x03fa; break; + }; + } + + return cp; +} + +#undef utf8_restrict +#undef utf8_null + +#ifdef __cplusplus +} // extern "C" +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // SHEREDOM_UTF8_H_INCLUDED + + +/******************************************************** + End of file "utf8.h" + ********************************************************/ + + +/******************************************************** + Begin of file "string_buffer.h" + ********************************************************/ + +#ifndef STRING_BUFFER_H +#define STRING_BUFFER_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ + + +/***************************************************************************** + * STRING BUFFER + * ***************************************************************************/ + +struct f_string_buffer { + union { + char *cstr; +#ifdef FT_HAVE_WCHAR + wchar_t *wstr; +#endif +#ifdef FT_HAVE_UTF8 + void *u8str; +#endif + void *data; + } str; + size_t data_sz; + enum f_string_type type; +}; + +FT_INTERNAL +f_string_buffer_t *create_string_buffer(size_t number_of_chars, enum f_string_type type); + +FT_INTERNAL +void destroy_string_buffer(f_string_buffer_t *buffer); + +FT_INTERNAL +f_string_buffer_t *copy_string_buffer(const f_string_buffer_t *buffer); + +FT_INTERNAL +f_status realloc_string_buffer_without_copy(f_string_buffer_t *buffer); + +FT_INTERNAL +f_status fill_buffer_from_string(f_string_buffer_t *buffer, const char *str); + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +f_status fill_buffer_from_wstring(f_string_buffer_t *buffer, const wchar_t *str); +#endif /* FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +f_status fill_buffer_from_u8string(f_string_buffer_t *buffer, const void *str); +#endif /* FT_HAVE_UTF8 */ + +FT_INTERNAL +size_t buffer_text_visible_width(const f_string_buffer_t *buffer); + +FT_INTERNAL +size_t buffer_text_visible_height(const f_string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_cod_width_capacity(const f_string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_raw_capacity(const f_string_buffer_t *buffer); + +FT_INTERNAL +size_t string_buffer_width_capacity(const f_string_buffer_t *buffer); + +FT_INTERNAL +void *buffer_get_data(f_string_buffer_t *buffer); + +FT_INTERNAL +int buffer_check_align(f_string_buffer_t *buffer); + +FT_INTERNAL +int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, f_conv_context_t *cntx, size_t cod_width, + const char *content_style_tag, const char *reset_content_style_tag); + +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width)); +#endif /* FT_HAVE_UTF8 */ + + +#endif /* STRING_BUFFER_H */ + +/******************************************************** + End of file "string_buffer.h" + ********************************************************/ + + +/******************************************************** + Begin of file "properties.h" + ********************************************************/ + +#ifndef PROPERTIES_H +#define PROPERTIES_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ +#include <stdint.h> +#include <limits.h> + +#define PROP_IS_SET(ft_props, property) ((ft_props) & (property)) +#define PROP_SET(ft_props, property) ((ft_props) |=(property)) +#define PROP_UNSET(ft_props, property) ((ft_props) &= ~((uint32_t)(property))) + +#define TEXT_STYLE_TAG_MAX_SIZE (64 * 2) + +FT_INTERNAL +void get_style_tag_for_cell(const f_table_properties_t *props, + size_t row, size_t col, char *style_tag, size_t sz); + +FT_INTERNAL +void get_reset_style_tag_for_cell(const f_table_properties_t *props, + size_t row, size_t col, char *style_tag, size_t sz); + +FT_INTERNAL +void get_style_tag_for_content(const f_table_properties_t *props, + size_t row, size_t col, char *style_tag, size_t sz); + +FT_INTERNAL +void get_reset_style_tag_for_content(const f_table_properties_t *props, + size_t row, size_t col, char *style_tag, size_t sz); + + +struct f_cell_props { + size_t cell_row; + size_t cell_col; + uint32_t properties_flags; + + unsigned int col_min_width; + enum ft_text_alignment align; + unsigned int cell_padding_top; + unsigned int cell_padding_bottom; + unsigned int cell_padding_left; + unsigned int cell_padding_right; + unsigned int cell_empty_string_height; + enum ft_row_type row_type; + unsigned int content_fg_color_number; + unsigned int content_bg_color_number; + unsigned int cell_bg_color_number; + enum ft_text_style cell_text_style; + enum ft_text_style content_text_style; + bool rgb; +}; + +typedef struct f_cell_props f_cell_props_t; +typedef f_vector_t f_cell_prop_container_t; + +FT_INTERNAL +f_cell_prop_container_t *create_cell_prop_container(void); + +FT_INTERNAL +void destroy_cell_prop_container(f_cell_prop_container_t *cont); + +FT_INTERNAL +const f_cell_props_t *cget_cell_prop(const f_cell_prop_container_t *cont, size_t row, size_t col); + +FT_INTERNAL +f_cell_props_t *get_cell_prop_and_create_if_not_exists(f_cell_prop_container_t *cont, size_t row, size_t col); + +FT_INTERNAL +f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t col, uint32_t property, int value); + +FT_INTERNAL +int get_cell_property_hierarchically(const f_table_properties_t *properties, size_t row, size_t column, uint32_t property); + +FT_INTERNAL +f_status set_default_cell_property(uint32_t property, int value); + + +/* TABLE BORDER DESСRIPTION + * + * + * TL TT TT TT TV TT TT TT TT TT TT TT TR + * LL IV RR + * LL IV RR + * LH IH IH IH II IH IH IH TI IH IH IH RH + * LL IV IV RR + * LL IV IV RR + * LL LI IH IH IH RI RH + * LL IV IV RR + * LL IV IV RR + * LH IH IH IH BI IH IH IH II IH IH IH RH + * LL IV RR + * LL IV RR + * BL BB BB BB BV BB BB BB BV BB BB BB BR + */ + + +/* HORIZONTAL SEPARATOR DESCRIPTION + * + * + * TL TT TT TT TV TT TT TT TV TT TT TT TR <----- TOP_SEPARATOR + * LL IV IV RR + * LH IH IH IH II IH IH IH II IH IH IH RH <----- INSIDE_SEPARATOR + * LL IV IV RR + * BL BB BB BB BV BB BB BB BV BB BB BB BR <----- BOTTOM_SEPARATOR + */ + +enum f_hor_separator_pos { + TOP_SEPARATOR, + INSIDE_SEPARATOR, + BOTTOM_SEPARATOR +}; + +enum f_border_item_pos { + TL_bip = 0, + TT_bip = 1, + TV_bip = 2, + TR_bip = 3, + + LL_bip = 4, + IV_bip = 5, + RR_bip = 6, + + LH_bip = 7, + IH_bip = 8, + II_bip = 9, + RH_bip = 10, + + BL_bip = 11, + BB_bip = 12, + BV_bip = 13, + BR_bip = 14, + + LI_bip = 15, + TI_bip = 16, + RI_bip = 17, + BI_bip = 18, + + BORDER_ITEM_POS_SIZE +}; + + +enum f_separator_item_pos { + LH_sip = 0, + IH_sip = 1, + II_sip = 2, + RH_sip = 3, + + TI_sip = 4, + BI_sip = 5, + + SEPARATOR_ITEM_POS_SIZE +}; + + +struct fort_border_style { + const char *border_chars[BORDER_ITEM_POS_SIZE]; + const char *header_border_chars[BORDER_ITEM_POS_SIZE]; + const char *separator_chars[SEPARATOR_ITEM_POS_SIZE]; +}; +extern struct fort_border_style FORT_BASIC_STYLE; +extern struct fort_border_style FORT_BASIC2_STYLE; +extern struct fort_border_style FORT_SIMPLE_STYLE; +extern struct fort_border_style FORT_PLAIN_STYLE; +extern struct fort_border_style FORT_DOT_STYLE; +extern struct fort_border_style FORT_EMPTY_STYLE; +extern struct fort_border_style FORT_EMPTY2_STYLE; +extern struct fort_border_style FORT_SOLID_STYLE; +extern struct fort_border_style FORT_SOLID_ROUND_STYLE; +extern struct fort_border_style FORT_NICE_STYLE; +extern struct fort_border_style FORT_DOUBLE_STYLE; +extern struct fort_border_style FORT_DOUBLE2_STYLE; +extern struct fort_border_style FORT_BOLD_STYLE; +extern struct fort_border_style FORT_BOLD2_STYLE; +extern struct fort_border_style FORT_FRAME_STYLE; + + +struct fort_entire_table_properties { + unsigned int left_margin; + unsigned int top_margin; + unsigned int right_margin; + unsigned int bottom_margin; + enum ft_adding_strategy add_strategy; +}; +typedef struct fort_entire_table_properties fort_entire_table_properties_t; +extern fort_entire_table_properties_t g_entire_table_properties; + +FT_INTERNAL +f_status set_entire_table_property(f_table_properties_t *table_properties, uint32_t property, int value); + +FT_INTERNAL +f_status set_default_entire_table_property(uint32_t property, int value); + +struct f_table_properties { + struct fort_border_style border_style; + f_cell_prop_container_t *cell_properties; + fort_entire_table_properties_t entire_table_properties; +}; +extern f_table_properties_t g_table_properties; + +FT_INTERNAL +size_t max_border_elem_strlen(struct f_table_properties *); + +FT_INTERNAL +f_table_properties_t *create_table_properties(void); + +FT_INTERNAL +void destroy_table_properties(f_table_properties_t *properties); + +FT_INTERNAL +f_table_properties_t *copy_table_properties(const f_table_properties_t *property); + +#endif /* PROPERTIES_H */ + +/******************************************************** + End of file "properties.h" + ********************************************************/ + + +/******************************************************** + Begin of file "cell.h" + ********************************************************/ + +#ifndef CELL_H +#define CELL_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ + +FT_INTERNAL +f_cell_t *create_cell(void); + +FT_INTERNAL +void destroy_cell(f_cell_t *cell); + +FT_INTERNAL +f_cell_t *copy_cell(f_cell_t *cell); + +FT_INTERNAL +size_t cell_vis_width(const f_cell_t *cell, const f_context_t *context); + +FT_INTERNAL +size_t cell_invis_codes_width(const f_cell_t *cell, const f_context_t *context); + +FT_INTERNAL +size_t hint_height_cell(const f_cell_t *cell, const f_context_t *context); + +FT_INTERNAL +void set_cell_type(f_cell_t *cell, enum f_cell_type type); + +FT_INTERNAL +enum f_cell_type get_cell_type(const f_cell_t *cell); + +FT_INTERNAL +int cell_printf(f_cell_t *cell, size_t row, f_conv_context_t *cntx, size_t cod_width); + +FT_INTERNAL +f_status fill_cell_from_string(f_cell_t *cell, const char *str); + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +f_status fill_cell_from_wstring(f_cell_t *cell, const wchar_t *str); +#endif + +FT_INTERNAL +f_status fill_cell_from_buffer(f_cell_t *cell, const f_string_buffer_t *buf); + +FT_INTERNAL +f_string_buffer_t *cell_get_string_buffer(f_cell_t *cell); + +#endif /* CELL_H */ + +/******************************************************** + End of file "cell.h" + ********************************************************/ + + +/******************************************************** + Begin of file "row.h" + ********************************************************/ + +#ifndef ROW_H +#define ROW_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ +#include "fort.h" +#include <stdarg.h> +/* #include "properties.h" */ /* Commented by amalgamation script */ +#ifdef FT_HAVE_WCHAR +#include <wchar.h> +#endif + +FT_INTERNAL +f_row_t *create_row(void); + +FT_INTERNAL +void destroy_row(f_row_t *row); + +FT_INTERNAL +f_row_t *copy_row(f_row_t *row); + +FT_INTERNAL +f_row_t *split_row(f_row_t *row, size_t pos); + +// Delete range [left; right] of cells (both ends included) +FT_INTERNAL +int ft_row_erase_range(f_row_t *row, size_t left, size_t right); + +FT_INTERNAL +f_row_t *create_row_from_string(const char *str); + +FT_INTERNAL +f_row_t *create_row_from_fmt_string(const struct f_string_view *fmt, va_list *va_args); + +FT_INTERNAL +size_t columns_in_row(const f_row_t *row); + +FT_INTERNAL +f_cell_t *get_cell(f_row_t *row, size_t col); + +FT_INTERNAL +const f_cell_t *get_cell_c(const f_row_t *row, size_t col); + +FT_INTERNAL +f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col); + +FT_INTERNAL +f_cell_t *create_cell_in_position(f_row_t *row, size_t col); + +FT_INTERNAL +f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos); + +FT_INTERNAL +f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos); + +FT_INTERNAL +size_t group_cell_number(const f_row_t *row, size_t master_cell_col); + +FT_INTERNAL +int get_row_cell_types(const f_row_t *row, enum f_cell_type *types, size_t types_sz); + +FT_INTERNAL +f_status row_set_cell_span(f_row_t *row, size_t cell_column, size_t hor_span); + +FT_INTERNAL +int 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); + +FT_INTERNAL +int 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); + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +f_row_t *create_row_from_wstring(const wchar_t *str); +#endif + + +#endif /* ROW_H */ + +/******************************************************** + End of file "row.h" + ********************************************************/ + + +/******************************************************** + Begin of file "table.h" + ********************************************************/ + +#ifndef TABLE_H +#define TABLE_H + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ + +struct ft_table { + f_vector_t *rows; + f_table_properties_t *properties; + f_string_buffer_t *conv_buffer; + size_t cur_row; + size_t cur_col; + f_vector_t *separators; +}; + +FT_INTERNAL +f_separator_t *create_separator(int enabled); + +FT_INTERNAL +void destroy_separator(f_separator_t *sep); + +FT_INTERNAL +f_separator_t *copy_separator(f_separator_t *sep); + +FT_INTERNAL +f_status get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols); + +FT_INTERNAL +f_row_t *get_row(ft_table_t *table, size_t row); + +FT_INTERNAL +const f_row_t *get_row_c(const ft_table_t *table, size_t row); + +FT_INTERNAL +f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row); + +FT_INTERNAL +f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table); + + +FT_INTERNAL +f_status 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); + +FT_INTERNAL +f_status table_geometry(const ft_table_t *table, size_t *height, size_t *width); + +/* + * Returns geometry in codepoints(characters) (include codepoints of invisible + * elements: e.g. styles tags). + */ +FT_INTERNAL +f_status table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width); + +#endif /* TABLE_H */ + +/******************************************************** + End of file "table.h" + ********************************************************/ + + +/******************************************************** + Begin of file "cell.c" + ********************************************************/ + +/* #include "cell.h" */ /* Commented by amalgamation script */ +/* #include "properties.h" */ /* Commented by amalgamation script */ +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +#include <assert.h> + +struct f_cell { + f_string_buffer_t *str_buffer; + enum f_cell_type cell_type; +}; + +FT_INTERNAL +f_cell_t *create_cell(void) +{ + f_cell_t *cell = (f_cell_t *)F_CALLOC(sizeof(f_cell_t), 1); + if (cell == NULL) + return NULL; + cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CHAR_BUF); + if (cell->str_buffer == NULL) { + F_FREE(cell); + return NULL; + } + cell->cell_type = COMMON_CELL; + return cell; +} + +FT_INTERNAL +void destroy_cell(f_cell_t *cell) +{ + if (cell == NULL) + return; + destroy_string_buffer(cell->str_buffer); + F_FREE(cell); +} + +FT_INTERNAL +f_cell_t *copy_cell(f_cell_t *cell) +{ + assert(cell); + + f_cell_t *result = create_cell(); + if (result == NULL) + return NULL; + destroy_string_buffer(result->str_buffer); + result->str_buffer = copy_string_buffer(cell->str_buffer); + if (result->str_buffer == NULL) { + destroy_cell(result); + return NULL; + } + result->cell_type = cell->cell_type; + return result; +} + +FT_INTERNAL +void set_cell_type(f_cell_t *cell, enum f_cell_type type) +{ + assert(cell); + cell->cell_type = type; +} + +FT_INTERNAL +enum f_cell_type get_cell_type(const f_cell_t *cell) +{ + assert(cell); + return cell->cell_type; +} + +FT_INTERNAL +size_t cell_vis_width(const f_cell_t *cell, const f_context_t *context) +{ + /* todo: + * At the moment min width includes paddings. Maybe it is better that min width weren't include + * paddings but be min width of the cell content without padding + */ + + assert(cell); + assert(context); + + f_table_properties_t *properties = context->table_properties; + size_t row = context->row; + size_t column = context->column; + + size_t padding_left = get_cell_property_hierarchically(properties, row, column, FT_CPROP_LEFT_PADDING); + size_t padding_right = get_cell_property_hierarchically(properties, row, column, FT_CPROP_RIGHT_PADDING); + size_t result = padding_left + padding_right; + if (cell->str_buffer && cell->str_buffer->str.data) { + result += buffer_text_visible_width(cell->str_buffer); + } + result = MAX(result, (size_t)get_cell_property_hierarchically(properties, row, column, FT_CPROP_MIN_WIDTH)); + return result; +} + +FT_INTERNAL +size_t cell_invis_codes_width(const f_cell_t *cell, const f_context_t *context) +{ + assert(cell); + assert(context); + + f_table_properties_t *properties = context->table_properties; + size_t row = context->row; + size_t column = context->column; + + size_t result = 0; + char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_style_tag_for_cell(properties, row, column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + result += strlen(cell_style_tag); + + char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_reset_style_tag_for_cell(properties, row, column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + result += strlen(reset_cell_style_tag); + + char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_style_tag_for_content(properties, row, column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + result += strlen(content_style_tag); + + char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_reset_style_tag_for_content(properties, row, column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + result += strlen(reset_content_style_tag); + return result; +} + +FT_INTERNAL +size_t hint_height_cell(const f_cell_t *cell, const f_context_t *context) +{ + assert(cell); + assert(context); + f_table_properties_t *properties = context->table_properties; + size_t row = context->row; + size_t column = context->column; + + size_t padding_top = get_cell_property_hierarchically(properties, row, column, FT_CPROP_TOP_PADDING); + size_t padding_bottom = get_cell_property_hierarchically(properties, row, column, FT_CPROP_BOTTOM_PADDING); + size_t empty_string_height = get_cell_property_hierarchically(properties, row, column, FT_CPROP_EMPTY_STR_HEIGHT); + + size_t result = padding_top + padding_bottom; + if (cell->str_buffer && cell->str_buffer->str.data) { + size_t text_height = buffer_text_visible_height(cell->str_buffer); + result += text_height == 0 ? empty_string_height : text_height; + } + return result; +} + + +FT_INTERNAL +int cell_printf(f_cell_t *cell, size_t row, f_conv_context_t *cntx, size_t vis_width) +{ + const f_context_t *context = cntx->cntx; + size_t buf_len = vis_width; + + if (cell == NULL || (vis_width < cell_vis_width(cell, context))) { + return -1; + } + + f_table_properties_t *properties = context->table_properties; + unsigned int padding_top = get_cell_property_hierarchically(properties, context->row, context->column, FT_CPROP_TOP_PADDING); + unsigned int padding_left = get_cell_property_hierarchically(properties, context->row, context->column, FT_CPROP_LEFT_PADDING); + unsigned int padding_right = get_cell_property_hierarchically(properties, context->row, context->column, FT_CPROP_RIGHT_PADDING); + + size_t written = 0; + size_t invisible_written = 0; + int tmp = 0; + + /* todo: Dirty hack with changing buf_len! need refactoring. */ + /* Also maybe it is better to move all struff with colors to buffers? */ + char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_style_tag_for_cell(properties, context->row, context->column, cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + buf_len += strlen(cell_style_tag); + + char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_reset_style_tag_for_cell(properties, context->row, context->column, reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + buf_len += strlen(reset_cell_style_tag); + + char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_style_tag_for_content(properties, context->row, context->column, content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + buf_len += strlen(content_style_tag); + + char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE]; + get_reset_style_tag_for_content(properties, context->row, context->column, reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE); + buf_len += strlen(reset_content_style_tag); + + /* CELL_STYLE_T LEFT_PADDING CONTENT_STYLE_T CONTENT RESET_CONTENT_STYLE_T RIGHT_PADDING RESET_CELL_STYLE_T + * | | | | | | | | + * L1 R1 + * L2 R2 + * L3 R3 + */ + + size_t L2 = padding_left; + + size_t R2 = padding_right; + size_t R3 = strlen(reset_cell_style_tag); + +#define TOTAL_WRITTEN (written + invisible_written) +#define RIGHT (padding_right + extra_right) + +#define WRITE_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, cell_style_tag)) +#define WRITE_RESET_CELL_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_cell_style_tag)) +#define WRITE_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, content_style_tag)) +#define WRITE_RESET_CONTENT_STYLE_TAG CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag)) + + if (row >= hint_height_cell(cell, context) + || row < padding_top + || row >= (padding_top + buffer_text_visible_height(cell->str_buffer))) { + WRITE_CELL_STYLE_TAG; + WRITE_CONTENT_STYLE_TAG; + WRITE_RESET_CONTENT_STYLE_TAG; + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, buf_len - TOTAL_WRITTEN - R3, FT_SPACE)); + WRITE_RESET_CELL_STYLE_TAG; + + return (int)TOTAL_WRITTEN; + } + + WRITE_CELL_STYLE_TAG; + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, L2, FT_SPACE)); + if (cell->str_buffer) { + 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)); + } else { + WRITE_CONTENT_STYLE_TAG; + WRITE_RESET_CONTENT_STYLE_TAG; + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, vis_width - L2 - R2, FT_SPACE)); + } + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, R2, FT_SPACE)); + WRITE_RESET_CELL_STYLE_TAG; + + return (int)TOTAL_WRITTEN; + +clear: + return -1; +#undef WRITE_CELL_STYLE_TAG +#undef WRITE_RESET_CELL_STYLE_TAG +#undef WRITE_CONTENT_STYLE_TAG +#undef WRITE_RESET_CONTENT_STYLE_TAG +#undef TOTAL_WRITTEN +#undef RIGHT +} + +FT_INTERNAL +f_status fill_cell_from_string(f_cell_t *cell, const char *str) +{ + assert(str); + assert(cell); + + return fill_buffer_from_string(cell->str_buffer, str); +} + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +f_status fill_cell_from_wstring(f_cell_t *cell, const wchar_t *str) +{ + assert(str); + assert(cell); + + return fill_buffer_from_wstring(cell->str_buffer, str); +} +#endif + +#ifdef FT_HAVE_UTF8 +static +f_status fill_cell_from_u8string(f_cell_t *cell, const void *str) +{ + assert(str); + assert(cell); + return fill_buffer_from_u8string(cell->str_buffer, str); +} +#endif /* FT_HAVE_UTF8 */ + +FT_INTERNAL +f_string_buffer_t *cell_get_string_buffer(f_cell_t *cell) +{ + assert(cell); + assert(cell->str_buffer); + return cell->str_buffer; +} + +FT_INTERNAL +f_status fill_cell_from_buffer(f_cell_t *cell, const f_string_buffer_t *buffer) +{ + assert(cell); + assert(buffer); + switch (buffer->type) { + case CHAR_BUF: + return fill_cell_from_string(cell, buffer->str.cstr); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return fill_cell_from_wstring(cell, buffer->str.wstr); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return fill_cell_from_u8string(cell, buffer->str.u8str); +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + return FT_GEN_ERROR; + } + +} + +/******************************************************** + End of file "cell.c" + ********************************************************/ + + +/******************************************************** + Begin of file "fort_impl.c" + ********************************************************/ + +/* +libfort + +MIT License + +Copyright (c) 2017 - 2018 Seleznev Anton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include "fort.h" +#include <assert.h> +#include <string.h> +#include <wchar.h> + +/* #include "vector.h" */ /* Commented by amalgamation script */ +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +/* #include "table.h" */ /* Commented by amalgamation script */ +/* #include "row.h" */ /* Commented by amalgamation script */ +/* #include "properties.h" */ /* Commented by amalgamation script */ + + +ft_table_t *ft_create_table(void) +{ + ft_table_t *result = (ft_table_t *)F_CALLOC(1, sizeof(ft_table_t)); + if (result == NULL) + return NULL; + + result->rows = create_vector(sizeof(f_row_t *), DEFAULT_VECTOR_CAPACITY); + if (result->rows == NULL) { + F_FREE(result); + return NULL; + } + result->separators = create_vector(sizeof(f_separator_t *), DEFAULT_VECTOR_CAPACITY); + if (result->separators == NULL) { + destroy_vector(result->rows); + F_FREE(result); + return NULL; + } + + result->properties = create_table_properties(); + if (result->properties == NULL) { + destroy_vector(result->separators); + destroy_vector(result->rows); + F_FREE(result); + return NULL; + } + result->conv_buffer = NULL; + result->cur_row = 0; + result->cur_col = 0; + return result; +} + + +void ft_destroy_table(ft_table_t *table) +{ + size_t i = 0; + + if (table == NULL) + return; + + if (table->rows) { + size_t row_n = vector_size(table->rows); + for (i = 0; i < row_n; ++i) { + destroy_row(VECTOR_AT(table->rows, i, f_row_t *)); + } + destroy_vector(table->rows); + } + if (table->separators) { + size_t row_n = vector_size(table->separators); + for (i = 0; i < row_n; ++i) { + destroy_separator(VECTOR_AT(table->separators, i, f_separator_t *)); + } + destroy_vector(table->separators); + } + destroy_table_properties(table->properties); + destroy_string_buffer(table->conv_buffer); + F_FREE(table); +} + +ft_table_t *ft_copy_table(ft_table_t *table) +{ + if (table == NULL) + return NULL; + + ft_table_t *result = ft_create_table(); + if (result == NULL) + return NULL; + + size_t i = 0; + size_t rows_n = vector_size(table->rows); + for (i = 0; i < rows_n; ++i) { + f_row_t *row = VECTOR_AT(table->rows, i, f_row_t *); + f_row_t *new_row = copy_row(row); + if (new_row == NULL) { + ft_destroy_table(result); + return NULL; + } + vector_push(result->rows, &new_row); + } + + size_t sep_sz = vector_size(table->separators); + for (i = 0; i < sep_sz; ++i) { + f_separator_t *sep = VECTOR_AT(table->separators, i, f_separator_t *); + f_separator_t *new_sep = copy_separator(sep); + if (new_sep == NULL) { + ft_destroy_table(result); + return NULL; + } + vector_push(result->separators, &new_sep); + } + + /* note: by default new table has allocated default properties, so we + * have to destroy them first. + */ + if (result->properties) { + destroy_table_properties(result->properties); + } + result->properties = copy_table_properties(table->properties); + if (result->properties == NULL) { + ft_destroy_table(result); + return NULL; + } + + /* todo: copy conv_buffer ?? */ + + result->cur_row = table->cur_row; + result->cur_col = table->cur_col; + return result; +} + +static int split_cur_row(ft_table_t *table, f_row_t **tail_of_cur_row) +{ + if (table->cur_row >= vector_size(table->rows)) { + tail_of_cur_row = NULL; + return 0; + } + + f_row_t *row = VECTOR_AT(table->rows, table->cur_row, f_row_t *); + if (table->cur_col >= columns_in_row(row)) { + tail_of_cur_row = NULL; + return 0; + } + + f_row_t *tail = split_row(row, table->cur_col); + if (!tail) { + tail_of_cur_row = NULL; + return FT_GEN_ERROR; + } + + *tail_of_cur_row = tail; + return 0; +} + +int ft_ln(ft_table_t *table) +{ + assert(table); + fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties; + switch (table_props->add_strategy) { + case FT_STRATEGY_INSERT: { + f_row_t *new_row = NULL; + if (FT_IS_ERROR(split_cur_row(table, &new_row))) { + return FT_GEN_ERROR; + } + if (new_row) { + if (FT_IS_ERROR(vector_insert(table->rows, &new_row, table->cur_row + 1))) { + destroy_row(new_row); + return FT_GEN_ERROR; + } + } + break; + } + case FT_STRATEGY_REPLACE: + // do nothing + break; + default: + assert(0 && "Unexpected situation inside libfort"); + break; + } + table->cur_col = 0; + table->cur_row++; + return FT_SUCCESS; +} + +size_t ft_cur_row(const ft_table_t *table) +{ + assert(table); + return table->cur_row; +} + +size_t ft_cur_col(const ft_table_t *table) +{ + assert(table); + return table->cur_col; +} + +void ft_set_cur_cell(ft_table_t *table, size_t row, size_t col) +{ + assert(table); + table->cur_row = row; + table->cur_col = col; +} + +int ft_is_empty(const ft_table_t *table) +{ + assert(table); + return ft_row_count(table) == 0; +} + +size_t ft_row_count(const ft_table_t *table) +{ + assert(table && table->rows); + return vector_size(table->rows); +} + +size_t ft_col_count(const ft_table_t *table) +{ + assert(table && table->rows); + size_t i = 0; + size_t cols_n = 0; + size_t rows_n = vector_size(table->rows); + for (i = 0; i < rows_n; ++i) { + f_row_t *row = VECTOR_AT(table->rows, i, f_row_t *); + size_t ncols = columns_in_row(row); + cols_n = MAX(cols_n, ncols); + } + return cols_n; +} + +int 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) +{ + assert(table && table->rows); + int status = FT_SUCCESS; + + size_t rows_n = vector_size(table->rows); + + if (top_left_row == FT_CUR_ROW) + top_left_row = table->cur_row; + if (bottom_right_row == FT_CUR_ROW) + bottom_right_row = table->cur_row; + + if (top_left_col == FT_CUR_COLUMN) + top_left_col = table->cur_row; + if (bottom_right_col == FT_CUR_COLUMN) + bottom_right_col = table->cur_row; + + if (top_left_row > bottom_right_row || top_left_col > bottom_right_col) + return FT_EINVAL; + + f_row_t *row = NULL; + size_t i = top_left_row; + while (i < rows_n && i <= bottom_right_row) { + row = VECTOR_AT(table->rows, i, f_row_t *); + status = ft_row_erase_range(row, top_left_col, bottom_right_col); + if (FT_IS_ERROR(status)) + return status; + ++i; + } + + f_separator_t *separator = NULL; + + size_t n_iterations = MIN(rows_n - 1, bottom_right_row) - top_left_row + 1; + size_t j = 0; + i = top_left_row; + for (j = 0; j < n_iterations; ++j) { + row = VECTOR_AT(table->rows, i, f_row_t *); + if (columns_in_row(row)) { + ++i; + } else { + destroy_row(row); + status = vector_erase(table->rows, i); + if (FT_IS_ERROR(status)) + return status; + if (i < vector_size(table->separators)) { + separator = VECTOR_AT(table->separators, i, f_separator_t *); + destroy_separator(separator); + vector_erase(table->separators, i); + } + } + } + + return FT_SUCCESS; +} + + +static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct f_string_view *fmt, va_list *va) +{ + size_t i = 0; + size_t new_cols = 0; + + if (table == NULL) + return -1; + + f_row_t *new_row = create_row_from_fmt_string(fmt, va); + + if (new_row == NULL) { + return -1; + } + + f_row_t **cur_row_p = NULL; + size_t sz = vector_size(table->rows); + if (row >= sz) { + size_t push_n = row - sz + 1; + for (i = 0; i < push_n; ++i) { + f_row_t *padding_row = create_row(); + if (padding_row == NULL) + goto clear; + + if (FT_IS_ERROR(vector_push(table->rows, &padding_row))) { + destroy_row(padding_row); + goto clear; + } + } + } + /* todo: clearing pushed items in case of error ?? */ + + new_cols = columns_in_row(new_row); + cur_row_p = &VECTOR_AT(table->rows, row, f_row_t *); + + switch (table->properties->entire_table_properties.add_strategy) { + case FT_STRATEGY_INSERT: { + if (FT_IS_ERROR(insert_row(*cur_row_p, new_row, table->cur_col))) + goto clear; + break; + } + case FT_STRATEGY_REPLACE: { + if (FT_IS_ERROR(swap_row(*cur_row_p, new_row, table->cur_col))) + goto clear; + break; + } + default: + assert(0 && "Unexpected situation inside libfort"); + break; + } + + table->cur_col += new_cols; + destroy_row(new_row); + return (int)new_cols; + +clear: + destroy_row(new_row); + return -1; +} + +#if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) +#define FT_PRINTF ft_printf +#define FT_PRINTF_LN ft_printf_ln +#else +#define FT_PRINTF ft_printf_impl +#define FT_PRINTF_LN ft_printf_ln_impl +#endif + + + +int FT_PRINTF(ft_table_t *table, const char *fmt, ...) +{ + assert(table); + va_list va; + va_start(va, fmt); + + struct f_string_view fmt_str; + fmt_str.type = CHAR_BUF; + fmt_str.u.cstr = fmt; + int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va); + va_end(va); + return result; +} + +int FT_PRINTF_LN(ft_table_t *table, const char *fmt, ...) +{ + assert(table); + va_list va; + va_start(va, fmt); + + struct f_string_view fmt_str; + fmt_str.type = CHAR_BUF; + fmt_str.u.cstr = fmt; + int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va); + if (result >= 0) { + ft_ln(table); + } + va_end(va); + return result; +} + +#undef FT_PRINTF +#undef FT_PRINTF_LN + +#ifdef FT_HAVE_WCHAR +int ft_wprintf(ft_table_t *table, const wchar_t *fmt, ...) +{ + assert(table); + va_list va; + va_start(va, fmt); + + struct f_string_view fmt_str; + fmt_str.type = W_CHAR_BUF; + fmt_str.u.wstr = fmt; + int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va); + va_end(va); + return result; +} + +int ft_wprintf_ln(ft_table_t *table, const wchar_t *fmt, ...) +{ + assert(table); + va_list va; + va_start(va, fmt); + + struct f_string_view fmt_str; + fmt_str.type = W_CHAR_BUF; + fmt_str.u.wstr = fmt; + int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va); + if (result >= 0) { + ft_ln(table); + } + va_end(va); + return result; +} + +#endif + +void ft_set_default_printf_field_separator(char separator) +{ + g_col_separator = separator; +} + +static int ft_write_impl_(ft_table_t *table, const f_string_view_t *cell_content) +{ + assert(table); + f_string_buffer_t *buf = get_cur_str_buffer_and_create_if_not_exists(table); + if (buf == NULL) + return FT_GEN_ERROR; + + int status = FT_SUCCESS; + switch (cell_content->type) { + case CHAR_BUF: + status = fill_buffer_from_string(buf, cell_content->u.cstr); + break; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + status = fill_buffer_from_wstring(buf, cell_content->u.wstr); + break; +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + status = fill_buffer_from_u8string(buf, cell_content->u.u8str); + break; +#endif + default: + status = FT_GEN_ERROR; + } + if (FT_IS_SUCCESS(status)) { + table->cur_col++; + } + return status; +} + +static int ft_write_impl(ft_table_t *table, const char *cell_content) +{ + f_string_view_t content; + content.type = CHAR_BUF; + content.u.cstr = cell_content; + return ft_write_impl_(table, &content); +} + +#ifdef FT_HAVE_UTF8 +static int ft_u8write_impl(ft_table_t *table, const void *cell_content) +{ + f_string_view_t content; + content.type = UTF8_BUF; + content.u.u8str = cell_content; + return ft_write_impl_(table, &content); +} +#endif /* FT_HAVE_UTF8 */ + +#ifdef FT_HAVE_WCHAR +static int ft_wwrite_impl(ft_table_t *table, const wchar_t *cell_content) +{ + f_string_view_t content; + content.type = W_CHAR_BUF; + content.u.wstr = cell_content; + return ft_write_impl_(table, &content); +} +#endif + + +int ft_nwrite(ft_table_t *table, size_t count, const char *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --count; + for (i = 0; i < count; ++i) { + const char *cell = va_arg(va, const char *); + status = ft_write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + return status; +} + +int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --count; + for (i = 0; i < count; ++i) { + const char *cell = va_arg(va, const char *); + status = ft_write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} + + + + +#ifdef FT_HAVE_WCHAR + +int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_wwrite_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const wchar_t *cell = va_arg(va, const wchar_t *); + status = ft_wwrite_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + return status; +} + +int ft_nwwrite_ln(ft_table_t *table, size_t n, const wchar_t *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_wwrite_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const wchar_t *cell = va_arg(va, const wchar_t *); + status = ft_wwrite_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} +#endif + + +int ft_row_write(ft_table_t *table, size_t cols, const char *cells[]) +{ + size_t i = 0; + assert(table); + for (i = 0; i < cols; ++i) { + int status = ft_write_impl(table, cells[i]); + if (FT_IS_ERROR(status)) { + /* todo: maybe current pos in case of error should be equal to the one before function call? */ + return status; + } + } + return FT_SUCCESS; +} + +int ft_row_write_ln(ft_table_t *table, size_t cols, const char *cells[]) +{ + assert(table); + int status = ft_row_write(table, cols, cells); + if (FT_IS_SUCCESS(status)) { + ft_ln(table); + } + return status; +} + +#ifdef FT_HAVE_WCHAR +int ft_row_wwrite(ft_table_t *table, size_t cols, const wchar_t *cells[]) +{ + size_t i = 0; + assert(table); + for (i = 0; i < cols; ++i) { + int status = ft_wwrite_impl(table, cells[i]); + if (FT_IS_ERROR(status)) { + /* todo: maybe current pos in case of error should be equal + * to the one before function call? + */ + return status; + } + } + return FT_SUCCESS; +} + +int ft_row_wwrite_ln(ft_table_t *table, size_t cols, const wchar_t *cells[]) +{ + assert(table); + int status = ft_row_wwrite(table, cols, cells); + if (FT_IS_SUCCESS(status)) { + ft_ln(table); + } + return status; +} +#endif + + + +int ft_table_write(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]) +{ + size_t i = 0; + assert(table); + for (i = 0; i < rows; ++i) { + int status = ft_row_write(table, cols, (const char **)&table_cells[i * cols]); + if (FT_IS_ERROR(status)) { + /* todo: maybe current pos in case of error should be equal + * to the one before function call? + */ + return status; + } + if (i != rows - 1) + ft_ln(table); + } + return FT_SUCCESS; +} + +int ft_table_write_ln(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]) +{ + assert(table); + int status = ft_table_write(table, rows, cols, table_cells); + if (FT_IS_SUCCESS(status)) { + ft_ln(table); + } + return status; +} + + +#ifdef FT_HAVE_WCHAR +int ft_table_wwrite(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]) +{ + size_t i = 0; + assert(table); + for (i = 0; i < rows; ++i) { + int status = ft_row_wwrite(table, cols, (const wchar_t **)&table_cells[i * cols]); + if (FT_IS_ERROR(status)) { + /* todo: maybe current pos in case of error should be equal + * to the one before function call? + */ + return status; + } + if (i != rows - 1) + ft_ln(table); + } + return FT_SUCCESS; +} + +int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]) +{ + assert(table); + int status = ft_table_wwrite(table, rows, cols, table_cells); + if (FT_IS_SUCCESS(status)) { + ft_ln(table); + } + return status; +} +#endif + +static +const char *empty_str_arr[] = {"", (const char *)L"", ""}; + +static +const void *ft_to_string_impl(const ft_table_t *table, enum f_string_type b_type) +{ + assert(table); + + const char *result = NULL; + + /* Determine size of table string representation */ + size_t cod_height = 0; + size_t cod_width = 0; + int status = table_internal_codepoints_geometry(table, &cod_height, &cod_width); + if (FT_IS_ERROR(status)) { + return NULL; + } + size_t n_codepoints = cod_height * cod_width + 1; + + /* Allocate string buffer for string representation */ + if (table->conv_buffer == NULL) { + ((ft_table_t *)table)->conv_buffer = create_string_buffer(n_codepoints, b_type); + if (table->conv_buffer == NULL) + return NULL; + } + while (string_buffer_cod_width_capacity(table->conv_buffer) < n_codepoints) { + if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { + return NULL; + } + } + if (!buffer_check_align(table->conv_buffer)) + return NULL; + char *buffer = (char *)buffer_get_data(table->conv_buffer); + + size_t cols = 0; + size_t rows = 0; + size_t *col_vis_width_arr = NULL; + size_t *row_vis_height_arr = NULL; + status = table_rows_and_cols_geometry(table, &col_vis_width_arr, &cols, &row_vis_height_arr, &rows, VISIBLE_GEOMETRY); + if (FT_IS_ERROR(status)) + return NULL; + + if (rows == 0) { + F_FREE(col_vis_width_arr); + F_FREE(row_vis_height_arr); + return empty_str_arr[b_type]; + } + + int tmp = 0; + size_t i = 0; + f_context_t context; + context.table_properties = (table->properties ? table->properties : &g_table_properties); + f_row_t *prev_row = NULL; + f_row_t *cur_row = NULL; + f_separator_t *cur_sep = NULL; + size_t sep_size = vector_size(table->separators); + + f_conv_context_t cntx; + cntx.u.buf = buffer; + cntx.raw_avail = string_buffer_raw_capacity(table->conv_buffer); + cntx.cntx = &context; + cntx.b_type = b_type; + + /* Print top margin */ + for (i = 0; i < context.table_properties->entire_table_properties.top_margin; ++i) { + FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE)); + FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE)); + } + + for (i = 0; i < rows; ++i) { + cur_sep = (i < sep_size) ? VECTOR_AT(table->separators, i, f_separator_t *) : NULL; + cur_row = VECTOR_AT(table->rows, i, f_row_t *); + enum f_hor_separator_pos separatorPos = (i == 0) ? TOP_SEPARATOR : INSIDE_SEPARATOR; + context.row = i; + FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep)); + FT_CHECK(snprintf_row(cur_row, &cntx, col_vis_width_arr, cols, row_vis_height_arr[i])); + prev_row = cur_row; + } + cur_row = NULL; + cur_sep = (i < sep_size) ? VECTOR_AT(table->separators, i, f_separator_t *) : NULL; + context.row = i; + FT_CHECK(print_row_separator(&cntx, col_vis_width_arr, cols, prev_row, cur_row, BOTTOM_SEPARATOR, cur_sep)); + + /* Print bottom margin */ + for (i = 0; i < context.table_properties->entire_table_properties.bottom_margin; ++i) { + FT_CHECK(print_n_strings(&cntx, cod_width - 1/* minus new_line*/, FT_SPACE)); + FT_CHECK(print_n_strings(&cntx, 1, FT_NEWLINE)); + } + + result = buffer; + +clear: + F_FREE(col_vis_width_arr); + F_FREE(row_vis_height_arr); + return result; +} + +const char *ft_to_string(const ft_table_t *table) +{ + return (const char *)ft_to_string_impl(table, CHAR_BUF); +} + +#ifdef FT_HAVE_WCHAR +const wchar_t *ft_to_wstring(const ft_table_t *table) +{ + return (const wchar_t *)ft_to_string_impl(table, W_CHAR_BUF); +} +#endif + + +int ft_add_separator(ft_table_t *table) +{ + assert(table); + assert(table->separators); + + while (vector_size(table->separators) <= table->cur_row) { + f_separator_t *sep_p = create_separator(F_FALSE); + if (sep_p == NULL) + return FT_MEMORY_ERROR; + int status = vector_push(table->separators, &sep_p); + if (FT_IS_ERROR(status)) + return status; + } + + f_separator_t **sep_p = &VECTOR_AT(table->separators, table->cur_row, f_separator_t *); + if (*sep_p == NULL) + *sep_p = create_separator(F_TRUE); + else + (*sep_p)->enabled = F_TRUE; + + if (*sep_p == NULL) + return FT_GEN_ERROR; + return FT_SUCCESS; +} + +static const struct fort_border_style *built_in_styles[] = { + &FORT_BASIC_STYLE, + &FORT_BASIC2_STYLE, + &FORT_SIMPLE_STYLE, + &FORT_PLAIN_STYLE, + &FORT_DOT_STYLE, + &FORT_EMPTY_STYLE, + &FORT_EMPTY2_STYLE, + &FORT_SOLID_STYLE, + &FORT_SOLID_ROUND_STYLE, + &FORT_NICE_STYLE, + &FORT_DOUBLE_STYLE, + &FORT_DOUBLE2_STYLE, + &FORT_BOLD_STYLE, + &FORT_BOLD2_STYLE, + &FORT_FRAME_STYLE, +}; +#define BUILT_IN_STYLES_SZ (sizeof(built_in_styles) / sizeof(built_in_styles[0])) + +/* todo: remove this stupid and dangerous code */ +static const struct ft_border_style built_in_external_styles[BUILT_IN_STYLES_SZ] = { + { + {"", "", "", "", "", ""}, + {"", "", "", "", "", ""}, + "" + } +}; + +const struct ft_border_style *const FT_BASIC_STYLE = &built_in_external_styles[0]; +const struct ft_border_style *const FT_BASIC2_STYLE = &built_in_external_styles[1]; +const struct ft_border_style *const FT_SIMPLE_STYLE = &built_in_external_styles[2]; +const struct ft_border_style *const FT_PLAIN_STYLE = &built_in_external_styles[3]; +const struct ft_border_style *const FT_DOT_STYLE = &built_in_external_styles[4]; +const struct ft_border_style *const FT_EMPTY_STYLE = &built_in_external_styles[5]; +const struct ft_border_style *const FT_EMPTY2_STYLE = &built_in_external_styles[6]; +const struct ft_border_style *const FT_SOLID_STYLE = &built_in_external_styles[7]; +const struct ft_border_style *const FT_SOLID_ROUND_STYLE = &built_in_external_styles[8]; +const struct ft_border_style *const FT_NICE_STYLE = &built_in_external_styles[9]; +const struct ft_border_style *const FT_DOUBLE_STYLE = &built_in_external_styles[10]; +const struct ft_border_style *const FT_DOUBLE2_STYLE = &built_in_external_styles[11]; +const struct ft_border_style *const FT_BOLD_STYLE = &built_in_external_styles[12]; +const struct ft_border_style *const FT_BOLD2_STYLE = &built_in_external_styles[13]; +const struct ft_border_style *const FT_FRAME_STYLE = &built_in_external_styles[14]; + +static void set_border_props_for_props(f_table_properties_t *properties, const struct ft_border_style *style) +{ + if (style >= built_in_external_styles && style < (built_in_external_styles + BUILT_IN_STYLES_SZ)) { + size_t pos = (size_t)(style - built_in_external_styles); + memcpy(&(properties->border_style), built_in_styles[pos], sizeof(struct fort_border_style)); + return; + } + + const struct ft_border_chars *border_chs = &(style->border_chs); + const struct ft_border_chars *header_border_chs = &(style->header_border_chs); + +#define BOR_CHARS properties->border_style.border_chars +#define H_BOR_CHARS properties->border_style.header_border_chars +#define SEP_CHARS properties->border_style.separator_chars + + BOR_CHARS[TT_bip] = border_chs->top_border_ch; + BOR_CHARS[IH_bip] = border_chs->separator_ch; + BOR_CHARS[BB_bip] = border_chs->bottom_border_ch; + BOR_CHARS[LL_bip] = BOR_CHARS[IV_bip] = BOR_CHARS[RR_bip] = border_chs->side_border_ch; + + BOR_CHARS[TL_bip] = BOR_CHARS[TV_bip] = BOR_CHARS[TR_bip] = border_chs->out_intersect_ch; + BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = border_chs->out_intersect_ch; + BOR_CHARS[BL_bip] = BOR_CHARS[BV_bip] = BOR_CHARS[BR_bip] = border_chs->out_intersect_ch; + BOR_CHARS[II_bip] = border_chs->in_intersect_ch; + + BOR_CHARS[LI_bip] = BOR_CHARS[TI_bip] = BOR_CHARS[RI_bip] = BOR_CHARS[BI_bip] = border_chs->in_intersect_ch; + + if (strlen(border_chs->separator_ch) == 0 && strlen(border_chs->in_intersect_ch) == 0) { + BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = "\0"; + } + + H_BOR_CHARS[TT_bip] = header_border_chs->top_border_ch; + H_BOR_CHARS[IH_bip] = header_border_chs->separator_ch; + H_BOR_CHARS[BB_bip] = header_border_chs->bottom_border_ch; + H_BOR_CHARS[LL_bip] = H_BOR_CHARS[IV_bip] = H_BOR_CHARS[RR_bip] = header_border_chs->side_border_ch; + + H_BOR_CHARS[TL_bip] = H_BOR_CHARS[TV_bip] = H_BOR_CHARS[TR_bip] = header_border_chs->out_intersect_ch; + H_BOR_CHARS[LH_bip] = H_BOR_CHARS[RH_bip] = header_border_chs->out_intersect_ch; + H_BOR_CHARS[BL_bip] = H_BOR_CHARS[BV_bip] = H_BOR_CHARS[BR_bip] = header_border_chs->out_intersect_ch; + H_BOR_CHARS[II_bip] = header_border_chs->in_intersect_ch; + + 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; + + if (strlen(header_border_chs->separator_ch) == 0 && strlen(header_border_chs->in_intersect_ch) == 0) { + BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = "\0"; + } + + SEP_CHARS[LH_sip] = SEP_CHARS[RH_sip] = SEP_CHARS[II_sip] = header_border_chs->out_intersect_ch; + SEP_CHARS[TI_sip] = SEP_CHARS[BI_sip] = header_border_chs->out_intersect_ch; + SEP_CHARS[IH_sip] = style->hor_separator_char; + + +#undef BOR_CHARS +#undef H_BOR_CHARS +#undef SEP_CHARS +} + + +int ft_set_default_border_style(const struct ft_border_style *style) +{ + set_border_props_for_props(&g_table_properties, style); + return FT_SUCCESS; +} + +int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style) +{ + assert(table); + if (table->properties == NULL) { + table->properties = create_table_properties(); + if (table->properties == NULL) + return FT_MEMORY_ERROR; + } + set_border_props_for_props(table->properties, style); + return FT_SUCCESS; +} + + + +int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t property, int value) +{ + assert(table); + + if (table->properties == NULL) { + table->properties = create_table_properties(); + if (table->properties == NULL) + return FT_MEMORY_ERROR; + } + if (table->properties->cell_properties == NULL) { + table->properties->cell_properties = create_cell_prop_container(); + if (table->properties->cell_properties == NULL) { + return FT_GEN_ERROR; + } + } + + if (row == FT_CUR_ROW) + row = table->cur_row; + if (col == FT_CUR_COLUMN) + col = table->cur_col; + + return set_cell_property(table->properties->cell_properties, row, col, property, value); +} + +int ft_set_default_cell_prop(uint32_t property, int value) +{ + return set_default_cell_property(property, value); +} + + +int ft_set_default_tbl_prop(uint32_t property, int value) +{ + return set_default_entire_table_property(property, value); +} + +int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value) +{ + assert(table); + + if (table->properties == NULL) { + table->properties = create_table_properties(); + if (table->properties == NULL) + return FT_MEMORY_ERROR; + } + return set_entire_table_property(table->properties, property, value); +} + +void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)) +{ + set_memory_funcs(f_malloc, f_free); +} + +const char *ft_strerror(int error_code) +{ + switch (error_code) { + case FT_MEMORY_ERROR: + return "Out of memory"; + case FT_GEN_ERROR: + return "General error"; + case FT_EINVAL: + return "Invalid argument"; + case FT_INTERN_ERROR: + return "Internal libfort error"; + default: + if (error_code < 0) + return "Unknown error code"; + else + return "Success"; + } +} + +int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span) +{ + assert(table); + if (hor_span < 2) + return FT_EINVAL; + + if (row == FT_CUR_ROW) + row = table->cur_row; + if (row == FT_CUR_COLUMN) + col = table->cur_col; + + f_row_t *row_p = get_row_and_create_if_not_exists(table, row); + if (row_p == NULL) + return FT_GEN_ERROR; + + return row_set_cell_span(row_p, col, hor_span); +} + +#ifdef FT_HAVE_UTF8 + +int ft_u8nwrite(ft_table_t *table, size_t n, const void *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_u8write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const void *cell = va_arg(va, const void *); + status = ft_u8write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + return status; +} + +int ft_u8nwrite_ln(ft_table_t *table, size_t n, const void *cell_content, ...) +{ + size_t i = 0; + assert(table); + int status = ft_u8write_impl(table, cell_content); + if (FT_IS_ERROR(status)) + return status; + + va_list va; + va_start(va, cell_content); + --n; + for (i = 0; i < n; ++i) { + const void *cell = va_arg(va, const void *); + status = ft_u8write_impl(table, cell); + if (FT_IS_ERROR(status)) { + va_end(va); + return status; + } + } + va_end(va); + + ft_ln(table); + return status; +} + +FT_PRINTF_ATTRIBUTE_FORMAT(2, 3) +int ft_u8printf(ft_table_t *table, const char *fmt, ...) +{ + assert(table); + va_list va; + va_start(va, fmt); + + struct f_string_view fmt_str; + fmt_str.type = UTF8_BUF; + fmt_str.u.cstr = fmt; + int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va); + va_end(va); + return result; +} + +FT_PRINTF_ATTRIBUTE_FORMAT(2, 3) +int ft_u8printf_ln(ft_table_t *table, const char *fmt, ...) +{ + assert(table); + va_list va; + va_start(va, fmt); + + struct f_string_view fmt_str; + fmt_str.type = UTF8_BUF; + fmt_str.u.cstr = fmt; + int result = ft_row_printf_impl_(table, table->cur_row, &fmt_str, &va); + if (result >= 0) { + ft_ln(table); + } + va_end(va); + return result; +} + +const void *ft_to_u8string(const ft_table_t *table) +{ + return (const void *)ft_to_string_impl(table, UTF8_BUF); +} + +void ft_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width)) +{ + buffer_set_u8strwid_func(u8strwid); +} + +#endif /* FT_HAVE_UTF8 */ + +/******************************************************** + End of file "fort_impl.c" + ********************************************************/ + + +/******************************************************** + Begin of file "fort_utils.c" + ********************************************************/ + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ +#ifdef FT_HAVE_WCHAR +#include <wchar.h> +#endif +#if defined(FT_HAVE_UTF8) +/* #include "utf8.h" */ /* Commented by amalgamation script */ +#endif +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ + + +char g_col_separator = FORT_DEFAULT_COL_SEPARATOR; + +/***************************************************************************** + * LIBFORT helpers + *****************************************************************************/ + +#if defined(FT_GCC_COMPILER) || defined(FT_CLANG_COMPILER) +void *(*fort_malloc)(size_t size) = &malloc; +void (*fort_free)(void *ptr) = &free; +void *(*fort_calloc)(size_t nmemb, size_t size) = &calloc; +void *(*fort_realloc)(void *ptr, size_t size) = &realloc; +#else +static void *local_malloc(size_t size) +{ + return malloc(size); +} + +static void local_free(void *ptr) +{ + free(ptr); +} + +static void *local_calloc(size_t nmemb, size_t size) +{ + return calloc(nmemb, size); +} + +static void *local_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void *(*fort_malloc)(size_t size) = &local_malloc; +void (*fort_free)(void *ptr) = &local_free; +void *(*fort_calloc)(size_t nmemb, size_t size) = &local_calloc; +void *(*fort_realloc)(void *ptr, size_t size) = &local_realloc; +#endif + +static void *custom_fort_calloc(size_t nmemb, size_t size) +{ + size_t total_size = nmemb * size; + void *result = F_MALLOC(total_size); + if (result != NULL) + memset(result, 0, total_size); + return result; +} + +static void *custom_fort_realloc(void *ptr, size_t size) +{ + if (ptr == NULL) + return F_MALLOC(size); + if (size == 0) { + F_FREE(ptr); + return NULL; + } + + void *new_chunk = F_MALLOC(size); + if (new_chunk == NULL) + return NULL; + + /* + * In theory we should copy MIN(size, size allocated for ptr) bytes, + * but this is rather dummy implementation so we don't care about it + */ + memcpy(new_chunk, ptr, size); + F_FREE(ptr); + return new_chunk; +} + + +FT_INTERNAL +void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)) +{ + assert((f_malloc == NULL && f_free == NULL) /* Use std functions */ + || (f_malloc != NULL && f_free != NULL) /* Use custom functions */); + + if (f_malloc == NULL && f_free == NULL) { +#if defined(FT_GCC_COMPILER) || defined(FT_CLANG_COMPILER) + fort_malloc = &malloc; + fort_free = &free; + fort_calloc = &calloc; + fort_realloc = &realloc; +#else + fort_malloc = &local_malloc; + fort_free = &local_free; + fort_calloc = &local_calloc; + fort_realloc = &local_realloc; +#endif + } else { + fort_malloc = f_malloc; + fort_free = f_free; + fort_calloc = &custom_fort_calloc; + fort_realloc = &custom_fort_realloc; + } + +} + +FT_INTERNAL +char *fort_strdup(const char *str) +{ + if (str == NULL) + return NULL; + + size_t sz = strlen(str); + char *str_copy = (char *)F_MALLOC((sz + 1) * sizeof(char)); + if (str_copy == NULL) + return NULL; + + strcpy(str_copy, str); + return str_copy; +} + +#if defined(FT_HAVE_WCHAR) +FT_INTERNAL +wchar_t *fort_wcsdup(const wchar_t *str) +{ + if (str == NULL) + return NULL; + + size_t sz = wcslen(str); + wchar_t *str_copy = (wchar_t *)F_MALLOC((sz + 1) * sizeof(wchar_t)); + if (str_copy == NULL) + return NULL; + + wcscpy(str_copy, str); + return str_copy; +} +#endif + + +static +size_t columns_number_in_fmt_string(const char *fmt) +{ + size_t separator_counter = 0; + const char *pos = fmt; + while (1) { + pos = strchr(pos, g_col_separator); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} + +#if defined(FT_HAVE_WCHAR) +static +size_t columns_number_in_fmt_wstring(const wchar_t *fmt) +{ + size_t separator_counter = 0; + const wchar_t *pos = fmt; + while (1) { + pos = wcschr(pos, g_col_separator); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} +#endif + +#if defined(FT_HAVE_UTF8) +static +size_t columns_number_in_fmt_u8string(const void *fmt) +{ + size_t separator_counter = 0; + const char *pos = (const char *)fmt; + while (1) { + pos = (const char *)utf8chr(pos, g_col_separator); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} +#endif + +FT_INTERNAL +size_t number_of_columns_in_format_string(const f_string_view_t *fmt) +{ + switch (fmt->type) { + case CHAR_BUF: + return columns_number_in_fmt_string(fmt->u.cstr); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return columns_number_in_fmt_wstring(fmt->u.wstr); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return columns_number_in_fmt_u8string(fmt->u.u8str); +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + } + return 0; +} + +FT_INTERNAL +size_t number_of_columns_in_format_buffer(const f_string_buffer_t *fmt) +{ + switch (fmt->type) { + case CHAR_BUF: + return columns_number_in_fmt_string(fmt->str.cstr); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return columns_number_in_fmt_wstring(fmt->str.wstr); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return columns_number_in_fmt_u8string(fmt->str.u8str); +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + } + return 0; +} + +static +int snprint_n_strings_impl(char *buf, size_t length, size_t n, const char *str) +{ + size_t str_len = strlen(str); + if (length <= n * str_len) + return -1; + + if (n == 0) + return 0; + + /* To ensure valid return value it is safely not print such big strings */ + if (n * str_len > INT_MAX) + return -1; + + if (str_len == 0) + return 0; + + int status = snprintf(buf, length, "%0*d", (int)(n * str_len), 0); + if (status < 0) + return status; + + size_t i = 0; + for (i = 0; i < n; ++i) { + const char *str_p = str; + while (*str_p) + *(buf++) = *(str_p++); + } + return (int)(n * str_len); +} + +static +int snprint_n_strings(f_conv_context_t *cntx, size_t n, const char *str) +{ + int w = snprint_n_strings_impl(cntx->u.buf, cntx->raw_avail, n, str); + if (w >= 0) { + cntx->u.buf += w; + cntx->raw_avail -= w; + } + return w; +} + +#if defined(FT_HAVE_WCHAR) +static +int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); +#endif + +#if defined(FT_HAVE_UTF8) +static +int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str); +#endif + + +FT_INTERNAL +int print_n_strings(f_conv_context_t *cntx, size_t n, const char *str) +{ + int cod_w; + int raw_written; + + switch (cntx->b_type) { + case CHAR_BUF: + raw_written = snprint_n_strings(cntx, n, str); + cod_w = raw_written; + return cod_w; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + cod_w = wsnprint_n_string(cntx->u.wbuf, cntx->raw_avail, n, str); + if (cod_w < 0) + return cod_w; + raw_written = sizeof(wchar_t) * cod_w; + + cntx->u.buf += raw_written; + cntx->raw_avail -= raw_written; + return cod_w; +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + /* Everying is very strange and differs with W_CHAR_BUF */ + raw_written = u8nprint_n_strings(cntx->u.buf, cntx->raw_avail, n, str); + if (raw_written < 0) { + fprintf(stderr, " raw_written = %d\n", raw_written); + return raw_written; + } + + cntx->u.buf += raw_written; + cntx->raw_avail -= raw_written; + return utf8len(str) * n; +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + return -1; + } +} + +FT_INTERNAL +int ft_nprint(f_conv_context_t *cntx, const char *str, size_t strlen) +{ + if (cntx->raw_avail + 1/* for 0 */ < strlen) + return -1; + + memcpy(cntx->u.buf, str, strlen); + cntx->u.buf += strlen; + cntx->raw_avail -= strlen; + *cntx->u.buf = '\0'; /* Do we need this ? */ + return strlen; +} + +#ifdef FT_HAVE_WCHAR +int ft_nwprint(f_conv_context_t *cntx, const wchar_t *str, size_t strlen) +{ + if (cntx->raw_avail + 1/* for 0 */ < strlen) + return -1; + + size_t raw_len = strlen * sizeof(wchar_t); + + memcpy(cntx->u.buf, str, raw_len); + cntx->u.buf += raw_len; + cntx->raw_avail -= raw_len; + + /* Do we need this ? */ + wchar_t end_of_string = L'\0'; + memcpy(cntx->u.buf, &end_of_string, sizeof(wchar_t)); + return strlen; +} +#endif /* FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +int ft_nu8print(f_conv_context_t *cntx, const void *beg, const void *end) +{ + const char *bc = (const char *)beg; + const char *ec = (const char *)end; + size_t raw_len = ec - bc; + if (cntx->raw_avail + 1 < raw_len) + return -1; + + memcpy(cntx->u.buf, beg, raw_len); + cntx->u.buf += raw_len; + cntx->raw_avail -= raw_len; + *(cntx->u.buf) = '\0'; /* Do we need this ? */ + return raw_len; /* what return here ? */ +} +#endif /* FT_HAVE_UTF8 */ + +#if defined(FT_HAVE_WCHAR) +#define WCS_SIZE 64 + +static +int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) +{ + size_t str_len = strlen(str); + + /* note: maybe it's, better to return -1 in case of multibyte character + * strings (not sure this case is done correctly). + */ + if (str_len > 1) { + const unsigned char *p = (const unsigned char *)str; + while (*p) { + if (*p <= 127) + p++; + else { + wchar_t wcs[WCS_SIZE]; + const char *ptr = str; + size_t wcs_len; + mbstate_t mbst; + memset(&mbst, 0, sizeof(mbst)); + wcs_len = mbsrtowcs(wcs, (const char **)&ptr, WCS_SIZE, &mbst); + /* for simplicity */ + if ((wcs_len == (size_t) - 1) || wcs_len > 1) { + return -1; + } else { + wcs[wcs_len] = L'\0'; + size_t k = n; + while (k) { + *buf = *wcs; + ++buf; + --k; + } + buf[n] = L'\0'; + return (int)n; + } + } + } + } + + if (length <= n * str_len) + return -1; + + if (n == 0) + return 0; + + /* To ensure valid return value it is safely not print such big strings */ + if (n * str_len > INT_MAX) + return -1; + + if (str_len == 0) + return 0; + + int status = swprintf(buf, length, L"%0*d", (int)(n * str_len), 0); + if (status < 0) + return status; + + size_t i = 0; + for (i = 0; i < n; ++i) { + const char *str_p = str; + while (*str_p) + *(buf++) = (wchar_t) * (str_p++); + } + return (int)(n * str_len); +} +#endif + + +#if defined(FT_HAVE_UTF8) +static +int u8nprint_n_strings(void *buf, size_t length, size_t n, const void *str) +{ + size_t str_size = utf8size(str) - 1; /* str_size - raw size in bytes, excluding \0 */ + if (length <= n * str_size) + return -1; + + if (n == 0) + return 0; + + /* To ensure valid return value it is safely not print such big strings */ + if (n * str_size > INT_MAX) + return -1; + + if (str_size == 0) + return 0; + + size_t i = n; + while (i) { + memcpy(buf, str, str_size); + buf = (char *)buf + str_size; + --i; + } + *(char *)buf = '\0'; + return (int)(n * str_size); +} +#endif + +/******************************************************** + End of file "fort_utils.c" + ********************************************************/ + + +/******************************************************** + Begin of file "properties.c" + ********************************************************/ + +/* #include "fort_utils.h" */ /* Commented by amalgamation script */ +#include <assert.h> +/* #include "properties.h" */ /* Commented by amalgamation script */ +/* #include "vector.h" */ /* Commented by amalgamation script */ + +#define FT_RESET_COLOR "\033[0m" + +static const char *fg_colors[] = { + "", + "\033[30m", + "\033[31m", + "\033[32m", + "\033[33m", + "\033[34m", + "\033[35m", + "\033[36m", + "\033[37m", + "\033[90m", + "\033[91m", + "\033[92m", + "\033[93m", + "\033[94m", + "\033[95m", + "\033[96m", + "\033[97m", +}; + +static const char *bg_colors[] = { + "", + "\033[40m", + "\033[41m", + "\033[42m", + "\033[43m", + "\033[44m", + "\033[45m", + "\033[46m", + "\033[47m", + "\033[100m", + "\033[101m", + "\033[102m", + "\033[103m", + "\033[104m", + "\033[105m", + "\033[106m", + "\033[107m", +}; + +static const char *text_styles[] = { + "", + "\033[1m", + "\033[2m", + "\033[3m", + "\033[4m", + "\033[5m", + "\033[7m", + "\033[8m", +}; + +#define UNIVERSAL_RESET_TAG "\033[0m" + +static const size_t n_fg_colors = sizeof(fg_colors) / sizeof(fg_colors[0]); +static const size_t n_bg_colors = sizeof(bg_colors) / sizeof(bg_colors[0]); +static const size_t n_styles = sizeof(text_styles) / sizeof(text_styles[0]); + +void get_style_tag_for_cell(const f_table_properties_t *props, + size_t row, size_t col, char *style_tag, size_t sz) +{ + (void)sz; + size_t i = 0; + + unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_BG_COLOR); + unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_TEXT_STYLE); + + style_tag[0] = '\0'; + + if (text_style < (1U << n_styles)) { + for (i = 0; i < n_styles; ++i) { + if (text_style & (1 << i)) { + strcat(style_tag, text_styles[i]); + } + } + } else { + goto error; + } + + if (get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_BG_RGBCOLOR)) { + char b[20]; + + #define BGTERMRGB "\x1b[48;2;" + snprintf(b, sizeof(b), BGTERMRGB "%u;%u;%um", bg_color_number>>16, (bg_color_number&0xFF00)>>8, bg_color_number&0xFF); + strcat(style_tag, b); + } + else if (bg_color_number < n_bg_colors) { + strcat(style_tag, bg_colors[bg_color_number]); + } else { + goto error; + } + + return; + +error: + /* shouldn't be here */ + assert(0); + style_tag[0] = '\0'; + return; +} + +void 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) +{ + (void)sz; + size_t i = 0; + + unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_BG_COLOR); + unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CELL_TEXT_STYLE); + + reset_style_tag[0] = '\0'; + + if (text_style < (1U << n_styles)) { + for (i = 0; i < n_styles; ++i) { + if (text_style & (1 << i)) { + if (i != 0) // FT_TSTYLE_DEFAULT + goto reset_style; + } + } + } else { + goto error; + } + +reset_style: + strcat(reset_style_tag, UNIVERSAL_RESET_TAG); + return; + +error: + /* shouldn't be here */ + assert(0); + reset_style_tag[0] = '\0'; + return; +} + + +void get_style_tag_for_content(const f_table_properties_t *props, + size_t row, size_t col, char *style_tag, size_t sz) +{ + (void)sz; + size_t i = 0; + + unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_TEXT_STYLE); + unsigned fg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_FG_COLOR); + unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_BG_COLOR); + + style_tag[0] = '\0'; + + if (text_style < (1U << n_styles)) { + for (i = 0; i < n_styles; ++i) { + if (text_style & (1 << i)) { + strcat(style_tag, text_styles[i]); + } + } + } else { + goto error; + } + + if (fg_color_number < n_fg_colors) { + if (fg_color_number) + strcat(style_tag, fg_colors[fg_color_number]); + } else { + goto error; + } + + if (bg_color_number < n_bg_colors) { + strcat(style_tag, bg_colors[bg_color_number]); + } else { + goto error; + } + + return; + +error: + /* shouldn't be here */ + assert(0); + style_tag[0] = '\0'; + return; +} + +void 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) +{ + (void)sz; + size_t i = 0; + size_t len = 0; + + unsigned text_style = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_TEXT_STYLE); + unsigned fg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_FG_COLOR); + unsigned bg_color_number = get_cell_property_hierarchically(props, row, col, FT_CPROP_CONT_BG_COLOR); + + reset_style_tag[0] = '\0'; + + if (text_style < (1U << n_styles)) { + for (i = 0; i < n_styles; ++i) { + if (text_style & (1 << i)) { + if (i != 0) // FT_TSTYLE_DEFAULT + goto reset_style; + } + } + } else { + goto error; + } + + if (fg_color_number < n_fg_colors) { + if (fg_color_number) + goto reset_style; + } else { + goto error; + } + + if (bg_color_number < n_bg_colors) { + if (bg_color_number) + goto reset_style; + } else { + goto error; + } + + return; + + +reset_style: + strcat(reset_style_tag, UNIVERSAL_RESET_TAG); + len = strlen(reset_style_tag); + get_style_tag_for_cell(props, row, col, reset_style_tag + len, sz - len); + return; + +error: + /* shouldn't be here */ + assert(0); + reset_style_tag[0] = '\0'; + return; +} + + +static struct f_cell_props g_default_cell_properties = { + FT_ANY_ROW, /* cell_row */ + FT_ANY_COLUMN, /* cell_col */ + + /* properties_flags */ + FT_CPROP_MIN_WIDTH | FT_CPROP_TEXT_ALIGN | FT_CPROP_TOP_PADDING + | FT_CPROP_BOTTOM_PADDING | FT_CPROP_LEFT_PADDING | FT_CPROP_RIGHT_PADDING + | FT_CPROP_EMPTY_STR_HEIGHT | FT_CPROP_CONT_FG_COLOR | FT_CPROP_CELL_BG_COLOR + | FT_CPROP_CONT_BG_COLOR | FT_CPROP_CELL_TEXT_STYLE | FT_CPROP_CONT_TEXT_STYLE, + + 0, /* col_min_width */ + FT_ALIGNED_LEFT, /* align */ + 0, /* cell_padding_top */ + 0, /* cell_padding_bottom */ + 1, /* cell_padding_left */ + 1, /* cell_padding_right */ + 1, /* cell_empty_string_height */ + + FT_ROW_COMMON, /* row_type */ + FT_COLOR_DEFAULT, /* content_fg_color_number */ + FT_COLOR_DEFAULT, /* content_bg_color_number */ + FT_COLOR_DEFAULT, /* cell_bg_color_number */ + FT_TSTYLE_DEFAULT, /* cell_text_style */ + FT_TSTYLE_DEFAULT, /* content_text_style */ + false, +}; + +static int get_prop_value_if_exists_otherwise_default(const struct f_cell_props *cell_opts, uint32_t property) +{ + if (cell_opts == NULL || !PROP_IS_SET(cell_opts->properties_flags, property)) { + cell_opts = &g_default_cell_properties; + } + + switch (property) { + case FT_CPROP_MIN_WIDTH: + return cell_opts->col_min_width; + case FT_CPROP_TEXT_ALIGN: + return cell_opts->align; + case FT_CPROP_TOP_PADDING: + return cell_opts->cell_padding_top; + case FT_CPROP_BOTTOM_PADDING: + return cell_opts->cell_padding_bottom; + case FT_CPROP_LEFT_PADDING: + return cell_opts->cell_padding_left; + case FT_CPROP_RIGHT_PADDING: + return cell_opts->cell_padding_right; + case FT_CPROP_EMPTY_STR_HEIGHT: + return cell_opts->cell_empty_string_height; + case FT_CPROP_ROW_TYPE: + return cell_opts->row_type; + case FT_CPROP_CONT_FG_COLOR: + return cell_opts->content_fg_color_number; + case FT_CPROP_CONT_BG_COLOR: + return cell_opts->content_bg_color_number; + case FT_CPROP_CELL_BG_COLOR: + return cell_opts->cell_bg_color_number; + case FT_CPROP_CELL_BG_RGBCOLOR: + return cell_opts->rgb; + case FT_CPROP_CELL_TEXT_STYLE: + return cell_opts->cell_text_style; + case FT_CPROP_CONT_TEXT_STYLE: + return cell_opts->content_text_style; + default: + /* todo: implement later */ + exit(333); + } +} + + +FT_INTERNAL +f_cell_prop_container_t *create_cell_prop_container(void) +{ + f_cell_prop_container_t *ret = create_vector(sizeof(f_cell_props_t), DEFAULT_VECTOR_CAPACITY); + return ret; +} + + +FT_INTERNAL +void destroy_cell_prop_container(f_cell_prop_container_t *cont) +{ + if (cont) + destroy_vector(cont); +} + + +FT_INTERNAL +const f_cell_props_t *cget_cell_prop(const f_cell_prop_container_t *cont, size_t row, size_t col) +{ + assert(cont); + size_t sz = vector_size(cont); + size_t i = 0; + for (i = 0; i < sz; ++i) { + const f_cell_props_t *opt = &VECTOR_AT_C(cont, i, const f_cell_props_t); + if (opt->cell_row == row && opt->cell_col == col) + return opt; + } + return NULL; +} + + +FT_INTERNAL +f_cell_props_t *get_cell_prop_and_create_if_not_exists(f_cell_prop_container_t *cont, size_t row, size_t col) +{ + assert(cont); + size_t sz = vector_size(cont); + size_t i = 0; + for (i = 0; i < sz; ++i) { + f_cell_props_t *opt = &VECTOR_AT(cont, i, f_cell_props_t); + if (opt->cell_row == row && opt->cell_col == col) + return opt; + } + + f_cell_props_t opt; + if (row == FT_ANY_ROW && col == FT_ANY_COLUMN) + memcpy(&opt, &g_default_cell_properties, sizeof(f_cell_props_t)); + else + memset(&opt, 0, sizeof(f_cell_props_t)); + + opt.cell_row = row; + opt.cell_col = col; + if (FT_IS_SUCCESS(vector_push(cont, &opt))) { + return &VECTOR_AT(cont, sz, f_cell_props_t); + } + + return NULL; +} + + +FT_INTERNAL +int get_cell_property_hierarchically(const f_table_properties_t *propertiess, size_t row, size_t column, uint32_t property) +{ + assert(propertiess); + size_t row_origin = row; + + const f_cell_props_t *opt = NULL; + if (propertiess->cell_properties != NULL) { + while (1) { + opt = cget_cell_prop(propertiess->cell_properties, row, column); + if (opt != NULL && PROP_IS_SET(opt->properties_flags, property)) + break; + + if (row != FT_ANY_ROW && column != FT_ANY_COLUMN) { + row = FT_ANY_ROW; + continue; + } else if (row == FT_ANY_ROW && column != FT_ANY_COLUMN) { + row = row_origin; + column = FT_ANY_COLUMN; + continue; + } else if (row != FT_ANY_ROW && column == FT_ANY_COLUMN) { + row = FT_ANY_ROW; + column = FT_ANY_COLUMN; + continue; + } + + opt = NULL; + break; + } + } + + return get_prop_value_if_exists_otherwise_default(opt, property); +} + + +static f_status set_cell_property_impl(f_cell_props_t *opt, uint32_t property, int value) +{ + assert(opt); + + PROP_SET(opt->properties_flags, property); + if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) { + CHECK_NOT_NEGATIVE(value); + opt->col_min_width = value; + } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) { + opt->align = (enum ft_text_alignment)value; + } else if (PROP_IS_SET(property, FT_CPROP_TOP_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_top = value; + } else if (PROP_IS_SET(property, FT_CPROP_BOTTOM_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_bottom = value; + } else if (PROP_IS_SET(property, FT_CPROP_LEFT_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_left = value; + } else if (PROP_IS_SET(property, FT_CPROP_RIGHT_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_right = value; + } else if (PROP_IS_SET(property, FT_CPROP_EMPTY_STR_HEIGHT)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_empty_string_height = value; + } else if (PROP_IS_SET(property, FT_CPROP_ROW_TYPE)) { + opt->row_type = (enum ft_row_type)value; + } else if (PROP_IS_SET(property, FT_CPROP_CONT_FG_COLOR)) { + opt->content_fg_color_number = value; + } else if (PROP_IS_SET(property, FT_CPROP_CONT_BG_COLOR)) { + opt->content_bg_color_number = value; + } else if (PROP_IS_SET(property, FT_CPROP_CELL_BG_COLOR)) { + opt->cell_bg_color_number = value; + } else if (PROP_IS_SET(property, FT_CPROP_CELL_BG_RGBCOLOR)) { + opt->cell_bg_color_number = value; + opt->rgb = true; + } else if (PROP_IS_SET(property, FT_CPROP_CELL_TEXT_STYLE)) { + enum ft_text_style v = (enum ft_text_style)value; + if (v == FT_TSTYLE_DEFAULT) { + opt->cell_text_style = FT_TSTYLE_DEFAULT; + } else { + opt->cell_text_style = (enum ft_text_style)(opt->cell_text_style | v); + } + } else if (PROP_IS_SET(property, FT_CPROP_CONT_TEXT_STYLE)) { + enum ft_text_style v = (enum ft_text_style)value; + if (v == FT_TSTYLE_DEFAULT) { + opt->content_text_style = v; + } else { + opt->content_text_style = (enum ft_text_style)(opt->content_text_style | v); + } + } + + return FT_SUCCESS; + +fort_fail: + return FT_EINVAL; +} + + +FT_INTERNAL +f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t col, uint32_t property, int value) +{ + f_cell_props_t *opt = get_cell_prop_and_create_if_not_exists(cont, row, col); + if (opt == NULL) + return FT_GEN_ERROR; + + return set_cell_property_impl(opt, property, value); + /* + PROP_SET(opt->propertiess, property); + if (PROP_IS_SET(property, FT_CPROP_MIN_WIDTH)) { + opt->col_min_width = value; + } else if (PROP_IS_SET(property, FT_CPROP_TEXT_ALIGN)) { + opt->align = value; + } + + return FT_SUCCESS; + */ +} + + +FT_INTERNAL +f_status set_default_cell_property(uint32_t property, int value) +{ + return set_cell_property_impl(&g_default_cell_properties, property, value); +} + + +#define BASIC_STYLE { \ + /* border_chars */ \ + { \ + "+", "-", "+", "+", \ + "|", "|", "|", \ + "\0", "\0", "\0", "\0", \ + "+", "-", "+", "+", \ + "+", "+", "+", "+", \ + }, \ + /* header_border_chars */ \ + { \ + "+", "-", "+", "+", \ + "|", "|", "|", \ + "+", "-", "+", "+", \ + "+", "-", "+", "+", \ + "+", "+", "+", "+", \ + }, \ + /* separator_chars */ \ + { \ + "+", "-", "+", "+", \ + "+", "+", \ + }, \ +} + +#define BASIC2_STYLE { \ + /* border_chars */ \ + { \ + "+", "-", "+", "+", \ + "|", "|", "|", \ + "+", "-", "+", "+", \ + "+", "-", "+", "+", \ + "+", "+", "+", "+", \ + }, \ + /* header_border_chars */ \ + { \ + "+", "-", "+", "+", \ + "|", "|", "|", \ + "+", "-", "+", "+", \ + "+", "-", "+", "+", \ + "+", "+", "+", "+", \ + }, \ + /* separator_chars */ \ + { \ + "+", "-", "+", "+", \ + "+", "+", \ + }, \ +} + +#define SIMPLE_STYLE { \ + /* border_chars */ \ + { \ + "\0", "\0", "\0", "\0", \ + "\0", " ", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + }, \ + /* header_border_chars */ \ + { \ + "\0", "\0", "\0", "\0", \ + "\0", " ", "\0", \ + "\0", "─", " ", "\0", \ + "\0", " ", " ", "\0", \ + " ", "─", " ", "─", \ + }, \ + /* separator_chars */ \ + { \ + "\0", "─", " ", "\0", \ + " ", " ", \ + }, \ +} + +#define PLAIN_STYLE { \ + /* border_chars */ \ + { \ + "\0", "\0", "\0", "\0", \ + "\0", " ", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + }, \ + /* header_border_chars */ \ + { \ + "\0", "-", "-", "\0", \ + "\0", " ", "\0", \ + "\0", "-", "-", "\0", \ + "\0", "-", "-", "\0", \ + " ", "-", " ", "-", \ + }, \ + /* separator_chars */ \ + { \ + "\0", "-", "-", "\0", \ + "-", "-", \ + }, \ +} + +#define DOT_STYLE { \ + /* border_chars */ \ + { \ + ".", ".", ".", ".", \ + ":", ":", ":", \ + "\0", "\0", "\0", "\0", \ + ":", ".", ":", ":", \ + "+", ":", "+", ":", \ + }, \ + /* header_border_chars */ \ + { \ + ".", ".", ".", ".", \ + ":", ":", ":", \ + ":", ".", ":", ":", \ + ":", ".", ":", ":", \ + "+", ".", "+", ".", \ + }, \ + /* separator_chars */ \ + { \ + ":", ".", ":", ":", \ + ":", ":", \ + }, \ +} + +#define EMPTY_STYLE { \ + /* border_chars */ \ + { \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + }, \ + /* header_border_chars */ \ + { \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + "\0", "\0", "\0", "\0", \ + }, \ + /* separator_chars */ \ + { \ + "\0", " ", "\0 ", "\0", \ + "\0", "\0", \ + }, \ +} + + +#define EMPTY2_STYLE { \ + /* border_chars */ \ + { \ + " ", " ", " ", " ", \ + " ", " ", " ", \ + "\0", "\0", "\0", "\0", \ + " ", " ", " ", " ", \ + " ", " ", " ", " ", \ + }, \ + /* header_border_chars */ \ + { \ + " ", " ", " ", " ", \ + " ", " ", " ", \ + "\0", "\0", "\0", "\0", \ + " ", " ", " ", " ", \ + " ", " ", " ", " ", \ + }, \ + /* separator_chars */ \ + { \ + " ", " ", " ", " ", \ + " ", " ", \ + }, \ +} + +#define SOLID_STYLE { \ + /* border_chars */ \ + { \ + "┌", "─", "┬", "┐", \ + "│", "│", "│", \ + "", "", "", "", \ + "└", "─", "┴", "┘", \ + "│", "─", "│", "─", \ + }, \ + /* header_border_chars */ \ + { \ + "┌", "─", "┬", "┐", \ + "│", "│", "│", \ + "├", "─", "┼", "┤", \ + "└", "─", "┴", "┘", \ + "┼", "┬", "┼", "┴", \ + }, \ + /* separator_chars */ \ + { \ + "├", "─", "┼", "┤", \ + "┬", "┴", \ + }, \ +} + +#define SOLID_ROUND_STYLE { \ + /* border_chars */ \ + { \ + "╭", "─", "┬", "╮", \ + "│", "│", "│", \ + "", "", "", "", \ + "╰", "─", "┴", "╯", \ + "│", "─", "│", "─", \ + }, \ + /* header_border_chars */ \ + { \ + "╭", "─", "┬", "╮", \ + "│", "│", "│", \ + "├", "─", "┼", "┤", \ + "╰", "─", "┴", "╯", \ + "┼", "┬", "┼", "┴", \ + }, \ + /* separator_chars */ \ + { \ + "├", "─", "┼", "┤", \ + "┬", "┴", \ + }, \ +} + +#define NICE_STYLE { \ + /* border_chars */ \ + { \ + "╔", "═", "╦", "╗", \ + "║", "║", "║", \ + "", "", "", "", \ + "╚", "═", "╩", "╝", \ + "┣", "┻", "┣", "┳", \ + }, \ + /* header_border_chars */ \ + { \ + "╔", "═", "╦", "╗", \ + "║", "║", "║", \ + "╠", "═", "╬", "╣", \ + "╚", "═", "╩", "╝", \ + "┣", "╦", "┣", "╩", \ + }, \ + /* separator_chars */ \ + { \ + "╟", "─", "╫", "╢", \ + "╥", "╨", \ + }, \ +} + +#define DOUBLE_STYLE { \ + /* border_chars */ \ + { \ + "╔", "═", "╦", "╗", \ + "║", "║", "║", \ + "", "", "", "", \ + "╚", "═", "╩", "╝", \ + "┣", "┻", "┣", "┳", \ + }, \ + /* header_border_chars */ \ + { \ + "╔", "═", "╦", "╗", \ + "║", "║", "║", \ + "╠", "═", "╬", "╣", \ + "╚", "═", "╩", "╝", \ + "┣", "╦", "┣", "╩", \ + }, \ + /* separator_chars */ \ + { \ + "╠", "═", "╬", "╣", \ + "╦", "╩", \ + }, \ +} + + + + +#define DOUBLE2_STYLE { \ + /* border_chars */ \ + { \ + "╔", "═", "╤", "╗", \ + "║", "│", "║", \ + "╟", "─", "┼", "╢", \ + "╚", "═", "╧", "╝", \ + "├", "┬", "┤", "┴", \ + }, \ + /* header_border_chars */ \ + { \ + "╔", "═", "╤", "╗", \ + "║", "│", "║", \ + "╠", "═", "╪", "╣", \ + "╚", "═", "╧", "╝", \ + "├", "╤", "┤", "╧", \ + }, \ + /* separator_chars */ \ + { \ + "╠", "═", "╪", "╣", \ + "╤", "╧", \ + }, \ +} + + +#define BOLD_STYLE { \ + /* border_chars */ \ + { \ + "┏", "━", "┳", "┓", \ + "┃", "┃", "┃", \ + "", "", "", "", \ + "┗", "━", "┻", "┛", \ + "┣", "┻", "┣", "┳", \ + }, \ + /* header_border_chars */ \ + { \ + "┏", "━", "┳", "┓", \ + "┃", "┃", "┃", \ + "┣", "━", "╋", "┫", \ + "┗", "━", "┻", "┛", \ + "┣", "┳", "┣", "┻", \ + }, \ + /* separator_chars */ \ + { \ + "┣", "━", "╋", "┫", \ + "┳", "┻", \ + }, \ +} + +#define BOLD2_STYLE { \ + /* border_chars */ \ + { \ + "┏", "━", "┯", "┓", \ + "┃", "│", "┃", \ + "┠", "─", "┼", "┨", \ + "┗", "━", "┷", "┛", \ + "┣", "┬", "┣", "┴", \ + }, \ + /* header_border_chars */ \ + { \ + "┏", "━", "┯", "┓", \ + "┃", "│", "┃", \ + "┣", "━", "┿", "┫", \ + "┗", "━", "┷", "┛", \ + "┣", "┯", "┣", "┷", \ + }, \ + /* separator_chars */ \ + { \ + "┣", "━", "┿", "┫", \ + "┯", "┷", \ + }, \ +} + +#define FRAME_STYLE { \ + /* border_chars */ \ + { \ + "▛", "▀", "▀", "▜", \ + "▌", "┃", "▐", \ + "", "", "", "", \ + "▙", "▄", "▄", "▟", \ + "┣", "━", "┣", "━" \ + }, \ + /* header_border_chars */ \ + { \ + "▛", "▀", "▀", "▜", \ + "▌", "┃", "▐", \ + "▌", "━", "╋", "▐", \ + "▙", "▄", "▄", "▟", \ + "┣", "━", "┣", "━", \ + }, \ + /* separator_chars */ \ + { \ + "▌", "━", "╋", "▐", \ + "╋", "╋", \ + }, \ +} + + +struct fort_border_style FORT_BASIC_STYLE = BASIC_STYLE; +struct fort_border_style FORT_BASIC2_STYLE = BASIC2_STYLE; +struct fort_border_style FORT_SIMPLE_STYLE = SIMPLE_STYLE; +struct fort_border_style FORT_PLAIN_STYLE = PLAIN_STYLE; +struct fort_border_style FORT_DOT_STYLE = DOT_STYLE; +struct fort_border_style FORT_EMPTY_STYLE = EMPTY_STYLE; +struct fort_border_style FORT_EMPTY2_STYLE = EMPTY2_STYLE; +struct fort_border_style FORT_SOLID_STYLE = SOLID_STYLE; +struct fort_border_style FORT_SOLID_ROUND_STYLE = SOLID_ROUND_STYLE; +struct fort_border_style FORT_NICE_STYLE = NICE_STYLE; +struct fort_border_style FORT_DOUBLE_STYLE = DOUBLE_STYLE; +struct fort_border_style FORT_DOUBLE2_STYLE = DOUBLE2_STYLE; +struct fort_border_style FORT_BOLD_STYLE = BOLD_STYLE; +struct fort_border_style FORT_BOLD2_STYLE = BOLD2_STYLE; +struct fort_border_style FORT_FRAME_STYLE = FRAME_STYLE; + + + +fort_entire_table_properties_t g_entire_table_properties = { + 0, /* left_margin */ + 0, /* top_margin */ + 0, /* right_margin */ + 0, /* bottom_margin */ + FT_STRATEGY_REPLACE, /* add_strategy */ +}; + +static f_status set_entire_table_property_internal(fort_entire_table_properties_t *properties, uint32_t property, int value) +{ + assert(properties); + CHECK_NOT_NEGATIVE(value); + if (PROP_IS_SET(property, FT_TPROP_LEFT_MARGIN)) { + properties->left_margin = value; + } else if (PROP_IS_SET(property, FT_TPROP_TOP_MARGIN)) { + properties->top_margin = value; + } else if (PROP_IS_SET(property, FT_TPROP_RIGHT_MARGIN)) { + properties->right_margin = value; + } else if (PROP_IS_SET(property, FT_TPROP_BOTTOM_MARGIN)) { + properties->bottom_margin = value; + } else if (PROP_IS_SET(property, FT_TPROP_ADDING_STRATEGY)) { + properties->add_strategy = (enum ft_adding_strategy)value; + } else { + return FT_EINVAL; + } + return FT_SUCCESS; + +fort_fail: + return FT_EINVAL; +} + + +FT_INTERNAL +f_status set_entire_table_property(f_table_properties_t *table_properties, uint32_t property, int value) +{ + assert(table_properties); + return set_entire_table_property_internal(&table_properties->entire_table_properties, property, value); +} + + +FT_INTERNAL +f_status set_default_entire_table_property(uint32_t property, int value) +{ + return set_entire_table_property_internal(&g_entire_table_properties, property, value); +} + + +FT_INTERNAL +size_t max_border_elem_strlen(struct f_table_properties *properties) +{ + assert(properties); + size_t result = 1; + int i = 0; + for (i = 0; i < BORDER_ITEM_POS_SIZE; ++i) { + result = MAX(result, strlen(properties->border_style.border_chars[i])); + } + + for (i = 0; i < BORDER_ITEM_POS_SIZE; ++i) { + result = MAX(result, strlen(properties->border_style.header_border_chars[i])); + } + + for (i = 0; i < SEPARATOR_ITEM_POS_SIZE; ++i) { + result = MAX(result, strlen(properties->border_style.separator_chars[i])); + } + return result; +} + + +f_table_properties_t g_table_properties = { + /* border_style */ + BASIC_STYLE, + NULL, /* cell_properties */ + /* entire_table_properties */ + { + 0, /* left_margin */ + 0, /* top_margin */ + 0, /* right_margin */ + 0, /* bottom_margin */ + FT_STRATEGY_REPLACE, /* add_strategy */ + } +}; + + +FT_INTERNAL +f_table_properties_t *create_table_properties(void) +{ + f_table_properties_t *properties = (f_table_properties_t *)F_CALLOC(sizeof(f_table_properties_t), 1); + if (properties == NULL) { + return NULL; + } + memcpy(properties, &g_table_properties, sizeof(f_table_properties_t)); + properties->cell_properties = create_cell_prop_container(); + if (properties->cell_properties == NULL) { + destroy_table_properties(properties); + return NULL; + } + memcpy(&properties->entire_table_properties, &g_entire_table_properties, sizeof(fort_entire_table_properties_t)); + return properties; +} + +FT_INTERNAL +void destroy_table_properties(f_table_properties_t *properties) +{ + if (properties == NULL) + return; + + if (properties->cell_properties != NULL) { + destroy_cell_prop_container(properties->cell_properties); + } + F_FREE(properties); +} + +static +f_cell_prop_container_t *copy_cell_properties(f_cell_prop_container_t *cont) +{ + f_cell_prop_container_t *result = create_cell_prop_container(); + if (result == NULL) + return NULL; + + size_t i = 0; + size_t sz = vector_size(cont); + for (i = 0; i < sz; ++i) { + f_cell_props_t *opt = (f_cell_props_t *)vector_at(cont, i); + if (FT_IS_ERROR(vector_push(result, opt))) { + destroy_cell_prop_container(result); + return NULL; + } + } + return result; +} + +FT_INTERNAL +f_table_properties_t *copy_table_properties(const f_table_properties_t *properties) +{ + f_table_properties_t *new_opt = create_table_properties(); + if (new_opt == NULL) + return NULL; + + destroy_vector(new_opt->cell_properties); + new_opt->cell_properties = copy_cell_properties(properties->cell_properties); + if (new_opt->cell_properties == NULL) { + destroy_table_properties(new_opt); + return NULL; + } + + memcpy(&new_opt->border_style, &properties->border_style, sizeof(struct fort_border_style)); + memcpy(&new_opt->entire_table_properties, + &properties->entire_table_properties, sizeof(fort_entire_table_properties_t)); + + return new_opt; +} + +/******************************************************** + End of file "properties.c" + ********************************************************/ + + +/******************************************************** + Begin of file "row.c" + ********************************************************/ + +#include <assert.h> +#include <ctype.h> +/* #include "row.h" */ /* Commented by amalgamation script */ +/* #include "cell.h" */ /* Commented by amalgamation script */ +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +/* #include "vector.h" */ /* Commented by amalgamation script */ + + +struct f_row { + f_vector_t *cells; +}; + +static +f_row_t *create_row_impl(f_vector_t *cells) +{ + f_row_t *row = (f_row_t *)F_CALLOC(1, sizeof(f_row_t)); + if (row == NULL) + return NULL; + if (cells) { + row->cells = cells; + } else { + row->cells = create_vector(sizeof(f_cell_t *), DEFAULT_VECTOR_CAPACITY); + if (row->cells == NULL) { + F_FREE(row); + return NULL; + } + } + return row; +} + +FT_INTERNAL +f_row_t *create_row(void) +{ + return create_row_impl(NULL); +} + +static +void destroy_each_cell(f_vector_t *cells) +{ + size_t i = 0; + size_t cells_n = vector_size(cells); + for (i = 0; i < cells_n; ++i) { + f_cell_t *cell = VECTOR_AT(cells, i, f_cell_t *); + destroy_cell(cell); + } +} + +FT_INTERNAL +void destroy_row(f_row_t *row) +{ + if (row == NULL) + return; + + if (row->cells) { + destroy_each_cell(row->cells); + destroy_vector(row->cells); + } + + F_FREE(row); +} + +FT_INTERNAL +f_row_t *copy_row(f_row_t *row) +{ + assert(row); + f_row_t *result = create_row(); + if (result == NULL) + return NULL; + + size_t i = 0; + size_t cols_n = vector_size(row->cells); + for (i = 0; i < cols_n; ++i) { + f_cell_t *cell = VECTOR_AT(row->cells, i, f_cell_t *); + f_cell_t *new_cell = copy_cell(cell); + if (new_cell == NULL) { + destroy_row(result); + return NULL; + } + vector_push(result->cells, &new_cell); + } + + return result; +} + +FT_INTERNAL +f_row_t *split_row(f_row_t *row, size_t pos) +{ + assert(row); + + f_vector_t *cells = vector_split(row->cells, pos); + if (!cells) + return NULL; + f_row_t *tail = create_row_impl(cells); + if (!tail) { + destroy_each_cell(cells); + destroy_vector(cells); + } + return tail; +} + +FT_INTERNAL +int ft_row_erase_range(f_row_t *row, size_t left, size_t right) +{ + assert(row); + size_t cols_n = vector_size(row->cells); + if (cols_n == 0 || (right < left)) + return FT_SUCCESS; + + f_cell_t *cell = NULL; + size_t i = left; + while (i < cols_n && i <= right) { + cell = VECTOR_AT(row->cells, i, f_cell_t *); + destroy_cell(cell); + ++i; + } + size_t n_destroy = MIN(cols_n - 1, right) - left + 1; + while (n_destroy--) { + vector_erase(row->cells, left); + } + return FT_SUCCESS; +} + +FT_INTERNAL +size_t columns_in_row(const f_row_t *row) +{ + if (row == NULL || row->cells == NULL) + return 0; + + return vector_size(row->cells); +} + + +static +f_cell_t *get_cell_impl(f_row_t *row, size_t col, enum f_get_policy policy) +{ + if (row == NULL || row->cells == NULL) { + return NULL; + } + + switch (policy) { + case DONT_CREATE_ON_NULL: + if (col < columns_in_row(row)) { + return VECTOR_AT(row->cells, col, f_cell_t *); + } + return NULL; + case CREATE_ON_NULL: + while (col >= columns_in_row(row)) { + f_cell_t *new_cell = create_cell(); + if (new_cell == NULL) + return NULL; + if (FT_IS_ERROR(vector_push(row->cells, &new_cell))) { + destroy_cell(new_cell); + return NULL; + } + } + return VECTOR_AT(row->cells, col, f_cell_t *); + } + + assert(0 && "Shouldn't be here!"); + return NULL; +} + + +FT_INTERNAL +f_cell_t *get_cell(f_row_t *row, size_t col) +{ + return get_cell_impl(row, col, DONT_CREATE_ON_NULL); +} + + +FT_INTERNAL +const f_cell_t *get_cell_c(const f_row_t *row, size_t col) +{ + return get_cell((f_row_t *)row, col); +} + + +FT_INTERNAL +f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col) +{ + return get_cell_impl(row, col, CREATE_ON_NULL); +} + +FT_INTERNAL +f_cell_t *create_cell_in_position(f_row_t *row, size_t col) +{ + if (row == NULL || row->cells == NULL) { + return NULL; + } + + f_cell_t *new_cell = create_cell(); + if (new_cell == NULL) + return NULL; + if (FT_IS_ERROR(vector_insert(row->cells, &new_cell, col))) { + destroy_cell(new_cell); + return NULL; + } + return VECTOR_AT(row->cells, col, f_cell_t *); +} + + +FT_INTERNAL +f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos) +{ + assert(cur_row); + assert(ins_row); + size_t cur_sz = vector_size(cur_row->cells); + if (cur_sz == 0 && pos == 0) { + f_row_t tmp; + memcpy(&tmp, cur_row, sizeof(f_row_t)); + memcpy(cur_row, ins_row, sizeof(f_row_t)); + memcpy(ins_row, &tmp, sizeof(f_row_t)); + return FT_SUCCESS; + } + + // Append empty cells to `cur_row` if needed. + while (vector_size(cur_row->cells) < pos) { + create_cell_in_position(cur_row, vector_size(cur_row->cells)); + } + + return vector_swap(cur_row->cells, ins_row->cells, pos); +} + +/* Ownership of cells of `ins_row` is passed to `cur_row`. */ +FT_INTERNAL +f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos) +{ + assert(cur_row); + assert(ins_row); + + while (vector_size(cur_row->cells) < pos) { + f_cell_t *new_cell = create_cell(); + if (!new_cell) + return FT_GEN_ERROR; + vector_push(cur_row->cells, &new_cell); + } + + size_t sz = vector_size(ins_row->cells); + size_t i = 0; + for (i = 0; i < sz; ++i) { + f_cell_t *cell = VECTOR_AT(ins_row->cells, i, f_cell_t *); + if (FT_IS_ERROR(vector_insert(cur_row->cells, &cell, pos + i))) { + /* clean up what we have inserted */ + while (i--) { + vector_erase(cur_row->cells, pos); + } + return FT_GEN_ERROR; + } + } + /* Clear cells so that it will be safe to destroy this row */ + vector_clear(ins_row->cells); + return FT_SUCCESS; +} + + +FT_INTERNAL +size_t group_cell_number(const f_row_t *row, size_t master_cell_col) +{ + assert(row); + const f_cell_t *master_cell = get_cell_c(row, master_cell_col); + if (master_cell == NULL) + return 0; + + if (get_cell_type(master_cell) != GROUP_MASTER_CELL) + return 1; + + size_t total_cols = vector_size(row->cells); + size_t slave_col = master_cell_col + 1; + while (slave_col < total_cols) { + const f_cell_t *cell = get_cell_c(row, slave_col); + if (cell && get_cell_type(cell) == GROUP_SLAVE_CELL) { + ++slave_col; + } else { + break; + } + } + return slave_col - master_cell_col; +} + + +FT_INTERNAL +int get_row_cell_types(const f_row_t *row, enum f_cell_type *types, size_t types_sz) +{ + assert(row); + assert(types); + size_t i = 0; + for (i = 0; i < types_sz; ++i) { + const f_cell_t *cell = get_cell_c(row, i); + if (cell) { + types[i] = get_cell_type(cell); + } else { + types[i] = COMMON_CELL; + } + } + return FT_SUCCESS; +} + + +FT_INTERNAL +f_status row_set_cell_span(f_row_t *row, size_t cell_column, size_t hor_span) +{ + assert(row); + + if (hor_span < 2) + return FT_EINVAL; + + f_cell_t *main_cell = get_cell_and_create_if_not_exists(row, cell_column); + if (main_cell == NULL) { + return FT_GEN_ERROR; + } + set_cell_type(main_cell, GROUP_MASTER_CELL); + --hor_span; + ++cell_column; + + while (hor_span) { + f_cell_t *slave_cell = get_cell_and_create_if_not_exists(row, cell_column); + if (slave_cell == NULL) { + return FT_GEN_ERROR; + } + set_cell_type(slave_cell, GROUP_SLAVE_CELL); + --hor_span; + ++cell_column; + } + + return FT_SUCCESS; +} + +static +int 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) +{ + assert(cntx); + + int status = FT_GEN_ERROR; + + const f_context_t *context = cntx->cntx; + + /* Get cell types + * + * Regions above top row and below bottom row areconsidered full of virtual + * GROUP_SLAVE_CELL cells + */ + enum f_cell_type *top_row_types = (enum f_cell_type *)F_MALLOC(sizeof(enum f_cell_type) * cols * 2); + if (top_row_types == NULL) { + return FT_MEMORY_ERROR; + } + enum f_cell_type *bottom_row_types = top_row_types + cols; + if (upper_row) { + get_row_cell_types(upper_row, top_row_types, cols); + } else { + size_t i = 0; + for (i = 0; i < cols; ++i) + top_row_types[i] = GROUP_SLAVE_CELL; + } + if (lower_row) { + get_row_cell_types(lower_row, bottom_row_types, cols); + } else { + size_t i = 0; + for (i = 0; i < cols; ++i) + bottom_row_types[i] = GROUP_SLAVE_CELL; + } + + + f_table_properties_t *properties = context->table_properties; + fort_entire_table_properties_t *entire_tprops = &properties->entire_table_properties; + + size_t written = 0; + int tmp = 0; + + enum ft_row_type lower_row_type = FT_ROW_COMMON; + if (lower_row != NULL) { + lower_row_type = (enum ft_row_type)get_cell_property_hierarchically(properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); + } + enum ft_row_type upper_row_type = FT_ROW_COMMON; + if (upper_row != NULL) { + upper_row_type = (enum ft_row_type)get_cell_property_hierarchically(properties, context->row - 1, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); + } + + /* Row separator anatomy + * + * | C11 | C12 C13 | C14 C15 | + * L I I I IV I I IT I I I IB I I II I I R + * | C21 | C22 | C23 C24 C25 | + */ + const char **L = NULL; + const char **I = NULL; + const char **IV = NULL; + const char **R = NULL; + const char **IT = NULL; + const char **IB = NULL; + const char **II = NULL; + + struct fort_border_style *border_style = &properties->border_style; + + typedef const char *(*border_chars_point_t)[BORDER_ITEM_POS_SIZE]; + const char *(*border_chars)[BORDER_ITEM_POS_SIZE] = NULL; + border_chars = (border_chars_point_t)&border_style->border_chars; + if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) { + border_chars = (border_chars_point_t)&border_style->header_border_chars; + } + + if (sep && sep->enabled) { + L = &(border_style->separator_chars[LH_sip]); + I = &(border_style->separator_chars[IH_sip]); + IV = &(border_style->separator_chars[II_sip]); + R = &(border_style->separator_chars[RH_sip]); + + IT = &(border_style->separator_chars[TI_sip]); + IB = &(border_style->separator_chars[BI_sip]); + II = &(border_style->separator_chars[IH_sip]); + + if (lower_row == NULL) { + L = &(*border_chars)[BL_bip]; + R = &(*border_chars)[BR_bip]; + } else if (upper_row == NULL) { + L = &(*border_chars)[TL_bip]; + R = &(*border_chars)[TR_bip]; + } + } else { + switch (separatorPos) { + case TOP_SEPARATOR: + L = &(*border_chars)[TL_bip]; + I = &(*border_chars)[TT_bip]; + IV = &(*border_chars)[TV_bip]; + R = &(*border_chars)[TR_bip]; + + IT = &(*border_chars)[TV_bip]; + IB = &(*border_chars)[TV_bip]; + II = &(*border_chars)[TT_bip]; + break; + case INSIDE_SEPARATOR: + L = &(*border_chars)[LH_bip]; + I = &(*border_chars)[IH_bip]; + IV = &(*border_chars)[II_bip]; + R = &(*border_chars)[RH_bip]; + + IT = &(*border_chars)[TI_bip]; + IB = &(*border_chars)[BI_bip]; + II = &(*border_chars)[IH_bip]; + break; + case BOTTOM_SEPARATOR: + L = &(*border_chars)[BL_bip]; + I = &(*border_chars)[BB_bip]; + IV = &(*border_chars)[BV_bip]; + R = &(*border_chars)[BR_bip]; + + IT = &(*border_chars)[BV_bip]; + IB = &(*border_chars)[BV_bip]; + II = &(*border_chars)[BB_bip]; + break; + default: + break; + } + } + + size_t i = 0; + + /* If all chars are not printable, skip line separator */ + /* NOTE: argument of `isprint` should be explicitly converted to + * unsigned char according to + * https://en.cppreference.com/w/c/string/byte/isprint + */ + if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint((unsigned char) **L))) + && (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint((unsigned char) **I))) + && (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint((unsigned char) **IV))) + && (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint((unsigned char) **R)))) { + status = 0; + goto clear; + } + + /* Print left margin */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->left_margin, FT_SPACE)); + + for (i = 0; i < cols; ++i) { + if (i == 0) { + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L)); + } else { + if ((top_row_types[i] == COMMON_CELL || top_row_types[i] == GROUP_MASTER_CELL) + && (bottom_row_types[i] == COMMON_CELL || bottom_row_types[i] == GROUP_MASTER_CELL)) { + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV)); + } else if (top_row_types[i] == GROUP_SLAVE_CELL && bottom_row_types[i] == GROUP_SLAVE_CELL) { + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *II)); + } else if (top_row_types[i] == GROUP_SLAVE_CELL) { + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IT)); + } else { + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IB)); + } + } + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[i], *I)); + } + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R)); + + /* Print right margin */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->right_margin, FT_SPACE)); + + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, FT_NEWLINE)); + + status = (int)written; + +clear: + F_FREE(top_row_types); + return status; +} + +FT_INTERNAL +int 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) +{ + return print_row_separator_impl(cntx, col_width_arr, cols, upper_row, lower_row, + separatorPos, sep); +} + +FT_INTERNAL +f_row_t *create_row_from_string(const char *str) +{ + typedef char char_type; + char_type *(*strdup_)(const char_type * str) = F_STRDUP; + const char_type zero_char = '\0'; + f_status(*fill_cell_from_string_)(f_cell_t *cell, const char *str) = fill_cell_from_string; + const char_type *const zero_string = ""; +#define STRCHR strchr + + char_type *pos = NULL; + char_type *base_pos = NULL; + size_t number_of_separators = 0; + + f_row_t *row = create_row(); + if (row == NULL) + return NULL; + + if (str == NULL) + return row; + + char_type *str_copy = strdup_(str); + if (str_copy == NULL) + goto clear; + + pos = str_copy; + base_pos = str_copy; + number_of_separators = 0; + while (*pos) { + pos = STRCHR(pos, g_col_separator); + if (pos != NULL) { + *(pos) = zero_char; + ++pos; + number_of_separators++; + } + + f_cell_t *cell = create_cell(); + if (cell == NULL) + goto clear; + + int status = fill_cell_from_string_(cell, base_pos); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + status = vector_push(row->cells, &cell); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + if (pos == NULL) + break; + base_pos = pos; + } + + /* special case if in format string last cell is empty */ + while (vector_size(row->cells) < (number_of_separators + 1)) { + f_cell_t *cell = create_cell(); + if (cell == NULL) + goto clear; + + int status = fill_cell_from_string_(cell, zero_string); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + status = vector_push(row->cells, &cell); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + } + + F_FREE(str_copy); + return row; + +clear: + destroy_row(row); + F_FREE(str_copy); + return NULL; + +#undef STRCHR +} + + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +f_row_t *create_row_from_wstring(const wchar_t *str) +{ + typedef wchar_t char_type; + char_type *(*strdup_)(const char_type * str) = F_WCSDUP; + const char_type zero_char = L'\0'; + f_status(*fill_cell_from_string_)(f_cell_t *cell, const wchar_t *str) = fill_cell_from_wstring; + const char_type *const zero_string = L""; +#define STRCHR wcschr + + char_type *pos = NULL; + char_type *base_pos = NULL; + size_t number_of_separators = 0; + + f_row_t *row = create_row(); + if (row == NULL) + return NULL; + + if (str == NULL) + return row; + + char_type *str_copy = strdup_(str); + if (str_copy == NULL) + goto clear; + + pos = str_copy; + base_pos = str_copy; + number_of_separators = 0; + while (*pos) { + pos = STRCHR(pos, g_col_separator); + if (pos != NULL) { + *(pos) = zero_char; + ++pos; + number_of_separators++; + } + + f_cell_t *cell = create_cell(); + if (cell == NULL) + goto clear; + + int status = fill_cell_from_string_(cell, base_pos); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + status = vector_push(row->cells, &cell); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + if (pos == NULL) + break; + base_pos = pos; + } + + /* special case if in format string last cell is empty */ + while (vector_size(row->cells) < (number_of_separators + 1)) { + f_cell_t *cell = create_cell(); + if (cell == NULL) + goto clear; + + int status = fill_cell_from_string_(cell, zero_string); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + status = vector_push(row->cells, &cell); + if (FT_IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + } + + F_FREE(str_copy); + return row; + +clear: + destroy_row(row); + F_FREE(str_copy); + return NULL; +#undef STRCHR +} +#endif + +FT_INTERNAL +f_row_t *create_row_from_buffer(const f_string_buffer_t *buffer) +{ + switch (buffer->type) { + case CHAR_BUF: + return create_row_from_string(buffer->str.cstr); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return create_row_from_wstring(buffer->str.wstr); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return create_row_from_string((const char *)buffer->str.u8str); +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + return NULL; + } +} + +static int +vsnprintf_buffer(f_string_buffer_t *buffer, const struct f_string_view *fmt, + va_list *va) +{ + /* Disable compiler diagnostic (format string is not a string literal) */ +#if defined(FT_CLANG_COMPILER) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif +#if defined(FT_GCC_COMPILER) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + size_t width_capacity = string_buffer_width_capacity(buffer); + switch (buffer->type) { + case CHAR_BUF: + return vsnprintf(buffer->str.cstr, width_capacity, fmt->u.cstr, *va); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return vswprintf(buffer->str.wstr, width_capacity, fmt->u.wstr, *va); +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return vsnprintf(buffer->str.cstr, width_capacity, fmt->u.cstr, *va); +#endif + default: + assert(0); + return 0; + } +#if defined(FT_CLANG_COMPILER) +#pragma clang diagnostic pop +#endif +#if defined(FT_GCC_COMPILER) +#pragma GCC diagnostic pop +#endif +} + +FT_INTERNAL +f_row_t *create_row_from_fmt_string(const struct f_string_view *fmt, va_list *va_args) +{ + f_string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, fmt->type); + if (buffer == NULL) + return NULL; + + size_t cols_origin = number_of_columns_in_format_string(fmt); + size_t cols = 0; + + while (1) { + va_list va; + va_copy(va, *va_args); + int virtual_sz = vsnprintf_buffer(buffer, fmt, &va); + va_end(va); + /* If error encountered */ + if (virtual_sz < 0) + goto clear; + + /* Successful write */ + if ((size_t)virtual_sz < string_buffer_width_capacity(buffer)) + break; + + /* Otherwise buffer was too small, so incr. buffer size ant try again. */ + if (!FT_IS_SUCCESS(realloc_string_buffer_without_copy(buffer))) + goto clear; + } + + cols = number_of_columns_in_format_buffer(buffer); + if (cols == cols_origin) { + f_row_t *row = create_row_from_buffer(buffer); + if (row == NULL) { + goto clear; + } + + destroy_string_buffer(buffer); + return row; + } + + if (cols_origin == 1) { + f_row_t *row = create_row(); + if (row == NULL) { + goto clear; + } + + f_cell_t *cell = get_cell_and_create_if_not_exists(row, 0); + if (cell == NULL) { + destroy_row(row); + goto clear; + } + + f_status result = fill_cell_from_buffer(cell, buffer); + if (FT_IS_ERROR(result)) { + destroy_row(row); + goto clear; + } + + destroy_string_buffer(buffer); + return row; + } + + /* + * todo: add processing of cols != cols_origin in a general way + * (when cols_origin != 1). + */ + +clear: + destroy_string_buffer(buffer); + return NULL; +} + + +FT_INTERNAL +int 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) +{ + const f_context_t *context = cntx->cntx; + assert(context); + + if (row == NULL) + return -1; + + size_t cols_in_row = columns_in_row(row); + if (cols_in_row > col_width_arr_sz) + return -1; + + /* Row separator anatomy + * + * L data IV data IV data R + */ + f_table_properties_t *properties = context->table_properties; + + typedef const char *(*border_chars_point_t)[BORDER_ITEM_POS_SIZE]; + enum ft_row_type row_type = (enum ft_row_type)get_cell_property_hierarchically(properties, context->row, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE); + const char *(*bord_chars)[BORDER_ITEM_POS_SIZE] = (row_type == FT_ROW_HEADER) + ? (border_chars_point_t)(&properties->border_style.header_border_chars) + : (border_chars_point_t)(&properties->border_style.border_chars); + const char **L = &(*bord_chars)[LL_bip]; + const char **IV = &(*bord_chars)[IV_bip]; + const char **R = &(*bord_chars)[RR_bip]; + + + size_t written = 0; + int tmp = 0; + size_t i = 0; + fort_entire_table_properties_t *entire_tprops = &context->table_properties->entire_table_properties; + for (i = 0; i < row_height; ++i) { + /* Print left margin */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->left_margin, FT_SPACE)); + + /* Print left table boundary */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *L)); + size_t j = 0; + while (j < col_width_arr_sz) { + if (j < cols_in_row) { + ((f_context_t *)context)->column = j; + f_cell_t *cell = VECTOR_AT(row->cells, j, f_cell_t *); + size_t cell_vis_width = 0; + + size_t group_slave_sz = group_cell_number(row, j); + cell_vis_width = col_width_arr[j]; + size_t slave_j = 0; + size_t master_j = j; + for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { + cell_vis_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; + ++j; + } + + CHCK_RSLT_ADD_TO_WRITTEN(cell_printf(cell, i, cntx, cell_vis_width)); + } else { + /* Print empty cell */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, col_width_arr[j], FT_SPACE)); + } + + /* Print boundary between cells */ + if (j < col_width_arr_sz - 1) + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *IV)); + + ++j; + } + + /* Print right table boundary */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, *R)); + + /* Print right margin */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, entire_tprops->right_margin, FT_SPACE)); + + /* Print new line character */ + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, FT_NEWLINE)); + } + return (int)written; + +clear: + return -1; +} + +/******************************************************** + End of file "row.c" + ********************************************************/ + + +/******************************************************** + Begin of file "string_buffer.c" + ********************************************************/ + +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +/* #include "properties.h" */ /* Commented by amalgamation script */ +/* #include "wcwidth.h" */ /* Commented by amalgamation script */ +#include <assert.h> +#include <stddef.h> +#ifdef FT_HAVE_WCHAR +#include <wchar.h> +#endif +#if defined(FT_HAVE_UTF8) +/* #include "utf8.h" */ /* Commented by amalgamation script */ +#endif + +static ptrdiff_t str_iter_width(const char *beg, const char *end) +{ + assert(end >= beg); + return (end - beg); +} + + +#ifdef FT_HAVE_WCHAR +static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end) +{ + assert(end >= beg); + return mk_wcswidth(beg, (size_t)(end - beg)); +} +#endif /* FT_HAVE_WCHAR */ + + +static size_t buf_str_len(const f_string_buffer_t *buf) +{ + assert(buf); + + switch (buf->type) { + case CHAR_BUF: + return strlen(buf->str.cstr); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return wcslen(buf->str.wstr); +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return utf8len(buf->str.u8str); +#endif + } + + assert(0); + return 0; +} + + +FT_INTERNAL +size_t strchr_count(const char *str, char ch) +{ + if (str == NULL) + return 0; + + size_t count = 0; + str = strchr(str, ch); + while (str) { + count++; + str++; + str = strchr(str, ch); + } + return count; +} + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +size_t wstrchr_count(const wchar_t *str, wchar_t ch) +{ + if (str == NULL) + return 0; + + size_t count = 0; + str = wcschr(str, ch); + while (str) { + count++; + str++; + str = wcschr(str, ch); + } + return count; +} +#endif + + +#if defined(FT_HAVE_UTF8) +/* todo: do something with code below!!! */ +FT_INTERNAL +void *ut8next(const void *str) +{ + utf8_int32_t out_codepoint; + return utf8codepoint(str, &out_codepoint); +} + +FT_INTERNAL +size_t utf8chr_count(const void *str, utf8_int32_t ch) +{ + if (str == NULL) + return 0; + + size_t count = 0; + str = utf8chr(str, ch); + while (str) { + count++; + str = ut8next(str); + str = utf8chr(str, ch); + } + return count; +} +#endif /* FT_HAVE_UTF8 */ + + +FT_INTERNAL +const char *str_n_substring_beg(const char *str, char ch_separator, size_t n) +{ + if (str == NULL) + return NULL; + + if (n == 0) + return str; + + str = strchr(str, ch_separator); + --n; + while (n > 0) { + if (str == NULL) + return NULL; + --n; + str++; + str = strchr(str, ch_separator); + } + return str ? (str + 1) : NULL; +} + + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +const wchar_t *wstr_n_substring_beg(const wchar_t *str, wchar_t ch_separator, size_t n) +{ + if (str == NULL) + return NULL; + + if (n == 0) + return str; + + str = wcschr(str, ch_separator); + --n; + while (n > 0) { + if (str == NULL) + return NULL; + --n; + str++; + str = wcschr(str, ch_separator); + } + return str ? (str + 1) : NULL; +} +#endif /* FT_HAVE_WCHAR */ + +#if defined(FT_HAVE_UTF8) +FT_INTERNAL +const void *utf8_n_substring_beg(const void *str, utf8_int32_t ch_separator, size_t n) +{ + if (str == NULL) + return NULL; + + if (n == 0) + return str; + + str = utf8chr(str, ch_separator); + --n; + while (n > 0) { + if (str == NULL) + return NULL; + --n; + str = ut8next(str); + str = utf8chr(str, ch_separator); + } + return str ? (ut8next(str)) : NULL; +} +#endif + + +FT_INTERNAL +void str_n_substring(const char *str, char ch_separator, size_t n, const char **begin, const char **end) +{ + const char *beg = str_n_substring_beg(str, ch_separator, n); + if (beg == NULL) { + *begin = NULL; + *end = NULL; + return; + } + + const char *en = strchr(beg, ch_separator); + if (en == NULL) { + en = str + strlen(str); + } + + *begin = beg; + *end = en; + return; +} + + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +void wstr_n_substring(const wchar_t *str, wchar_t ch_separator, size_t n, const wchar_t **begin, const wchar_t **end) +{ + const wchar_t *beg = wstr_n_substring_beg(str, ch_separator, n); + if (beg == NULL) { + *begin = NULL; + *end = NULL; + return; + } + + const wchar_t *en = wcschr(beg, ch_separator); + if (en == NULL) { + en = str + wcslen(str); + } + + *begin = beg; + *end = en; + return; +} +#endif /* FT_HAVE_WCHAR */ + +#if defined(FT_HAVE_UTF8) +FT_INTERNAL +void utf8_n_substring(const void *str, utf8_int32_t ch_separator, size_t n, const void **begin, const void **end) +{ + const char *beg = (const char *)utf8_n_substring_beg(str, ch_separator, n); + if (beg == NULL) { + *begin = NULL; + *end = NULL; + return; + } + + const char *en = (const char *)utf8chr(beg, ch_separator); + if (en == NULL) { + en = (const char *)str + strlen((const char *)str); + } + + *begin = beg; + *end = en; + return; +} +#endif /* FT_HAVE_UTF8 */ + + + +FT_INTERNAL +f_string_buffer_t *create_string_buffer(size_t n_chars, enum f_string_type type) +{ + size_t char_sz = 0; + switch (type) { + case CHAR_BUF: + char_sz = 1; + break; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + char_sz = sizeof(wchar_t); + break; +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + char_sz = 4; + break; +#endif + } + + size_t sz = n_chars * char_sz; + f_string_buffer_t *result = (f_string_buffer_t *)F_MALLOC(sizeof(f_string_buffer_t)); + if (result == NULL) + return NULL; + result->str.data = F_MALLOC(sz); + if (result->str.data == NULL) { + F_FREE(result); + return NULL; + } + result->data_sz = sz; + result->type = type; + + if (sz) { + switch (type) { + case CHAR_BUF: + result->str.cstr[0] = '\0'; + break; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + result->str.wstr[0] = L'\0'; + break; +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + result->str.cstr[0] = '\0'; + break; +#endif + } + } + + return result; +} + + +FT_INTERNAL +void destroy_string_buffer(f_string_buffer_t *buffer) +{ + if (buffer == NULL) + return; + F_FREE(buffer->str.data); + buffer->str.data = NULL; + F_FREE(buffer); +} + +FT_INTERNAL +f_string_buffer_t *copy_string_buffer(const f_string_buffer_t *buffer) +{ + assert(buffer); + f_string_buffer_t *result = create_string_buffer(buffer->data_sz, buffer->type); + if (result == NULL) + return NULL; + switch (buffer->type) { + case CHAR_BUF: + if (FT_IS_ERROR(fill_buffer_from_string(result, buffer->str.cstr))) { + destroy_string_buffer(result); + return NULL; + } + break; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + if (FT_IS_ERROR(fill_buffer_from_wstring(result, buffer->str.wstr))) { + destroy_string_buffer(result); + return NULL; + } + break; +#endif /* FT_HAVE_WCHAR */ + default: + destroy_string_buffer(result); + return NULL; + } + return result; +} + +FT_INTERNAL +f_status realloc_string_buffer_without_copy(f_string_buffer_t *buffer) +{ + assert(buffer); + char *new_str = (char *)F_MALLOC(buffer->data_sz * 2); + if (new_str == NULL) { + return FT_MEMORY_ERROR; + } + F_FREE(buffer->str.data); + buffer->str.data = new_str; + buffer->data_sz *= 2; + return FT_SUCCESS; +} + + +FT_INTERNAL +f_status fill_buffer_from_string(f_string_buffer_t *buffer, const char *str) +{ + assert(buffer); + assert(str); + + char *copy = F_STRDUP(str); + if (copy == NULL) + return FT_MEMORY_ERROR; + + F_FREE(buffer->str.data); + buffer->str.cstr = copy; + buffer->type = CHAR_BUF; + + return FT_SUCCESS; +} + + +#ifdef FT_HAVE_WCHAR +FT_INTERNAL +f_status fill_buffer_from_wstring(f_string_buffer_t *buffer, const wchar_t *str) +{ + assert(buffer); + assert(str); + + wchar_t *copy = F_WCSDUP(str); + if (copy == NULL) + return FT_MEMORY_ERROR; + + F_FREE(buffer->str.data); + buffer->str.wstr = copy; + buffer->type = W_CHAR_BUF; + + return FT_SUCCESS; +} +#endif /* FT_HAVE_WCHAR */ + +#ifdef FT_HAVE_UTF8 +FT_INTERNAL +f_status fill_buffer_from_u8string(f_string_buffer_t *buffer, const void *str) +{ + assert(buffer); + assert(str); + + void *copy = F_UTF8DUP(str); + if (copy == NULL) + return FT_MEMORY_ERROR; + + F_FREE(buffer->str.u8str); + buffer->str.u8str = copy; + buffer->type = UTF8_BUF; + + return FT_SUCCESS; +} +#endif /* FT_HAVE_UTF8 */ + +FT_INTERNAL +size_t buffer_text_visible_height(const f_string_buffer_t *buffer) +{ + if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) { + return 0; + } + if (buffer->type == CHAR_BUF) + return 1 + strchr_count(buffer->str.cstr, '\n'); +#ifdef FT_HAVE_WCHAR + else if (buffer->type == W_CHAR_BUF) + return 1 + wstrchr_count(buffer->str.wstr, L'\n'); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + else if (buffer->type == UTF8_BUF) + return 1 + utf8chr_count(buffer->str.u8str, '\n'); +#endif /* FT_HAVE_WCHAR */ + + assert(0); + return 0; +} + +FT_INTERNAL +size_t string_buffer_cod_width_capacity(const f_string_buffer_t *buffer) +{ + return string_buffer_width_capacity(buffer); +} + +FT_INTERNAL +size_t string_buffer_raw_capacity(const f_string_buffer_t *buffer) +{ + return buffer->data_sz; +} + +#ifdef FT_HAVE_UTF8 +/* User provided function to compute utf8 string visible width */ +static int (*_custom_u8strwid)(const void *beg, const void *end, size_t *width) = NULL; + +FT_INTERNAL +void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width)) +{ + _custom_u8strwid = u8strwid; +} + +static +size_t utf8_width(const void *beg, const void *end) +{ + if (_custom_u8strwid) { + size_t width = 0; + if (!_custom_u8strwid(beg, end, &width)) + return width; + } + + size_t sz = (size_t)((const char *)end - (const char *)beg); + char *tmp = (char *)F_MALLOC(sizeof(char) * (sz + 1)); + // @todo: add check to tmp + assert(tmp); + + memcpy(tmp, beg, sz); + tmp[sz] = '\0'; + size_t result = utf8width(tmp); + F_FREE(tmp); + return result; +} +#endif /* FT_HAVE_WCHAR */ + +FT_INTERNAL +size_t buffer_text_visible_width(const f_string_buffer_t *buffer) +{ + size_t max_length = 0; + if (buffer->type == CHAR_BUF) { + size_t n = 0; + while (1) { + const char *beg = NULL; + const char *end = NULL; + str_n_substring(buffer->str.cstr, '\n', n, &beg, &end); + if (beg == NULL || end == NULL) + return max_length; + + max_length = MAX(max_length, (size_t)(end - beg)); + ++n; + } +#ifdef FT_HAVE_WCHAR + } else if (buffer->type == W_CHAR_BUF) { + size_t n = 0; + while (1) { + const wchar_t *beg = NULL; + const wchar_t *end = NULL; + wstr_n_substring(buffer->str.wstr, L'\n', n, &beg, &end); + if (beg == NULL || end == NULL) + return max_length; + + int line_width = mk_wcswidth(beg, (size_t)(end - beg)); + if (line_width < 0) /* For safety */ + line_width = 0; + max_length = MAX(max_length, (size_t)line_width); + + ++n; + } +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + } else if (buffer->type == UTF8_BUF) { + size_t n = 0; + while (1) { + const void *beg = NULL; + const void *end = NULL; + utf8_n_substring(buffer->str.u8str, '\n', n, &beg, &end); + if (beg == NULL || end == NULL) + return max_length; + + max_length = MAX(max_length, (size_t)utf8_width(beg, end)); + ++n; + } +#endif /* FT_HAVE_WCHAR */ + } + + return max_length; /* shouldn't be here */ +} + + +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) +{ + switch (buffer->type) { + case CHAR_BUF: + str_n_substring(buffer->str.cstr, '\n', buffer_row, (const char **)begin, (const char **)end); + if ((*(const char **)begin) && (*(const char **)end)) + *str_it_width = str_iter_width(*(const char **)begin, *(const char **)end); + break; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + wstr_n_substring(buffer->str.wstr, L'\n', buffer_row, (const wchar_t **)begin, (const wchar_t **)end); + if ((*(const wchar_t **)begin) && (*(const wchar_t **)end)) + *str_it_width = wcs_iter_width(*(const wchar_t **)begin, *(const wchar_t **)end); + break; +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + utf8_n_substring(buffer->str.u8str, '\n', buffer_row, begin, end); + if ((*(const char **)begin) && (*(const char **)end)) + *str_it_width = utf8_width(*begin, *end); + break; +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + } +} + + +static int +buffer_print_range(f_conv_context_t *cntx, const void *beg, const void *end) +{ + size_t len; + switch (cntx->b_type) { + case CHAR_BUF: + len = (size_t)((const char *)end - (const char *)beg); + return ft_nprint(cntx, (const char *)beg, len); +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + len = (size_t)((const wchar_t *)end - (const wchar_t *)beg); + return ft_nwprint(cntx, (const wchar_t *)beg, len); +#endif /* FT_HAVE_WCHAR */ +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return ft_nu8print(cntx, beg, end); +#endif /* FT_HAVE_UTF8 */ + default: + assert(0); + return -1; + } +} + + +FT_INTERNAL +int buffer_printf(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) +{ + const f_context_t *context = cntx->cntx; + f_table_properties_t *props = context->table_properties; + size_t row = context->row; + size_t column = context->column; + + if (buffer == NULL || buffer->str.data == NULL + || buffer_row >= buffer_text_visible_height(buffer)) { + return -1; + } + + size_t content_width = buffer_text_visible_width(buffer); + if (vis_width < content_width) + return -1; + + size_t left = 0; + size_t right = 0; + switch (get_cell_property_hierarchically(props, row, column, FT_CPROP_TEXT_ALIGN)) { + case FT_ALIGNED_LEFT: + left = 0; + right = (vis_width) - content_width; + break; + case FT_ALIGNED_CENTER: + left = ((vis_width) - content_width) / 2; + right = ((vis_width) - content_width) - left; + break; + case FT_ALIGNED_RIGHT: + left = (vis_width) - content_width; + right = 0; + break; + default: + assert(0); + break; + } + + size_t written = 0; + int tmp = 0; + ptrdiff_t str_it_width = 0; + const void *beg = NULL; + const void *end = NULL; + buffer_substring(buffer, buffer_row, &beg, &end, &str_it_width); + if (beg == NULL || end == NULL) + return -1; + if (str_it_width < 0 || content_width < (size_t)str_it_width) + return -1; + + size_t padding = content_width - (size_t)str_it_width; + + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, left, FT_SPACE)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, content_style_tag)); + CHCK_RSLT_ADD_TO_WRITTEN(buffer_print_range(cntx, beg, end)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, 1, reset_content_style_tag)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, padding, FT_SPACE)); + CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, right, FT_SPACE)); + return (int)written; + +clear: + return -1; +} + +FT_INTERNAL +size_t string_buffer_width_capacity(const f_string_buffer_t *buffer) +{ + assert(buffer); + switch (buffer->type) { + case CHAR_BUF: + return buffer->data_sz; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return buffer->data_sz / sizeof(wchar_t); +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return buffer->data_sz / 4; +#endif + default: + assert(0); + return 0; + } +} + + +FT_INTERNAL +void *buffer_get_data(f_string_buffer_t *buffer) +{ + assert(buffer); + return buffer->str.data; +} + +FT_INTERNAL +int buffer_check_align(f_string_buffer_t *buffer) +{ + assert(buffer); + assert(buffer->str.data); + + switch (buffer->type) { + case CHAR_BUF: + return 1; +#ifdef FT_HAVE_WCHAR + case W_CHAR_BUF: + return (((uintptr_t)buffer->str.data) & (sizeof(wchar_t) - 1)) == 0; +#endif +#ifdef FT_HAVE_UTF8 + case UTF8_BUF: + return 1; +#endif + default: + assert(0); + return 0; + } +} + +/******************************************************** + End of file "string_buffer.c" + ********************************************************/ + + +/******************************************************** + Begin of file "table.c" + ********************************************************/ + +/* #include "table.h" */ /* Commented by amalgamation script */ +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +/* #include "cell.h" */ /* Commented by amalgamation script */ +/* #include "vector.h" */ /* Commented by amalgamation script */ +/* #include "row.h" */ /* Commented by amalgamation script */ + +FT_INTERNAL +f_separator_t *create_separator(int enabled) +{ + f_separator_t *res = (f_separator_t *)F_CALLOC(1, sizeof(f_separator_t)); + if (res == NULL) + return NULL; + res->enabled = enabled; + return res; +} + + +FT_INTERNAL +void destroy_separator(f_separator_t *sep) +{ + F_FREE(sep); +} + + +FT_INTERNAL +f_separator_t *copy_separator(f_separator_t *sep) +{ + assert(sep); + return create_separator(sep->enabled); +} + + +static +f_row_t *get_row_impl(ft_table_t *table, size_t row, enum f_get_policy policy) +{ + if (table == NULL || table->rows == NULL) { + return NULL; + } + + switch (policy) { + case DONT_CREATE_ON_NULL: + if (row < vector_size(table->rows)) { + return VECTOR_AT(table->rows, row, f_row_t *); + } + return NULL; + case CREATE_ON_NULL: + while (row >= vector_size(table->rows)) { + f_row_t *new_row = create_row(); + if (new_row == NULL) + return NULL; + if (FT_IS_ERROR(vector_push(table->rows, &new_row))) { + destroy_row(new_row); + return NULL; + } + } + return VECTOR_AT(table->rows, row, f_row_t *); + } + + assert(0 && "Shouldn't be here!"); + return NULL; +} + + +FT_INTERNAL +f_row_t *get_row(ft_table_t *table, size_t row) +{ + return get_row_impl(table, row, DONT_CREATE_ON_NULL); +} + + +FT_INTERNAL +const f_row_t *get_row_c(const ft_table_t *table, size_t row) +{ + return get_row((ft_table_t *)table, row); +} + + +FT_INTERNAL +f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row) +{ + return get_row_impl(table, row, CREATE_ON_NULL); +} + +FT_INTERNAL +f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table) +{ + assert(table); + + f_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row); + if (row == NULL) + return NULL; + + f_cell_t *cell = NULL; + fort_entire_table_properties_t *table_props = &table->properties->entire_table_properties; + switch (table_props->add_strategy) { + case FT_STRATEGY_INSERT: + cell = create_cell_in_position(row, table->cur_col); + break; + case FT_STRATEGY_REPLACE: + cell = get_cell_and_create_if_not_exists(row, table->cur_col); + break; + default: + assert(0 && "Unexpected situation inside libfort"); + break; + } + + if (cell == NULL) + return NULL; + + return cell_get_string_buffer(cell); +} + + +/* + * Returns number of cells (rows * cols) + */ +FT_INTERNAL +f_status get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols) +{ + *rows = 0; + *cols = 0; + if (table && table->rows) { + *rows = vector_size(table->rows); + size_t row_index = 0; + for (row_index = 0; row_index < vector_size(table->rows); ++row_index) { + f_row_t *row = VECTOR_AT(table->rows, row_index, f_row_t *); + size_t cols_in_row = columns_in_row(row); + if (cols_in_row > *cols) + *cols = cols_in_row; + } + } + return FT_SUCCESS; +} + + +FT_INTERNAL +f_status 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) +{ + if (table == NULL) { + return FT_GEN_ERROR; + } + + size_t max_invis_codepoints = 0; + size_t cols = 0; + size_t rows = 0; + int status = get_table_sizes(table, &rows, &cols); + if (FT_IS_ERROR(status)) + return status; + + size_t *col_width_arr = (size_t *)F_CALLOC(cols, sizeof(size_t)); + size_t *row_height_arr = (size_t *)F_CALLOC(rows, sizeof(size_t)); + if (col_width_arr == NULL || row_height_arr == NULL) { + F_FREE(col_width_arr); + F_FREE(row_height_arr); + return FT_GEN_ERROR; + } + + int combined_cells_found = 0; + f_context_t context; + context.table_properties = (table->properties ? table->properties : &g_table_properties); + size_t col = 0; + for (col = 0; col < cols; ++col) { + col_width_arr[col] = 0; + size_t row = 0; + for (row = 0; row < rows; ++row) { + const f_row_t *row_p = get_row_c(table, row); + const f_cell_t *cell = get_cell_c(row_p, col); + context.column = col; + context.row = row; + if (cell) { + switch (get_cell_type(cell)) { + case COMMON_CELL: + col_width_arr[col] = MAX(col_width_arr[col], cell_vis_width(cell, &context)); + break; + case GROUP_MASTER_CELL: + combined_cells_found = 1; + break; + case GROUP_SLAVE_CELL: + ; /* Do nothing */ + break; + } + row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, &context)); + } else { + size_t cell_empty_string_height = get_cell_property_hierarchically(context.table_properties, context.row, context.column, FT_CPROP_EMPTY_STR_HEIGHT); + if (cell_empty_string_height) { + size_t cell_top_padding = get_cell_property_hierarchically(context.table_properties, context.row, context.column, FT_CPROP_TOP_PADDING); + size_t cell_bottom_padding = get_cell_property_hierarchically(context.table_properties, context.row, context.column, FT_CPROP_BOTTOM_PADDING); + row_height_arr[row] = MAX(row_height_arr[row], cell_empty_string_height + cell_top_padding + cell_bottom_padding); + } + } + } + + if (geom == INTERN_REPR_GEOMETRY) { + max_invis_codepoints = 0; + for (row = 0; row < rows; ++row) { + const f_row_t *row_p = get_row_c(table, row); + const f_cell_t *cell = get_cell_c(row_p, col); + if (!cell) + continue; + context.column = col; + context.row = row; + size_t inv_codepoints = cell_invis_codes_width(cell, &context); + max_invis_codepoints = MAX(max_invis_codepoints, inv_codepoints); + } + col_width_arr[col] += max_invis_codepoints; + } + } + + if (combined_cells_found) { + for (col = 0; col < cols; ++col) { + size_t row = 0; + for (row = 0; row < rows; ++row) { + const f_row_t *row_p = get_row_c(table, row); + const f_cell_t *cell = get_cell_c(row_p, col); + context.column = col; + context.row = row; + if (cell) { + if (get_cell_type(cell) == GROUP_MASTER_CELL) { + size_t hint_width = cell_vis_width(cell, &context); + if (geom == INTERN_REPR_GEOMETRY) { + hint_width += cell_invis_codes_width(cell, &context); + } + size_t slave_col = col + group_cell_number(row_p, col); + size_t cur_adj_col = col; + size_t group_width = col_width_arr[col]; + size_t i; + for (i = col + 1; i < slave_col; ++i) + group_width += col_width_arr[i] + FORT_COL_SEPARATOR_LENGTH; + /* adjust col. widths */ + while (1) { + if (group_width >= hint_width) + break; + col_width_arr[cur_adj_col] += 1; + group_width++; + cur_adj_col++; + if (cur_adj_col == slave_col) + cur_adj_col = col; + } + } + } + } + } + } + + /* todo: Maybe it is better to move min width checking to a particular cell + * width checking. At the moment min width includes paddings. Maybe it is + * better that min width weren't include paddings but be min width of the + * cell content without padding + */ + /* + if (table->properties) { + for (size_t i = 0; i < cols; ++i) { + col_width_arr[i] = MAX((int)col_width_arr[i], fort_props_column_width(table->properties, i)); + } + } + */ + + *col_width_arr_p = col_width_arr; + *col_width_arr_sz = cols; + *row_height_arr_p = row_height_arr; + *row_height_arr_sz = rows; + return FT_SUCCESS; +} + + +/* + * Returns geometry in characters + */ +FT_INTERNAL +f_status table_geometry(const ft_table_t *table, size_t *height, size_t *width) +{ + if (table == NULL) + return FT_GEN_ERROR; + + *height = 0; + *width = 0; + size_t cols = 0; + size_t rows = 0; + size_t *col_width_arr = NULL; + size_t *row_height_arr = NULL; + + int status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows, INTERN_REPR_GEOMETRY); + if (FT_IS_ERROR(status)) + return status; + + *width = 1 + (cols == 0 ? 1 : cols) + 1; /* for boundaries (that take 1 symbol) + newline */ + size_t i = 0; + for (i = 0; i < cols; ++i) { + *width += col_width_arr[i]; + } + + /* todo: add check for non printable horizontal row separators */ + *height = 1 + (rows == 0 ? 1 : rows); /* for boundaries (that take 1 symbol) */ + for (i = 0; i < rows; ++i) { + *height += row_height_arr[i]; + } + F_FREE(col_width_arr); + F_FREE(row_height_arr); + + f_table_properties_t *properties = table->properties; + if (properties) { + *height += properties->entire_table_properties.top_margin; + *height += properties->entire_table_properties.bottom_margin; + *width += properties->entire_table_properties.left_margin; + *width += properties->entire_table_properties.right_margin; + } + + /* Take into account that border elements can be more than one byte long */ + f_table_properties_t *table_properties = properties ? properties : &g_table_properties; + size_t max_border_elem_len = max_border_elem_strlen(table_properties); + *width *= max_border_elem_len; + + return FT_SUCCESS; +} + +FT_INTERNAL +f_status table_internal_codepoints_geometry(const ft_table_t *table, size_t *height, size_t *width) +{ + return table_geometry(table, height, width); +} + +/******************************************************** + End of file "table.c" + ********************************************************/ + + +/******************************************************** + Begin of file "vector.c" + ********************************************************/ + +/* #include "vector.h" */ /* Commented by amalgamation script */ +#include <assert.h> +#include <string.h> + +struct f_vector { + size_t m_size; + void *m_data; + size_t m_capacity; + size_t m_item_size; +}; + + +static int vector_reallocate_(f_vector_t *vector, size_t new_capacity) +{ + assert(vector); + assert(new_capacity > vector->m_capacity); + + size_t new_size = new_capacity * vector->m_item_size; + vector->m_data = F_REALLOC(vector->m_data, new_size); + if (vector->m_data == NULL) + return -1; + return 0; +} + + +FT_INTERNAL +f_vector_t *create_vector(size_t item_size, size_t capacity) +{ + f_vector_t *vector = (f_vector_t *)F_MALLOC(sizeof(f_vector_t)); + if (vector == NULL) { + return NULL; + } + + size_t init_size = MAX(item_size * capacity, 1); + vector->m_data = F_MALLOC(init_size); + if (vector->m_data == NULL) { + F_FREE(vector); + return NULL; + } + + vector->m_size = 0; + vector->m_capacity = capacity; + vector->m_item_size = item_size; + + return vector; +} + + +FT_INTERNAL +void destroy_vector(f_vector_t *vector) +{ + assert(vector); + F_FREE(vector->m_data); + F_FREE(vector); +} + + +FT_INTERNAL +size_t vector_size(const f_vector_t *vector) +{ + assert(vector); + return vector->m_size; +} + + +FT_INTERNAL +size_t vector_capacity(const f_vector_t *vector) +{ + assert(vector); + return vector->m_capacity; +} + + +FT_INTERNAL +int vector_push(f_vector_t *vector, const void *item) +{ + assert(vector); + assert(item); + + if (vector->m_size == vector->m_capacity) { + if (vector_reallocate_(vector, vector->m_capacity * 2) == -1) + return FT_GEN_ERROR; + vector->m_capacity = vector->m_capacity * 2; + } + + size_t offset = vector->m_size * vector->m_item_size; + memcpy((char *)vector->m_data + offset, item, vector->m_item_size); + + ++(vector->m_size); + + return FT_SUCCESS; +} + +FT_INTERNAL +int vector_insert(f_vector_t *vector, const void *item, size_t pos) +{ + assert(vector); + assert(item); + size_t needed_capacity = MAX(pos + 1, vector->m_size + 1); + if (vector->m_capacity < needed_capacity) { + if (vector_reallocate_(vector, needed_capacity) == -1) + return FT_GEN_ERROR; + vector->m_capacity = needed_capacity; + } + size_t offset = pos * vector->m_item_size; + if (pos >= vector->m_size) { + /* Data in the middle are not initialized */ + memcpy((char *)vector->m_data + offset, item, vector->m_item_size); + vector->m_size = pos + 1; + return FT_SUCCESS; + } else { + /* Shift following data by one position */ + memmove((char *)vector->m_data + offset + vector->m_item_size, + (char *)vector->m_data + offset, + vector->m_item_size * (vector->m_size - pos)); + memcpy((char *)vector->m_data + offset, item, vector->m_item_size); + ++(vector->m_size); + return FT_SUCCESS; + } +} + +FT_INTERNAL +f_vector_t *vector_split(f_vector_t *vector, size_t pos) +{ + size_t trailing_sz = vector->m_size > pos ? vector->m_size - pos : 0; + f_vector_t *new_vector = create_vector(vector->m_item_size, trailing_sz); + if (!new_vector) + return new_vector; + if (new_vector->m_capacity < trailing_sz) { + destroy_vector(new_vector); + return NULL; + } + + if (trailing_sz == 0) + return new_vector; + + size_t offset = vector->m_item_size * pos; + memcpy(new_vector->m_data, (char *)vector->m_data + offset, + trailing_sz * vector->m_item_size); + new_vector->m_size = trailing_sz; + vector->m_size = pos; + return new_vector; +} + +FT_INTERNAL +const void *vector_at_c(const f_vector_t *vector, size_t index) +{ + if (index >= vector->m_size) + return NULL; + + return (char *)vector->m_data + index * vector->m_item_size; +} + + +FT_INTERNAL +void *vector_at(f_vector_t *vector, size_t index) +{ + if (index >= vector->m_size) + return NULL; + + return (char *)vector->m_data + index * vector->m_item_size; +} + + +FT_INTERNAL +f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos) +{ + assert(cur_vec); + assert(mv_vec); + assert(cur_vec != mv_vec); + assert(cur_vec->m_item_size == mv_vec->m_item_size); + + size_t cur_sz = vector_size(cur_vec); + size_t mv_sz = vector_size(mv_vec); + if (mv_sz == 0) { + return FT_SUCCESS; + } + + size_t min_targ_size = pos + mv_sz; + if (vector_capacity(cur_vec) < min_targ_size) { + if (vector_reallocate_(cur_vec, min_targ_size) == -1) + return FT_GEN_ERROR; + cur_vec->m_capacity = min_targ_size; + } + + size_t offset = pos * cur_vec->m_item_size; + void *tmp = NULL; + size_t new_mv_sz = 0; + if (cur_sz > pos) { + new_mv_sz = MIN(cur_sz - pos, mv_sz); + tmp = F_MALLOC(cur_vec->m_item_size * new_mv_sz); + if (tmp == NULL) { + return FT_MEMORY_ERROR; + } + } + + if (tmp) { + memcpy(tmp, + (char *)cur_vec->m_data + offset, + cur_vec->m_item_size * new_mv_sz); + } + + memcpy((char *)cur_vec->m_data + offset, + mv_vec->m_data, + cur_vec->m_item_size * mv_sz); + + if (tmp) { + memcpy(mv_vec->m_data, + tmp, + cur_vec->m_item_size * new_mv_sz); + } + + cur_vec->m_size = MAX(cur_vec->m_size, min_targ_size); + mv_vec->m_size = new_mv_sz; + F_FREE(tmp); + return FT_SUCCESS; +} + +FT_INTERNAL +void vector_clear(f_vector_t *vector) +{ + vector->m_size = 0; +} + +FT_INTERNAL +int vector_erase(f_vector_t *vector, size_t index) +{ + assert(vector); + + if (vector->m_size == 0 || index >= vector->m_size) + return FT_GEN_ERROR; + + memmove((char *)vector->m_data + vector->m_item_size * index, + (char *)vector->m_data + vector->m_item_size * (index + 1), + (vector->m_size - 1 - index) * vector->m_item_size); + vector->m_size--; + return FT_SUCCESS; +} + +#ifdef FT_TEST_BUILD + +f_vector_t *copy_vector(f_vector_t *v) +{ + if (v == NULL) + return NULL; + + f_vector_t *new_vector = create_vector(v->m_item_size, v->m_capacity); + if (new_vector == NULL) + return NULL; + + memcpy(new_vector->m_data, v->m_data, v->m_item_size * v->m_size); + new_vector->m_size = v->m_size ; + new_vector->m_item_size = v->m_item_size ; + return new_vector; +} + +size_t vector_index_of(const f_vector_t *vector, const void *item) +{ + assert(vector); + assert(item); + + size_t i = 0; + for (i = 0; i < vector->m_size; ++i) { + void *data_pos = (char *)vector->m_data + i * vector->m_item_size; + if (memcmp(data_pos, item, vector->m_item_size) == 0) { + return i; + } + } + return INVALID_VEC_INDEX; +} + +#endif + +/******************************************************** + End of file "vector.c" + ********************************************************/ + + +/******************************************************** + Begin of file "wcwidth.c" + ********************************************************/ + +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +/* #include "wcwidth.h" */ /* Commented by amalgamation script */ + +#ifdef FT_HAVE_WCHAR + + +struct interval { + int32_t first; + int32_t last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(int32_t ucs, const struct interval *table, int max) +{ + int min = 0; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + int mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static int mk_wcwidth(wchar_t wcs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* We convert wchar_t to int32_t to avoid compiler warnings + * about implicit integer conversions + * https://github.com/seleznevae/libfort/issues/20 + * + * note: didn't test if we can do it + */ + int32_t ucs = (int32_t)wcs; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + + +FT_INTERNAL +int mk_wcswidth(const wchar_t *pwcs, size_t n) +{ + int width = 0; + + for (; *pwcs && n-- > 0; pwcs++) { + int w; + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + } + + return width; +} +#endif /* FT_HAVE_WCHAR */ + +/******************************************************** + End of file "wcwidth.c" + ********************************************************/ + diff --git a/fort.h b/fort.h @@ -0,0 +1,1057 @@ +/* +libfort + +MIT License + +Copyright (c) 2017 - 2020 Seleznev Anton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * @file fort.h + * @brief Main header file describing libfort API. + * + * This file contains declarations of all libfort functions and macro + * definitions. + */ + +#ifndef LIBFORT_H +#define LIBFORT_H + +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> + +/***************************************************************************** + * VERSION + *****************************************************************************/ + +#define LIBFORT_MAJOR_VERSION 0 +#define LIBFORT_MINOR_VERSION 5 +#define LIBFORT_REVISION 0 +#define LIBFORT_VERSION_STR "0.5.0" + + +/***************************************************************************** + * Configuration + *****************************************************************************/ + +/** + * libfort configuration macros + * (to disable wchar_t/UTF-8 support this macros should be defined) + */ +/** #define FT_CONGIG_DISABLE_WCHAR */ +/** #define FT_CONGIG_DISABLE_UTF8 */ + +#if !defined(FT_CONGIG_DISABLE_WCHAR) +#define FT_HAVE_WCHAR +#endif + +#if !defined(FT_CONGIG_DISABLE_UTF8) +#define FT_HAVE_UTF8 +#endif + + +/***************************************************************************** + * RETURN CODES + *****************************************************************************/ + +/** + * Operation successfully ended. + */ +#define FT_SUCCESS 0 + +/** + * Memory allocation failed. + */ +#define FT_MEMORY_ERROR -1 + +/** + * Invalid argument. + */ +#define FT_EINVAL -2 + +/** + * Libfort internal logic error. + * + * Usually such errors mean that something is wrong in + * libfort internal logic and in most of cases cause of + * these errors is a library bug. + */ +#define FT_INTERN_ERROR -3 + +/** + * General error. + * + * Different errors that do not belong to the group of errors + * mentioned above. + */ +#define FT_GEN_ERROR -4 + + +#define FT_IS_SUCCESS(arg) ((arg) >= 0) +#define FT_IS_ERROR(arg) ((arg) < 0) + + + + +/** + * @cond HELPER_MACROS + */ + +/***************************************************************************** + * Determine compiler + *****************************************************************************/ + +#if defined(__clang__) +#define FT_CLANG_COMPILER +#elif defined(__GNUC__) +#define FT_GCC_COMPILER +#elif defined(_MSC_VER) +#define FT_MICROSOFT_COMPILER +#else +#define FT_UNDEFINED_COMPILER +#endif + + +/***************************************************************************** + * Declare inline + *****************************************************************************/ + +#if defined(__cplusplus) +#define FT_INLINE inline +#else +#define FT_INLINE __inline +#endif /* if defined(__cplusplus) */ + + +/***************************************************************************** + * C++ needs to know that types and declarations are C, not C++. + *****************************************************************************/ + +#ifdef __cplusplus +# define FT_BEGIN_DECLS extern "C" { +# define FT_END_DECLS } +#else +# define FT_BEGIN_DECLS +# define FT_END_DECLS +#endif + + +/***************************************************************************** + * Helper macros + *****************************************************************************/ + +#define FT_STR_2_CAT_(arg1, arg2) \ + arg1##arg2 +#define FT_STR_2_CAT(arg1, arg2) \ + FT_STR_2_CAT_(arg1, arg2) + +/** + * @interanl + */ +static FT_INLINE int ft_check_if_string_helper(const char *str) +{ + (void)str; + return 0; +} + +/** + * @interanl + */ +static FT_INLINE int ft_check_if_wstring_helper(const wchar_t *str) +{ + (void)str; + return 0; +} + +#define FT_NARGS_IMPL_(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,N,...) N +#define FT_EXPAND_(x) x +#define FT_PP_NARG_(...) \ + FT_EXPAND_(FT_NARGS_IMPL_(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) + +#define FT_CHECK_IF_STR_32(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_31(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_31(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_30(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_30(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_29(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_29(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_28(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_28(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_27(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_27(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_26(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_26(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_25(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_25(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_24(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_24(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_23(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_23(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_22(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_22(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_21(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_21(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_20(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_20(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_19(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_19(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_18(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_18(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_17(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_17(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_16(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_16(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_15(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_15(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_14(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_14(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_13(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_13(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_12(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_12(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_11(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_11(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_10(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_10(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_9(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_9(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_8(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_8(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_7(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_7(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_6(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_6(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_5(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_5(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_4(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_4(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_3(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_3(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_2(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_2(checker,arg,...) (checker(arg),FT_EXPAND_(FT_CHECK_IF_STR_1(checker,__VA_ARGS__))) +#define FT_CHECK_IF_STR_1(checker,arg) (checker(arg)) + +#define FT_CHECK_IF_ARGS_ARE_STRINGS__(checker,func, ...) \ + FT_EXPAND_(func(checker,__VA_ARGS__)) +#define FT_CHECK_IF_ARGS_ARE_STRINGS_(checker,basis, n, ...) \ + FT_CHECK_IF_ARGS_ARE_STRINGS__(checker,FT_STR_2_CAT_(basis, n), __VA_ARGS__) +#define FT_CHECK_IF_ARGS_ARE_STRINGS(...) \ + FT_CHECK_IF_ARGS_ARE_STRINGS_(ft_check_if_string_helper,FT_CHECK_IF_STR_,FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__) + +#ifdef FT_HAVE_WCHAR +#define CHECK_IF_ARGS_ARE_WSTRINGS(...) \ + FT_CHECK_IF_ARGS_ARE_STRINGS_(ft_check_if_wstring_helper,FT_CHECK_IF_STR_,FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__) +#endif + +/** + * @endcond + */ + + +/***************************************************************************** + * Attribute format for argument checking + *****************************************************************************/ + +#if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) +#define FT_PRINTF_ATTRIBUTE_FORMAT(string_index, first_to_check) \ + __attribute__ ((format (printf, string_index, first_to_check))) +#else +#define FT_PRINTF_ATTRIBUTE_FORMAT(string_index, first_to_check) +#endif /* defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) */ + + +/***************************************************************************** + * libfort API + *****************************************************************************/ + +FT_BEGIN_DECLS + +/** + * The main structure of libfort containing information about formatted table. + */ +struct ft_table; + +/** + * The main structure of libfort containing information about formatted table. + * + * ft_table_t objects should be created by a call to ft_create_table and + * destroyed with ft_destroy_table. + */ +typedef struct ft_table ft_table_t; + +/** + * Create formatted table. + * + * @return + * The pointer to the new allocated ft_table_t, on success. NULL on error. + */ +ft_table_t *ft_create_table(void); + +/** + * Destroy formatted table. + * + * Destroy formatted table and free all resources allocated during table creation + * and work with it. + * + * @param table + * Pointer to formatted table previousley created with ft_create_table. If + * table is a null pointer, the function does nothing. + */ +void ft_destroy_table(ft_table_t *table); + +/** + * Copy formatted table. + * + * @param table + * Pointer to formatted table previousley created with ft_create_table. If + * table is a null pointer, the function returns null. + * @return + * The pointer to the new allocated ft_table_t, on success. NULL on error. + */ +ft_table_t *ft_copy_table(ft_table_t *table); + +/** + * Move current position to the first cell of the next line(row). + * + * @param table + * Pointer to formatted table. + * @return + * - 0: Success; data were written + * - (<0): In case of error. + * @note + * This function can fail only in case FT_STRATEGY_INSERT adding strategy + * was set for the table. + */ +int ft_ln(ft_table_t *table); + +/** + * Get row number of the current cell. + * + * @param table + * Pointer to formatted table. + * @return + * Row number of the current cell. + */ +size_t ft_cur_row(const ft_table_t *table); + +/** + * Get column number of the current cell. + * + * @param table + * Pointer to formatted table. + * @return + * Column number of the current cell. + */ +size_t ft_cur_col(const ft_table_t *table); + +/** + * Set current cell position. + * + * Current cell - cell that will be edited with all modifiing functions + * (ft_printf, ft_write ...). + * + * @param table + * Pointer to formatted table. + * @param row + * New row number for the current cell. + * @param col + * New row number for the current cell. + */ +void ft_set_cur_cell(ft_table_t *table, size_t row, size_t col); + +/** + * Check if table is empty. + * + * @param table + * Pointer to the table. + * @return + * 1 - table is empty + * 0 - some data has been inserted + */ +int ft_is_empty(const ft_table_t *table); + +/** + * Get number of rows in the table. + * + * @param table + * Pointer to formatted table. + * @return + * Number of rows in the table. + */ +size_t ft_row_count(const ft_table_t *table); + +/** + * Get number of columns in the table. + * + * @param table + * Pointer to formatted table. + * @return + * Number of columns in the table. + */ +size_t ft_col_count(const ft_table_t *table); + +/** + * Erase range of cells. + * + * Range of cells is determined by 2 points (top-left and bottom-right) (both + * ends are included). + * + * @param table + * Pointer to formatted table. + * @param top_left_row + * Row number of the top left cell in the range. + * @param top_left_col + * Column number of the top left cell in the range. + * @param bottom_right_row + * Row number of the bottom right cell in the range. + * @param bottom_right_col + * Column number of the bottom right cell in the range. + * @return + * - 0 - Operation was successfully implemented + * - (<0): In case of error + */ +int 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); + +#if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) + +/** + * Write data formatted according to the format string to a variety of table + * cells. + * + * @param table + * Pointer to formatted table. + * @param fmt + * Pointer to a null-terminated multibyte string specifying how to interpret + * the data. The format string consists of ordinary characters (except % and |), + * which are copied unchanged into the output stream, and conversion + * specifications. Conversion specifications are the same as for standard + * printf function. Character '|' (which can be changed with + * {@link ft_set_default_printf_field_separator}) in the format string is treated as + * a cell separator. + * @param ... + * Arguments specifying data to print. Similarly to standard printf-like + * functions if any argument after default conversions is not the type + * expected by the corresponding conversion specifier, or if there are fewer + * arguments than required by format, the behavior is undefined. If there are + * more arguments than required by format, the extraneous arguments are + * evaluated and ignored. + * @return + * - Number of printed cells + * - (<0): In case of error + */ +int ft_printf(ft_table_t *table, const char *fmt, ...) FT_PRINTF_ATTRIBUTE_FORMAT(2, 3); + +/** + * Write data formatted according to the format string to a variety of table + * cells and move current position to the first cell of the next line(row). + * + * @param table + * Pointer to formatted table. + * @param fmt + * Pointer to a null-terminated multibyte string specifying how to interpret + * the data. The format string consists of ordinary characters (except % and |), + * which are copied unchanged into the output stream, and conversion + * specifications. Conversion specifications are the same as for standard + * printf function. Character '|' (which can be changed with + * {@link ft_set_default_printf_field_separator}) in the format string is treated as + * a cell separator. + * @param ... + * Arguments specifying data to print. Similarly to standard printf-like + * functions if any argument after default conversions is not the type + * expected by the corresponding conversion specifier, or if there are fewer + * arguments than required by format, the behavior is undefined. If there are + * more arguments than required by format, the extraneous arguments are + * evaluated and ignored. + * @return + * - Number of printed cells. + * - (<0): In case of error. + */ +int ft_printf_ln(ft_table_t *table, const char *fmt, ...) FT_PRINTF_ATTRIBUTE_FORMAT(2, 3); + +#else + +/** + * @cond IGNORE_DOC + */ + +int ft_printf_impl(ft_table_t *table, const char *fmt, ...) FT_PRINTF_ATTRIBUTE_FORMAT(2, 3); +int ft_printf_ln_impl(ft_table_t *table, const char *fmt, ...) FT_PRINTF_ATTRIBUTE_FORMAT(2, 3); + +#define ft_printf(table, ...) \ + (( 0 ? fprintf(stderr, __VA_ARGS__) : 1), ft_printf_impl(table, __VA_ARGS__)) +#define ft_printf_ln(table, ...) \ + (( 0 ? fprintf(stderr, __VA_ARGS__) : 1), ft_printf_ln_impl(table, __VA_ARGS__)) + +/** + * @endcond + */ +#endif + +/** + * Set field separator for {@link ft_printf}, {@link ft_printf_ln} + * (default separator is '|'). + * + * @param separator + * New separator. + */ +void ft_set_default_printf_field_separator(char separator); + + +/** + * Write strings to the table. + * + * Write specified strings to the same number of consecutive cells in the + * current row. + * + * @param table + * Pointer to formatted table. + * @param ... + * Strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +#define ft_write(table, ...)\ + (0 ? FT_CHECK_IF_ARGS_ARE_STRINGS(__VA_ARGS__) : ft_nwrite(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) + +/** + * Write strings to the table and go to the next line. + * + * Write specified strings to the same number of consecutive cells in the + * current row and move current position to the first cell of the next + * line(row). + * + * @param table + * Pointer to formatted table. + * @param ... + * Strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +#define ft_write_ln(table, ...)\ + (0 ? FT_CHECK_IF_ARGS_ARE_STRINGS(__VA_ARGS__) : ft_nwrite_ln(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) + +/** + * Write specified number of strings to the table. + * + * Write specified number of strings to the same number of consecutive cells in + * the current row. + * + * @note In most cases it is more preferable to use MACRO @ref ft_write instead + * of @ref ft_nwrite, which is more safe (@ref ft_write automatically counts the + * number of string arguments and at compile check that all passed arguments are + * strings). + * + * @param table + * Pointer to formatted table. + * @param count + * Number of strings to write. + * @param cell_content + * First string to write. + * @param ... + * Other strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +int ft_nwrite(ft_table_t *table, size_t count, const char *cell_content, ...); + +/** + * Write specified number of strings to the table and go to the next line. + * + * Write specified number of strings to the same number of consecutive cells + * in the current row and move current position to the first cell of the next + * line(row). + * + * @note In most cases it is more preferable to use MACRO @ref ft_write instead + * of @ref ft_nwrite, which is more safe (@ref ft_write automatically counts the + * number of string arguments and at compile check that all passed arguments are + * strings). + * + * @param table + * Pointer to formatted table. + * @param count + * Number of strings to write. + * @param cell_content + * First string to write. + * @param ... + * Other strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +int ft_nwrite_ln(ft_table_t *table, size_t count, const char *cell_content, ...); + + + +/** + * Write strings from the array to the table. + * + * Write specified number of strings from the array to the same number of + * consecutive cells in the current row. + * + * @param table + * Pointer to formatted table. + * @param cols + * Number of elements in row_cells. + * @param row_cells + * Array of strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +int ft_row_write(ft_table_t *table, size_t cols, const char *row_cells[]); + +/** + * Write strings from the array to the table and go to the next line. + * + * Write specified number of strings from the array to the same number of + * consecutive cells in the current row and move current position to the first + * cell of the next line(row). + * + * @param table + * Pointer to formatted table. + * @param cols + * Number of elements in row_cells. + * @param row_cells + * Array of strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +int ft_row_write_ln(ft_table_t *table, size_t cols, const char *row_cells[]); + + +/** + * Write strings from the 2D array to the table. + * + * Write specified number of strings from the 2D array to the formatted table. + * + * @param table + * Pointer to formatted table. + * @param rows + * Number of rows in the 2D array. + * @param cols + * Number of columns in the 2D array. + * @param table_cells + * 2D array of strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +int ft_table_write(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]); + +/** + * Write strings from the 2D array to the table and go to the next line. + * + * Write specified number of strings from the 2D array to the formatted table + * and move current position to the first cell of the next line(row). + * + * @param table + * Pointer to formatted table. + * @param rows + * Number of rows in the 2D array. + * @param cols + * Number of columns in the 2D array. + * @param table_cells + * 2D array of strings to write. + * @return + * - 0: Success; data were written + * - (<0): In case of error + */ +int ft_table_write_ln(ft_table_t *table, size_t rows, size_t cols, const char *table_cells[]); + + +/** + * Add separator after the current row. + * + * @param table + * Formatted table. + * @return + * - 0: Success; separator was added. + * - (<0): In case of error + */ +int ft_add_separator(ft_table_t *table); + + +/** + * Convert table to string representation. + * + * ft_table_t has ownership of the returned pointer. So there is no need to + * free it. To take ownership user should explicitly copy the returned + * string with strdup or similar functions. + * + * Returned pointer may be later invalidated by: + * - Calling ft_destroy_table; + * - Other invocations of ft_to_string. + * + * @param table + * Formatted table. + * @return + * - The pointer to the string representation of formatted table, on success. + * - NULL on error. + */ +const char *ft_to_string(const ft_table_t *table); + + + + + + + +/** + * Structure describing border appearance. + */ +struct ft_border_chars { + const char *top_border_ch; + const char *separator_ch; + const char *bottom_border_ch; + const char *side_border_ch; + const char *out_intersect_ch; + const char *in_intersect_ch; +}; + +/** + * Structure describing border style. + */ +struct ft_border_style { + struct ft_border_chars border_chs; + struct ft_border_chars header_border_chs; + const char *hor_separator_char; +}; + +/** + * @defgroup BasicStyles + * @name Built-in table border styles. + * @note Built-in border styles (FT_BASIC_STYLE, FT_BASIC2_STYLE ...) can be + * used as arguments for @ref ft_set_border_style and + * @ref ft_set_default_border_style, but their fields shouldn't be accessed + * directly because implementation doesn't guarantee that these objects are + * properly initialized. + * @{ + */ +extern const struct ft_border_style *const FT_BASIC_STYLE; +extern const struct ft_border_style *const FT_BASIC2_STYLE; +extern const struct ft_border_style *const FT_SIMPLE_STYLE; +extern const struct ft_border_style *const FT_PLAIN_STYLE; +extern const struct ft_border_style *const FT_DOT_STYLE; +extern const struct ft_border_style *const FT_EMPTY_STYLE; +extern const struct ft_border_style *const FT_EMPTY2_STYLE; +extern const struct ft_border_style *const FT_SOLID_STYLE; +extern const struct ft_border_style *const FT_SOLID_ROUND_STYLE; +extern const struct ft_border_style *const FT_NICE_STYLE; +extern const struct ft_border_style *const FT_DOUBLE_STYLE; +extern const struct ft_border_style *const FT_DOUBLE2_STYLE; +extern const struct ft_border_style *const FT_BOLD_STYLE; +extern const struct ft_border_style *const FT_BOLD2_STYLE; +extern const struct ft_border_style *const FT_FRAME_STYLE; +/** @} */ + + + +/** + * Set default border style for all new formatted tables. + * + * @param style + * Pointer to border style. + * @return + * - 0: Success; default border style was changed. + * - (<0): In case of error + */ +int ft_set_default_border_style(const struct ft_border_style *style); + +/** + * Set border style for the table. + * + * @param table + * A pointer to the ft_table_t structure. + * @param style + * Pointer to border style. + * @return + * - 0: Success; table border style was changed. + * - (<0): In case of error + */ +int ft_set_border_style(ft_table_t *table, const struct ft_border_style *style); + + + +/** + * @name Special macros to define cell position (row and column). + * @{ + */ +#define FT_ANY_COLUMN (UINT_MAX) /**< Any column (can be used to refer to all cells in a row)*/ +#define FT_CUR_COLUMN (UINT_MAX - 1) /**< Current column */ +#define FT_ANY_ROW (UINT_MAX) /**< Any row (can be used to refer to all cells in a column)*/ +#define FT_CUR_ROW (UINT_MAX - 1) /**< Current row */ +/** @} */ + +#define FT_MAX_ROW_INDEX (UINT_MAX - 2) +#define FT_MAX_COL_INDEX (UINT_MAX - 2) + + +/** + * @name Cell properties identifiers. + * @{ + */ +#define FT_CPROP_MIN_WIDTH (0x01U << 0) /**< Minimum width */ +#define FT_CPROP_TEXT_ALIGN (0x01U << 1) /**< Text alignment */ +#define FT_CPROP_TOP_PADDING (0x01U << 2) /**< Top padding for cell content */ +#define FT_CPROP_BOTTOM_PADDING (0x01U << 3) /**< Bottom padding for cell content */ +#define FT_CPROP_LEFT_PADDING (0x01U << 4) /**< Left padding for cell content */ +#define FT_CPROP_RIGHT_PADDING (0x01U << 5) /**< Right padding for cell content */ +#define FT_CPROP_EMPTY_STR_HEIGHT (0x01U << 6) /**< Height of empty cell */ +#define FT_CPROP_ROW_TYPE (0x01U << 7) /**< Row type */ +#define FT_CPROP_CONT_FG_COLOR (0x01U << 8) /**< Cell content foreground text color */ +#define FT_CPROP_CELL_BG_COLOR (0x01U << 9) /**< Cell background color */ +#define FT_CPROP_CONT_BG_COLOR (0x01U << 10) /**< Cell content background color */ +#define FT_CPROP_CELL_TEXT_STYLE (0x01U << 11) /**< Cell text style */ +#define FT_CPROP_CONT_TEXT_STYLE (0x01U << 12) /**< Cell content text style */ +#define FT_CPROP_CELL_BG_RGBCOLOR (0x01U << 13) /**< Cell background color */ +/** @} */ + + +/** + * Colors. + */ +enum ft_color { + FT_COLOR_DEFAULT = 0, /**< Default color */ + FT_COLOR_BLACK = 1, /**< Black color*/ + FT_COLOR_RED = 2, /**< Red color */ + FT_COLOR_GREEN = 3, /**< Green color */ + FT_COLOR_YELLOW = 4, /**< Yellow color */ + FT_COLOR_BLUE = 5, /**< Blue color */ + FT_COLOR_MAGENTA = 6, /**< Magenta color */ + FT_COLOR_CYAN = 7, /**< Cyan color */ + FT_COLOR_LIGHT_GRAY = 8, /**< Light gray color */ + FT_COLOR_DARK_GRAY = 9, /**< Dark gray color */ + FT_COLOR_LIGHT_RED = 10, /**< Light red color */ + FT_COLOR_LIGHT_GREEN = 11, /**< Light green color */ + FT_COLOR_LIGHT_YELLOW = 12, /**< Light yellow color */ + FT_COLOR_LIGHT_BLUE = 13, /**< Light blue color */ + FT_COLOR_LIGHT_MAGENTA = 14, /**< Light magenta color */ + FT_COLOR_LIGHT_CYAN = 15, /**< Light cyan color */ + FT_COLOR_LIGHT_WHYTE = 16 /**< Light whyte color */ +}; + +/** + * Text styles. + */ +enum ft_text_style { + FT_TSTYLE_DEFAULT = (1U << 0), /**< Default style */ + FT_TSTYLE_BOLD = (1U << 1), /**< Bold */ + FT_TSTYLE_DIM = (1U << 2), /**< Dim */ + FT_TSTYLE_ITALIC = (1U << 3), /**< Italic */ + FT_TSTYLE_UNDERLINED = (1U << 4), /**< Underlined */ + FT_TSTYLE_BLINK = (1U << 5), /**< Blink */ + FT_TSTYLE_INVERTED = (1U << 6), /**< Reverse (invert the foreground and background colors) */ + FT_TSTYLE_HIDDEN = (1U << 7) /**< Hidden (useful for passwords) */ +}; + + +/** + * Alignment of cell content. + */ +enum ft_text_alignment { + FT_ALIGNED_LEFT = 0, /**< Align left */ + FT_ALIGNED_CENTER, /**< Align center */ + FT_ALIGNED_RIGHT /**< Align right */ +}; + +/** + * Type of table row. Determines appearance of row. + */ +enum ft_row_type { + FT_ROW_COMMON = 0, /**< Common row */ + FT_ROW_HEADER /**< Header row */ +}; + +/** + * Set default cell property for all new formatted tables. + * + * @param property + * Cell property identifier. + * @param value + * Cell property value. + * @return + * - 0: Success; default cell property was changed. + * - (<0): In case of error + */ +int ft_set_default_cell_prop(uint32_t property, int value); + +/** + * Set property for the specified cell of the table. + * + * @param table + * A pointer to the ft_table_t structure. + * @param row + * Cell row. + * @param col + * Cell column. + * @param property + * Cell property identifier. + * @param value + * Cell property value. + * @return + * - 0: Success; cell property was changed. + * - (<0): In case of error + */ +int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t property, int value); + + +/** + * @name Table properties identifiers. + * @{ + */ +#define FT_TPROP_LEFT_MARGIN (0x01U << 0) +#define FT_TPROP_TOP_MARGIN (0x01U << 1) +#define FT_TPROP_RIGHT_MARGIN (0x01U << 2) +#define FT_TPROP_BOTTOM_MARGIN (0x01U << 3) +#define FT_TPROP_ADDING_STRATEGY (0x01U << 4) +/** @} */ + +/** + * Adding strategy. + * + * Determines what happens with old content if current cell is not empty after + * adding data to it. Default strategy is FT_STRATEGY_REPLACE. + */ +enum ft_adding_strategy { + FT_STRATEGY_REPLACE = 0, /**< Replace old content. */ + FT_STRATEGY_INSERT /**< Insert new conten. Old content is shifted. */ +}; + + +/** + * Set default table property. + * + * @param property + * Table property identifier. + * @param value + * Table property value. + * @return + * - 0: Success; default table property was changed. + * - (<0): In case of error + */ +int ft_set_default_tbl_prop(uint32_t property, int value); + +/** + * Set table property. + * + * @param table + * A pointer to the ft_table_t structure. + * @param property + * Table property identifier. + * @param value + * Table property value. + * @return + * - 0: Success; default table property was changed. + * - (<0): In case of error + */ +int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value); + + +/** + * Set column span for the specified cell of the table. + * + * @param table + * A pointer to the ft_table_t structure. + * @param row + * Cell row. + * @param col + * Cell column. + * @param hor_span + * Column span. + * @return + * - 0: Success; cell span was changed. + * - (<0): In case of error + */ +int ft_set_cell_span(ft_table_t *table, size_t row, size_t col, size_t hor_span); + + +/** + * Set functions for memory allocation and deallocation to be used instead of + * standard ones. + * + * @param f_malloc + * Pointer to a function for memory allocation that should be used instead of + * malloc. + * @param f_free + * Pointer to a function for memory deallocation that should be used instead + * of free. + * @note + * To return memory allocation/deallocation functions to their standard values + * set f_malloc and f_free to NULL. + */ +void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)); + + +/** + * Return string describing the `error_code`. + * + * @param error_code + * Error code returned by the library. + * @return + * String describing the error. + */ +const char *ft_strerror(int error_code); + + + +#ifdef FT_HAVE_WCHAR + + +int ft_wprintf(ft_table_t *table, const wchar_t *fmt, ...); +int ft_wprintf_ln(ft_table_t *table, const wchar_t *fmt, ...); + + +#define ft_wwrite(table, ...)\ + (0 ? CHECK_IF_ARGS_ARE_WSTRINGS(__VA_ARGS__) : ft_nwwrite(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +#define ft_wwrite_ln(table, ...)\ + (0 ? CHECK_IF_ARGS_ARE_WSTRINGS(__VA_ARGS__) : ft_nwwrite_ln(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +int ft_nwwrite(ft_table_t *table, size_t n, const wchar_t *cell_content, ...); +int ft_nwwrite_ln(ft_table_t *table, size_t n, const wchar_t *cell_content, ...); + +int ft_row_wwrite(ft_table_t *table, size_t cols, const wchar_t *row_cells[]); +int ft_row_wwrite_ln(ft_table_t *table, size_t cols, const wchar_t *row_cells[]); + +int ft_table_wwrite(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]); +int ft_table_wwrite_ln(ft_table_t *table, size_t rows, size_t cols, const wchar_t *table_cells[]); + +const wchar_t *ft_to_wstring(const ft_table_t *table); +#endif + + + +#ifdef FT_HAVE_UTF8 +#define ft_u8write(table, ...)\ + (ft_u8nwrite(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +#define ft_u8write_ln(table, ...)\ + (ft_u8nwrite_ln(table, FT_PP_NARG_(__VA_ARGS__), __VA_ARGS__)) +int ft_u8nwrite(ft_table_t *table, size_t n, const void *cell_content, ...); +int ft_u8nwrite_ln(ft_table_t *table, size_t n, const void *cell_content, ...); + +int ft_u8printf(ft_table_t *table, const char *fmt, ...) FT_PRINTF_ATTRIBUTE_FORMAT(2, 3); +int ft_u8printf_ln(ft_table_t *table, const char *fmt, ...) FT_PRINTF_ATTRIBUTE_FORMAT(2, 3); + +const void *ft_to_u8string(const ft_table_t *table); + +/** + * Set custom function to compute visible width of UTF-8 string. + * + * libfort internally has a very simple logic to compute visible width of UTF-8 + * strings. It considers that each codepoint will occupy one position on the + * terminal in case of monowidth font (some east asians wide and fullwidth + * characters (see http://www.unicode.org/reports/tr11/tr11-33.html) will occupy + * 2 positions). This logic is very simple and covers wide range of cases. But + * obviously there a lot of cases when it is not sufficient. In such cases user + * should use some external libraries and provide an appropriate function to + * libfort. + * + * @param u8strwid + * User provided function to evaluate width of UTF-8 string ( beg - start of + * UTF-8 string, end - end of UTF-8 string (not included), width - pointer to + * the result). If function succeed it should return 0, otherwise some non- + * zero value. If function returns nonzero value libfort fallbacks to default + * internal algorithm. + */ +void ft_set_u8strwid_func(int (*u8strwid)(const void *beg, const void *end, size_t *width)); + +#endif /* FT_HAVE_UTF8 */ + + +FT_END_DECLS + +#endif /* LIBFORT_H */ diff --git a/main.c b/main.c @@ -0,0 +1,82 @@ +#! /usr/bin/env sheepy +/* or direct path to sheepy: #! /usr/local/bin/sheepy */ + +/* Libsheepy documentation: https://spartatek.se/libsheepy/ */ +#include "libsheepyObject.h" +#include "fort.h" + +/* enable/disable logging */ +/* #undef pLog */ +/* #define pLog(...) */ + +int main(int ARGC, char** ARGV) { + + initLibsheepy(ARGV[0]); + setLogMode(LOG_FUNC); + + /* ft_table_t *table2 = ft_create_table(); */ + /* ft_set_border_style(table2, FT_DOUBLE2_STYLE); */ + /* ft_write(table2, "qwewe"); */ + /* ft_write(table2, "QWEWE"); */ + /* ft_write_ln(table2,"wefe"); */ + /* ft_write(table2, "qwewe"); */ + /* ft_write(table2, "QWEWE"); */ + /* ft_write_ln(table2,"wefe"); */ + /* */ + /* printf("%s\n", ft_to_string(table2)); */ + /* ft_destroy_table(table2); */ + /* ret 0; */ + + void table(const struct ft_border_style *style) { + ft_table_t *table = ft_create_table(); + + /* Change border style */ + /* ft_set_border_style(table, FT_DOUBLE2_STYLE); */ + ft_set_border_style(table, style); + + /* Set "header" type for the first row */ + ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); + + ft_write_ln(table, "N", "Driver", "Time", "Avg Speed"); + + ft_write_ln(table, "1", "Ricciardo", "1:25.945", "222.128"); + ft_write_ln(table, "2", "Hamilton", "1:26.373", "221.027"); + + ft_set_cell_prop(table, 1, 0, FT_CPROP_CONT_FG_COLOR, FT_COLOR_LIGHT_YELLOW); + ft_set_cell_prop(table, 1, 0, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); + + printf("%s\n", ft_to_string(table)); + ft_destroy_table(table); + } + puts("FT_BASIC_STYLE"); + table(FT_BASIC_STYLE); + puts("FT_BASIC2_STYLE"); + table(FT_BASIC2_STYLE); + puts("FT_SIMPLE_STYLE"); + table(FT_SIMPLE_STYLE); + puts("FT_PLAIN_STYLE"); + table(FT_PLAIN_STYLE); + puts("FT_DOT_STYLE"); + table(FT_DOT_STYLE); + puts("FT_EMPTY_STYLE"); + table(FT_EMPTY_STYLE); + puts("FT_EMPTY2_STYLE"); + table(FT_EMPTY2_STYLE); + puts("FT_SOLID_STYLE"); + table(FT_SOLID_STYLE); + puts("FT_SOLID_ROUND_STYLE"); + table(FT_SOLID_ROUND_STYLE); + puts("FT_NICE_STYLE"); + table(FT_NICE_STYLE); + puts("FT_DOUBLE_STYLE"); + table(FT_DOUBLE_STYLE); + puts("FT_DOUBLE2_STYLE"); + table(FT_DOUBLE2_STYLE); + puts("FT_BOLD_STYLE"); + table(FT_BOLD_STYLE); + puts("FT_BOLD2_STYLE"); + table(FT_BOLD2_STYLE); + puts("FT_FRAME_STYLE"); + table(FT_FRAME_STYLE); +} +// vim: set expandtab ts=2 sw=2: diff --git a/package.yml b/package.yml @@ -0,0 +1,37 @@ +--- + name: csvFmt + version: 0.0.1 + description: "display csv file with formating" + bin: ./csvFmt.c + #cflags: -DA -g3 -std=gnu11 -fPIC -pipe + #lflags: -lpcre + repository: + type: git + url: git+https://github.com/USER/csvFmt.git + keywords: + #- utility + - command + author: Remy + license: MIT + bugs: + url: https://github.com/USER/csvFmt/issues + homepage: https://github.com/USER/csvFmt#readme + #compileHelp: # text displayed when there is a compilation error + #dependencies: + # md4c: + # Test configuration: + #testBin: ./testCsvFmt.c + #testCflags: -g3 -std=gnu11 -fPIC -pipe -fprofile-arcs -ftest-coverage -Wall -Wextra + #testLflags: -lcheck_pic -lrt -lm -lsubunit -fprofile-arcs -ftest-coverage -rdynamic + # Memcheck configuration (sheepy -m): + #memcheckBin: ./memcheckCsvFmt.c + #memcheckCmd: valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 + #memcheckCflags: -g3 -std=gnu11 -fPIC -pipe + #memcheckLflags: -rdynamic + # The asan* are the options for building the libsasan tests (sheepy -a): + #asanBin: ./asanCsvFmt.c + #asanCmd: env ASAN_OPTIONS="detect_leaks=1:detect_stack_use_after_return=1:halt_on_error=0:log_path=stdout:color=always:print_cmdline=1" + #asanCflags: -fsanitize=address -fno-omit-frame-pointer -fsanitize-recover=address # these flags are overriden by package.yml + #asanLflags: -rdynamic -fsanitize=address -lasan # these flags are overriden by package.yml + #documentationCmd: # command for generating the documentation with spm doc + private: false # true for private package diff --git a/phones.csv b/phones.csv @@ -0,0 +1,17 @@ +Year;Standard;Bandwidth;Ping;Standby;Name +1996;2G;;;1 day;Nokia, could not send SMS +2000;2G;9.6kb/s;>1s;2 days;Ericsson T28 +2001;2.5G;57.6kb/s;>1s;10 days;Ericsson T39 +2001;2G;;;2 days;Ericsson T66 +2002;2.5G;57.6kb/s;>1s;10 days;Ericsson R520 +2002;2.5G;64.2kb/s;>1s;8 days;Ericsson T68 +2003;2.5G;64.2kb/s;>1s;7 days;Sony Ericsson T630 +2005;3G;384kb/s;700ms;7 days;Sony Ericsson V600 +2007;3G;384kb/s;700ms;7 days;Sony Ericsson T880 +2008;3G;3.6mb/s;400ms;8 days;Sony Ericsson T700 +2009;3G;10mb/s;300ms;3 days;Nokia N900 +2011;3G;14.4mb/s;200ms;3 days;Apple iPhone 4s +2015;4G;150mb/s;70ms;1 day;Samsung Galaxy A7 +2017;4G;600mb/s;30ms;3 days;Apple iPhone X +2020;5G;4gb/s(5G) 1gb/s(4G);10ms;5 days;Apple iPhone 12 Pro Max +2021;5G;4gb/s(5G) 2gb/s(4G);10ms;4 days;Samsung Galaxy S21 diff --git a/styles.txt b/styles.txt @@ -0,0 +1,116 @@ +FT_BASIC_STYLE ++---+-----------+----------+-----------+ +| N | Driver | Time | Avg Speed | ++---+-----------+----------+-----------+ +| 1 | Ricciardo | 1:25.945 | 222.128 | +| 2 | Hamilton | 1:26.373 | 221.027 | ++---+-----------+----------+-----------+ + +FT_BASIC2_STYLE ++---+-----------+----------+-----------+ +| N | Driver | Time | Avg Speed | ++---+-----------+----------+-----------+ +| 1 | Ricciardo | 1:25.945 | 222.128 | ++---+-----------+----------+-----------+ +| 2 | Hamilton | 1:26.373 | 221.027 | ++---+-----------+----------+-----------+ + +FT_SIMPLE_STYLE + N  Driver  Time  Avg Speed  +─── ─────────── ────────── ─────────── + 1  Ricciardo  1:25.945  222.128  + 2  Hamilton  1:26.373  221.027  + +FT_PLAIN_STYLE +-------------------------------------- + N  Driver  Time  Avg Speed  +-------------------------------------- + 1  Ricciardo  1:25.945  222.128  + 2  Hamilton  1:26.373  221.027  + +FT_DOT_STYLE +........................................ +: N : Driver : Time : Avg Speed : +:...:...........:..........:...........: +: 1 : Ricciardo : 1:25.945 : 222.128 : +: 2 : Hamilton : 1:26.373 : 221.027 : +:...:...........:..........:...........: + +FT_EMPTY_STYLE + N  Driver  Time  Avg Speed  + 1  Ricciardo  1:25.945  222.128  + 2  Hamilton  1:26.373  221.027  + +FT_EMPTY2_STYLE + + N  Driver  Time  Avg Speed  + 1  Ricciardo  1:25.945  222.128  + 2  Hamilton  1:26.373  221.027  + + +FT_SOLID_STYLE +┌───┬───────────┬──────────┬───────────┐ +│ N │ Driver │ Time │ Avg Speed │ +├───┼───────────┼──────────┼───────────┤ +│ 1 │ Ricciardo │ 1:25.945 │ 222.128 │ +│ 2 │ Hamilton │ 1:26.373 │ 221.027 │ +└───┴───────────┴──────────┴───────────┘ + +FT_SOLID_ROUND_STYLE +╭───┬───────────┬──────────┬───────────╮ +│ N │ Driver │ Time │ Avg Speed │ +├───┼───────────┼──────────┼───────────┤ +│ 1 │ Ricciardo │ 1:25.945 │ 222.128 │ +│ 2 │ Hamilton │ 1:26.373 │ 221.027 │ +╰───┴───────────┴──────────┴───────────╯ + +FT_NICE_STYLE +╔═══╦═══════════╦══════════╦═══════════╗ +║ N ║ Driver ║ Time ║ Avg Speed ║ +╠═══╬═══════════╬══════════╬═══════════╣ +║ 1 ║ Ricciardo ║ 1:25.945 ║ 222.128 ║ +║ 2 ║ Hamilton ║ 1:26.373 ║ 221.027 ║ +╚═══╩═══════════╩══════════╩═══════════╝ + +FT_DOUBLE_STYLE +╔═══╦═══════════╦══════════╦═══════════╗ +║ N ║ Driver ║ Time ║ Avg Speed ║ +╠═══╬═══════════╬══════════╬═══════════╣ +║ 1 ║ Ricciardo ║ 1:25.945 ║ 222.128 ║ +║ 2 ║ Hamilton ║ 1:26.373 ║ 221.027 ║ +╚═══╩═══════════╩══════════╩═══════════╝ + +FT_DOUBLE2_STYLE +╔═══╤═══════════╤══════════╤═══════════╗ +║ N │ Driver │ Time │ Avg Speed ║ +╠═══╪═══════════╪══════════╪═══════════╣ +║ 1 │ Ricciardo │ 1:25.945 │ 222.128 ║ +╟───┼───────────┼──────────┼───────────╢ +║ 2 │ Hamilton │ 1:26.373 │ 221.027 ║ +╚═══╧═══════════╧══════════╧═══════════╝ + +FT_BOLD_STYLE +┏━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓ +┃ N ┃ Driver ┃ Time ┃ Avg Speed ┃ +┣━━━╋━━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━━━━━┫ +┃ 1 ┃ Ricciardo ┃ 1:25.945 ┃ 222.128 ┃ +┃ 2 ┃ Hamilton ┃ 1:26.373 ┃ 221.027 ┃ +┗━━━┻━━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━━━━━┛ + +FT_BOLD2_STYLE +┏━━━┯━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━┓ +┃ N │ Driver │ Time │ Avg Speed ┃ +┣━━━┿━━━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━━┫ +┃ 1 │ Ricciardo │ 1:25.945 │ 222.128 ┃ +┠───┼───────────┼──────────┼───────────┨ +┃ 2 │ Hamilton │ 1:26.373 │ 221.027 ┃ +┗━━━┷━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━┛ + +FT_FRAME_STYLE +▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜ +▌ N ┃ Driver ┃ Time ┃ Avg Speed ▐ +▌━━━╋━━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━━━━━▐ +▌ 1 ┃ Ricciardo ┃ 1:25.945 ┃ 222.128 ▐ +▌ 2 ┃ Hamilton ┃ 1:26.373 ┃ 221.027 ▐ +▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟ +