diff --git a/.travis.yml b/.travis.yml index 4ac1335..9a7037a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -85,6 +85,7 @@ script: - cmake --build . --target all - ls - ./libfort_example + - ./libfort_test_dev - ./libfort_test # Test build without optimizations and with ubsan @@ -97,6 +98,7 @@ script: cmake --build . --target all ; ls ; ./libfort_example ; + ./libfort_test_dev ; ./libfort_test ; fi @@ -143,7 +145,7 @@ script: cmake .. -DFORT_BUILD_TYPE=coveralls ; cmake --build . --target all ; ls ; - ./libfort_test ; + ./libfort_test_dev ; fi - cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b55b0f..c69f15a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_definitions(-DFT_CONGIG_HAVE_WCHAR) include_directories(include) include_directories(src) +include_directories(tests) @@ -88,18 +89,32 @@ add_executable(${PROJECT_NAME}_example_cpp ${EXAMPLE_CPP_SOURCES} ${FORT_SOURCES}) -set(TEST_SOURCES - tests/test.c - tests/test_vector.c - tests/test_string_buffer.c - tests/test_table_geometry.c - tests/test_table_basic.c - tests/test_table_border_style.c - tests/test_table_options.c - tests/test_memory_errors.c - tests/test_utility.c) -add_executable(${PROJECT_NAME}_test +set(TEST_SOURCES_DEV + tests/main_test.c + tests/wb_tests/test_vector.c + tests/wb_tests/test_string_buffer.c + tests/wb_tests/test_table_geometry.c + tests/bb_tests/test_table_basic.c + tests/bb_tests/test_table_border_style.c + tests/bb_tests/test_table_options.c + tests/bb_tests/test_memory_errors.c + tests/test_common.c) +add_executable(${PROJECT_NAME}_test_dev ${FORT_SOURCES} + ${TEST_SOURCES_DEV}) + +target_compile_definitions(${PROJECT_NAME}_test_dev + PUBLIC FORT_WB_TESTING_ENABLED=1) + +set(TEST_SOURCES + tests/main_test.c + tests/bb_tests/test_table_basic.c + tests/bb_tests/test_table_border_style.c + tests/bb_tests/test_table_options.c + tests/bb_tests/test_memory_errors.c + tests/test_common.c) +add_executable(${PROJECT_NAME}_test + include/fort.c ${TEST_SOURCES}) diff --git a/amalgamate.py b/amalgamate.py new file mode 100644 index 0000000..46f24a2 --- /dev/null +++ b/amalgamate.py @@ -0,0 +1,81 @@ + +import os + +def comment_line(line): + return "/* {} */ /* Commented by amalgamation script */".format(line) + +def amalgamate(config): + with open(config["output_file"], "w") as output_f: + output_f.write("/* The file was GENERATED by an amalgamation script.*/\n") + output_f.write("/* DO NOT EDIT BY HAND!!! */\n\n") + + for hdr_file in config["header_files"]: + with open(config["src_dir"] + "/" + hdr_file, "r") as input_f: + txt = input_f.read() + output_f.write('\n/********************************************************\n') + output_f.write(' Begin of file "{}"\n'.format(hdr_file)) + output_f.write(' ********************************************************/\n\n') + output_f.write(txt) + output_f.write('\n/********************************************************\n') + output_f.write(' End of file "{}"\n'.format(hdr_file)) + output_f.write(' ********************************************************/\n\n') + + for src_file in config["src_files"]: + with open(config["src_dir"] + "/" + src_file, "r") as input_f: + txt = input_f.read() + output_f.write('\n/********************************************************\n') + output_f.write(' Begin of file "{}"\n'.format(src_file)) + output_f.write(' ********************************************************/\n\n') + output_f.write(txt) + output_f.write('\n/********************************************************\n') + output_f.write(' End of file "{}"\n'.format(src_file)) + output_f.write(' ********************************************************/\n\n') + + with open(config["output_file"]) as f: + lines = f.readlines() + + + forbidden_strings = map(lambda hdr_name: '#include "{}"'.format(hdr_name), config["header_files"]) + + lines = map(lambda line: comment_line(line.strip()) + "\n" if line.strip() in forbidden_strings else line, lines) + + with open(config["output_file"], "w") as f: + for line in lines: + f.write(line) + + + + +def is_c_header_file(file): + return ".h" in file + + +def is_c_source_file(file): + return ".c" in file + + +def main(): + config = {} + config["output_file"] = "./include/fort.c" + config["src_dir"] = "./src" + all_files = os.listdir(config["src_dir"]) + config["src_files"] = filter(is_c_source_file, all_files) + + # config["header_files"] = filter(is_c_header_file, all_files) + config["header_files"] = [ + "fort_impl.h", + "vector.h", + "wcwidth.h", + "string_buffer.h", + "options.h", + "cell.h", + "row.h", + "table.h" + ]; + amalgamate(config) + + + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 09922f1..e0f415b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,5 +21,6 @@ build: project: $(APPVEYOR_BUILD_FOLDER)\build\$(APPVEYOR_PROJECT_NAME).sln test_script: +- '%APPVEYOR_BUILD_FOLDER%\build\Debug\libfort_test_dev.exe' - '%APPVEYOR_BUILD_FOLDER%\build\Debug\libfort_test.exe' - '%APPVEYOR_BUILD_FOLDER%\build\Debug\libfort_example.exe' \ No newline at end of file diff --git a/include/fort.c b/include/fort.c new file mode 100644 index 0000000..a149ea5 --- /dev/null +++ b/include/fort.c @@ -0,0 +1,5060 @@ +/* The file was GENERATED by an amalgamation script.*/ +/* DO NOT EDIT BY HAND!!! */ + + +/******************************************************** + Begin of file "fort_impl.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 +#include +#include +#include +#include +#include "fort.h" + + + +#define FORT_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 + +#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) + + +enum PolicyOnNull +{ + Create, + DoNotCreate +}; + + +enum F_BOOL +{ + F_FALSE = 0, + F_TRUE = 1 +}; + + +#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) + + + +/***************************************************************************** + * LOGGER + *****************************************************************************/ +#define SYS_LOG_ERROR(...) + + + +/***************************************************************************** + * DEFAULT_SIZES + * ***************************************************************************/ +#define DEFAULT_STR_BUF_SIZE 1024 +#define DEFAULT_VECTOR_CAPACITY 10 + + + +struct fort_table_options; +struct fort_column_options; +struct fort_row; +struct vector; +struct fort_cell; +struct string_buffer; +struct separator +{ + int enabled; +}; + +typedef struct fort_table_options fort_table_options_t; +struct fort_context +{ + fort_table_options_t *table_options; + size_t row; + size_t column; +}; +typedef struct fort_context context_t; +typedef struct fort_column_options fort_column_options_t; +typedef struct vector vector_t; +typedef struct fort_cell fort_cell_t; +typedef struct string_buffer string_buffer_t; +typedef struct fort_row fort_row_t; +typedef struct ft_table ft_table_t; +typedef struct separator separator_t; + + + + +enum CellType +{ + CommonCell, + GroupMasterCell, + GroupSlaveCell +}; + + +/***************************************************************************** + * 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); +void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)); + + +char *fort_strdup(const char* str); +wchar_t *fort_wcsdup(const wchar_t* str); +size_t number_of_columns_in_format_string(const char *fmt); +size_t number_of_columns_in_format_wstring(const wchar_t *fmt); +//int snprint_n_chars(char *buf, size_t length, size_t n, char ch); +//int wsnprint_n_chars(wchar_t *buf, size_t length, size_t n, wchar_t ch); +int snprint_n_strings(char *buf, size_t length, size_t n, const char *str); +int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str); + + +#define CHCK_RSLT_ADD_TO_WRITTEN(statement) \ + do { \ + tmp = statement; \ + if (tmp < 0) {\ + goto clear; \ + } \ + written += 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_impl.h" + ********************************************************/ + + +/******************************************************** + Begin of file "vector.h" + ********************************************************/ + +#ifndef VECTOR_H +#define VECTOR_H + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ + + +/***************************************************************************** + * VECTOR + * ***************************************************************************/ + +struct vector; +typedef struct vector vector_t; + +#define INVALID_VEC_INDEX ((size_t) -1) + +extern vector_t* create_vector(size_t item_size, size_t capacity); +extern void destroy_vector(vector_t*); +extern vector_t* copy_vector(vector_t*); + +extern size_t vector_size(const vector_t*); +extern size_t vector_capacity(const vector_t*); +extern size_t vector_index_of(const vector_t*, const void *item); + +extern int vector_push(vector_t*, const void *item); +extern int vector_erase(vector_t*, size_t index); +extern void vector_clear(vector_t*); +extern const void *vector_at_c(const vector_t *vector, size_t index); +extern void* vector_at(vector_t*, size_t index); +extern fort_status_t vector_swap(vector_t *cur_vec, vector_t *mv_vec, size_t pos); + + +#define FOR_EACH_(type, item, vector, index_name) \ + size_t index_name = 0; \ + for (index_name = 0; (index_name < vector_size(vector)) ? ((item = *(type*)vector_at(vector, index_name)), 1) : 0; ++index_name) + +#define FOR_EACH(type, item, vector) \ + FOR_EACH_(type, item, vector, UNIQUE_NAME(i)) + +#endif /* VECTOR_H */ + +/******************************************************** + End of file "vector.h" + ********************************************************/ + + +/******************************************************** + Begin of file "wcwidth.h" + ********************************************************/ + +#ifndef WCWIDTH_H +#define WCWIDTH_H + + +int mk_wcswidth(const wchar_t *pwcs, size_t n); + +#endif // WCWIDTH_H + +/******************************************************** + End of file "wcwidth.h" + ********************************************************/ + + +/******************************************************** + Begin of file "string_buffer.h" + ********************************************************/ + +#ifndef STRING_BUFFER_H +#define STRING_BUFFER_H + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ + + +/***************************************************************************** + * STRING BUFFER + * ***************************************************************************/ +enum str_buf_type +{ + CharBuf, + WCharBuf +}; + +struct string_buffer; +typedef struct string_buffer string_buffer_t; +struct string_buffer +{ + union { + char *cstr; + wchar_t *wstr; + void *data; + }str; + size_t data_sz; + enum str_buf_type type; +}; + +string_buffer_t* create_string_buffer(size_t number_of_chars, enum str_buf_type type); +void destroy_string_buffer(string_buffer_t *buffer); +fort_status_t realloc_string_buffer_without_copy(string_buffer_t *buffer); + +fort_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str); +fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str); + + +size_t buffer_text_height(string_buffer_t *buffer); +size_t string_buffer_capacity(const string_buffer_t *buffer); +void *buffer_get_data(string_buffer_t *buffer); + + +size_t buffer_text_width(string_buffer_t *buffer); +int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t buf_len, const context_t *context); +int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t buf_len, const context_t *context); + +#endif /* STRING_BUFFER_H */ + +/******************************************************** + End of file "string_buffer.h" + ********************************************************/ + + +/******************************************************** + Begin of file "options.h" + ********************************************************/ + +#ifndef OPTIONS_H +#define OPTIONS_H + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ +#include +#include + +struct fort_column_options +{ + int col_min_width; + enum ft_text_alignment align; +}; +typedef struct fort_column_options fort_column_options_t; + +extern fort_column_options_t g_column_options; +fort_column_options_t create_column_options(void); + + + +struct vector; +typedef struct vector vector_t; + +#define OPTION_IS_SET(ft_opts, option) ((ft_opts) & (option)) +#define OPTION_SET(ft_opts, option) ((ft_opts) |=(option)) +#define OPTION_UNSET(ft_opts, option) ((ft_opts) &= ~((uint32_t)option)) + +struct fort_cell_options +{ + size_t cell_row; + size_t cell_col; + uint32_t options; + 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; +}; + +typedef struct fort_cell_options fort_cell_options_t; + + + +typedef vector_t fort_cell_opt_container_t; +fort_cell_opt_container_t *create_cell_opt_container(void); +void destroy_cell_opt_container(fort_cell_opt_container_t *cont); +const fort_cell_options_t* cget_cell_opt(const fort_cell_opt_container_t *cont, size_t row, size_t col); +fort_cell_options_t* get_cell_opt_and_create_if_not_exists(fort_cell_opt_container_t *cont, size_t row, size_t col); +fort_status_t set_cell_option(fort_cell_opt_container_t *cont, size_t row, size_t col, uint32_t option, int value); +fort_status_t unset_cell_option(fort_cell_opt_container_t *cont, unsigned row, unsigned col, uint32_t option); + +int get_cell_opt_value_hierarcial(const fort_table_options_t *options, size_t row, size_t column, uint32_t option); + +fort_status_t set_default_cell_option(uint32_t option, int value); + +/***************************************************************************** + * TABLE BORDER + *****************************************************************************/ + +/* + * TL TT TT TT TV TT TT TT TV TB TB TB TR <----- TopSeparator + * LL IV IV RR + * LH IH IH IH II IH IH IH II IH IH IH RH <----- InsideSeparator + * LL IV IV RR + * BL BB BB BB BV BB BB BB BV BB BB BB BR <----- BottomSeparator + */ + +enum HorSeparatorPos +{ + TopSeparator, + InsideSeparator, + BottomSeparator +}; + +enum BorderItemPos +{ + 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, + + BorderItemPosSize +}; + + +enum SeparatorItemPos +{ + LH_sip = 0, + IH_sip = 1, + II_sip = 2, + RH_sip = 3, + + SepratorItemPosSize +}; + + +struct fort_border_style +{ + const char *border_chars[BorderItemPosSize]; + const char *header_border_chars[BorderItemPosSize]; + const char *separator_chars[SepratorItemPosSize]; +}; +extern struct fort_border_style FORT_BASIC_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_SOLID_STYLE; +extern struct fort_border_style FORT_SOLID_ROUND_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_options +{ + unsigned int left_margin; + unsigned int top_margin; + unsigned int right_margin; + unsigned int bottom_margin; +}; +typedef struct fort_entire_table_options fort_entire_table_options_t; +extern fort_entire_table_options_t g_entire_table_options; +fort_status_t set_entire_table_option(fort_table_options_t *table_options, uint32_t option, int value); +fort_status_t set_default_entire_table_option(uint32_t option, int value); + +struct fort_table_options +{ + struct fort_border_style border_style; + fort_cell_opt_container_t * cell_options; + fort_entire_table_options_t entire_table_options; +}; +typedef struct fort_table_options fort_table_options_t; +extern fort_table_options_t g_table_options; + +size_t max_border_elem_strlen(struct fort_table_options*); + + +fort_table_options_t* create_table_options(void); +fort_table_options_t* copy_table_options(const fort_table_options_t *option); +void destroy_table_options(fort_table_options_t* options); + + + +#endif /* OPTIONS_H */ + +/******************************************************** + End of file "options.h" + ********************************************************/ + + +/******************************************************** + Begin of file "cell.h" + ********************************************************/ + +#ifndef CELL_H +#define CELL_H + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ + + + + +/***************************************************************************** + * CELL + * ***************************************************************************/ +fort_cell_t * create_cell(void); + + +void destroy_cell(fort_cell_t *cell); +size_t hint_width_cell(const fort_cell_t *cell, const context_t *context); +size_t hint_height_cell(const fort_cell_t *cell, const context_t *context); + + +void set_cell_type(fort_cell_t *cell, enum CellType type); +enum CellType get_cell_type(const fort_cell_t *cell); + +/* + * Returns number of lines in cell. If cell is empty or + * contains empty string, then 0 is returned. + */ +/* static int lines_number_cell(fort_cell_t *cell); */ + +int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context); +fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str); + +#ifdef FT_HAVE_WCHAR +int cell_wprintf(fort_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context); +fort_status_t fill_cell_from_wstring(fort_cell_t *cell, const wchar_t *str); +#endif + +string_buffer_t* cell_get_string_buffer(fort_cell_t *cell); + +#endif /* CELL_H */ + +/******************************************************** + End of file "cell.h" + ********************************************************/ + + +/******************************************************** + Begin of file "row.h" + ********************************************************/ + +#ifndef ROW_H +#define ROW_H + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ +#include "fort.h" +#include +/* #include "options.h" */ /* Commented by amalgamation script */ +#ifdef FT_HAVE_WCHAR +#include +#endif + +struct fort_row; +typedef struct fort_row fort_row_t; + + +fort_row_t * create_row(void); +void destroy_row(fort_row_t *row); +fort_row_t * create_row_from_string(const char *str); +fort_row_t* create_row_from_fmt_string(const char* fmt, va_list *va_args); + + +size_t columns_in_row(const fort_row_t *row); + +fort_cell_t *get_cell_implementation(fort_row_t *row, size_t col, enum PolicyOnNull policy); +fort_cell_t *get_cell(fort_row_t *row, size_t col); +const fort_cell_t *get_cell_c(const fort_row_t *row, size_t col); +fort_cell_t *get_cell_and_create_if_not_exists(fort_row_t *row, size_t col); +fort_status_t swap_row(fort_row_t *cur_row, fort_row_t *ins_row, size_t pos); + +size_t group_cell_number(const fort_row_t *row, size_t master_cell_col); +int get_row_cell_types(const fort_row_t *row, enum CellType *types, size_t types_sz); +fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_span); + +int print_row_separator(char *buffer, size_t buffer_sz, + const size_t *col_width_arr, size_t cols, + const fort_row_t *upper_row, const fort_row_t *lower_row, + enum HorSeparatorPos separatorPos, const separator_t *sep, + const context_t *context); + + + + + + + +int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height, const context_t *context); + +#ifdef FT_HAVE_WCHAR +fort_row_t *create_row_from_wstring(const wchar_t *str); +fort_row_t* create_row_from_fmt_wstring(const wchar_t* fmt, va_list *va_args); + +int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, + const size_t *col_width_arr, size_t cols, + const fort_row_t *upper_row, const fort_row_t *lower_row, + enum HorSeparatorPos separatorPos, const separator_t *sep, + const context_t *context); +int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height, const context_t *context); +#endif + + +#endif /* ROW_H */ + +/******************************************************** + End of file "row.h" + ********************************************************/ + + +/******************************************************** + Begin of file "table.h" + ********************************************************/ + +#ifndef TABLE_H +#define TABLE_H + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ + +struct ft_table; +typedef struct ft_table ft_table_t; +struct ft_table +{ + vector_t *rows; + fort_table_options_t *options; + string_buffer_t *conv_buffer; + size_t cur_row; + size_t cur_col; + vector_t *separators; +}; + +static FT_INLINE +separator_t *create_separator(int enabled) +{ + separator_t *res = (separator_t *)F_CALLOC(1, sizeof(separator_t)); + if (res == NULL) + return NULL; + res->enabled = enabled; + return res; +} + +static FT_INLINE +void destroy_separator(separator_t *sep) +{ + F_FREE(sep); +} + + + +fort_status_t get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols); +fort_row_t *get_row_implementation(ft_table_t *table, size_t row, enum PolicyOnNull policy); +fort_row_t *get_row(ft_table_t *table, size_t row); +const fort_row_t *get_row_c(const ft_table_t *table, size_t row); +fort_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row); + +string_buffer_t * get_cur_str_buffer_and_create_if_not_exists(ft_table_t * table); + + + +fort_status_t 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); +fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width); + +#endif /* TABLE_H */ + +/******************************************************** + End of file "table.h" + ********************************************************/ + + +/******************************************************** + Begin of file "options.c" + ********************************************************/ + +#include +/* #include "options.h" */ /* Commented by amalgamation script */ +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ +/* #include "vector.h" */ /* Commented by amalgamation script */ + +/***************************************************************************** + * COLUMN OPTIONS + * ***************************************************************************/ + +struct fort_cell_options g_default_cell_option = { + FT_ANY_ROW, /* cell_row */ + FT_ANY_COLUMN, /* cell_col */ + + /* options */ + FT_COPT_MIN_WIDTH | FT_COPT_TEXT_ALIGN | FT_COPT_TOP_PADDING + | FT_COPT_BOTTOM_PADDING | FT_COPT_LEFT_PADDING | FT_COPT_RIGHT_PADDING + | FT_COPT_EMPTY_STR_HEIGHT, + + 0, /* col_min_width */ + FT_ALIGNED_RIGHT, /* 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 */ +}; + +static int get_option_value_if_exists_otherwise_default(const struct fort_cell_options *cell_opts, uint32_t option) +{ + if (cell_opts == NULL || !OPTION_IS_SET(cell_opts->options, option)) { + cell_opts = &g_default_cell_option; + } + + switch (option) { + case FT_COPT_MIN_WIDTH: + return cell_opts->col_min_width; + case FT_COPT_TEXT_ALIGN: + return cell_opts->align; + case FT_COPT_TOP_PADDING: + return cell_opts->cell_padding_top; + case FT_COPT_BOTTOM_PADDING: + return cell_opts->cell_padding_bottom; + case FT_COPT_LEFT_PADDING: + return cell_opts->cell_padding_left; + case FT_COPT_RIGHT_PADDING: + return cell_opts->cell_padding_right; + case FT_COPT_EMPTY_STR_HEIGHT: + return cell_opts->cell_empty_string_height; + case FT_COPT_ROW_TYPE: + return cell_opts->row_type; + default: + /* todo: implement later */ + exit(333); + } +} + + +fort_column_options_t g_column_options = { + 0, /* col_min_width*/ + FT_ALIGNED_RIGHT, /* align */ +}; + +fort_column_options_t create_column_options(void) +{ + fort_column_options_t result; + memset(&result, '\0', sizeof(result)); + memcpy(&result, &g_column_options, sizeof(result)); + return result; +} + + +//#define DEFAULT_CELL_OPTION {FT_ROW_UNSPEC, FT_COLUMN_UNSPEC, 0, 0, 0} + +fort_cell_opt_container_t *create_cell_opt_container(void) +{ + fort_cell_opt_container_t *ret = create_vector(sizeof(fort_cell_options_t), DEFAULT_VECTOR_CAPACITY); + return ret; +} + + +void destroy_cell_opt_container(fort_cell_opt_container_t *cont) +{ + if (cont) + destroy_vector(cont); +} + +const fort_cell_options_t *cget_cell_opt(const fort_cell_opt_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 fort_cell_options_t *opt = (const fort_cell_options_t *)vector_at_c(cont, i); + if (opt->cell_row == row && opt->cell_col == col) + return opt; + } + return NULL; +} + +fort_cell_options_t *get_cell_opt_and_create_if_not_exists(fort_cell_opt_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) { + fort_cell_options_t *opt = (fort_cell_options_t *)vector_at(cont, i); + if (opt->cell_row == row && opt->cell_col == col) + return opt; + } + fort_cell_options_t opt = g_default_cell_option;// DEFAULT_CELL_OPTION; + opt.cell_row = row; + opt.cell_col = col; + if (FT_IS_SUCCESS(vector_push(cont, &opt))) { + return (fort_cell_options_t *)vector_at(cont, sz); + } + + return NULL; +} + + + + +int get_cell_opt_value_hierarcial(const fort_table_options_t *options, size_t row, size_t column, uint32_t option) +{ + assert(options); + + const fort_cell_options_t *opt = NULL; + if (options->cell_options != NULL) { + while (1) { + opt = cget_cell_opt(options->cell_options, row, column); + if (opt != NULL) + break; + if (row != FT_ANY_ROW) { + row = FT_ANY_ROW; + continue; + } + if (column != FT_ANY_COLUMN) { + column = FT_ANY_COLUMN; + continue; + } + + opt = NULL; + break; + } + } + + return get_option_value_if_exists_otherwise_default(opt, option); +} + + +static fort_status_t set_cell_option_impl(fort_cell_options_t *opt, uint32_t option, int value) +{ + assert(opt); + + OPTION_SET(opt->options, option); + if (OPTION_IS_SET(option, FT_COPT_MIN_WIDTH)) { + CHECK_NOT_NEGATIVE(value); + opt->col_min_width = value; + } else if (OPTION_IS_SET(option, FT_COPT_TEXT_ALIGN)) { + opt->align = (enum ft_text_alignment)value; + } else if (OPTION_IS_SET(option, FT_COPT_TOP_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_top = value; + } else if (OPTION_IS_SET(option, FT_COPT_BOTTOM_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_bottom = value; + } else if (OPTION_IS_SET(option, FT_COPT_LEFT_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_left = value; + } else if (OPTION_IS_SET(option, FT_COPT_RIGHT_PADDING)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_padding_right = value; + } else if (OPTION_IS_SET(option, FT_COPT_EMPTY_STR_HEIGHT)) { + CHECK_NOT_NEGATIVE(value); + opt->cell_empty_string_height = value; + } else if (OPTION_IS_SET(option, FT_COPT_ROW_TYPE)) { + opt->row_type = (enum ft_row_type)value; + } + + return FT_SUCCESS; + +fort_fail: + return FT_EINVAL; +} + + +fort_status_t set_cell_option(fort_cell_opt_container_t *cont, size_t row, size_t col, uint32_t option, int value) +{ + fort_cell_options_t *opt = get_cell_opt_and_create_if_not_exists(cont, row, col); + if (opt == NULL) + return FT_ERROR; + + return set_cell_option_impl(opt, option, value); + /* + OPTION_SET(opt->options, option); + if (OPTION_IS_SET(option, FT_COPT_MIN_WIDTH)) { + opt->col_min_width = value; + } else if (OPTION_IS_SET(option, FT_COPT_TEXT_ALIGN)) { + opt->align = value; + } + + return FT_SUCCESS; + */ +} + +fort_status_t set_default_cell_option(uint32_t option, int value) +{ + return set_cell_option_impl(&g_default_cell_option, option, value); +} + +/***************************************************************************** + * OPTIONS + * ***************************************************************************/ + + +#define BASIC_STYLE { \ + /* border_chars */ \ + { \ + "+", "-", "+", "+", \ + "|", "|", "|", \ + "\0", "\0", "\0", "\0", \ + "+", "-", "+", "+" \ + }, \ + /* header_border_chars */ \ + { \ + "+", "-", "+", "+", \ + "|", "|", "|", \ + "+", "-", "+", "+", \ + "+", "-", "+", "+" \ + }, \ + /* separator_chars */ \ + { \ + "+", "-", "+", "+", \ + }, \ +} + +#define SIMPLE_STYLE { \ + /* border_chars */ \ + { \ + " ", " ", " ", " ", \ + " ", " ", " ", \ + "\0", "\0", "\0", "\0", \ + " ", " ", " ", " " \ + }, \ + /* header_border_chars */ \ + { \ + " ", " ", " ", " ", \ + " ", " ", " ", \ + " ", "-", " ", " ", \ + " ", " ", " ", " " \ + }, \ + /* separator_chars */ \ + { \ + " ", "-", " ", " ", \ + }, \ +} + +#define PLAIN_STYLE { \ + /* border_chars */ \ + { \ + " ", " ", " ", " ", \ + " ", " ", " ", \ + "\0", "\0", "\0", "\0", \ + " ", " ", " ", " " \ + }, \ + /* header_border_chars */ \ + { \ + " ", "-", "-", " ", \ + " ", " ", " ", \ + " ", "-", "-", " ", \ + " ", "-", "-", " " \ + }, \ + /* separator_chars */ \ + { \ + " ", "-", "-", " ", \ + }, \ +} + +#define DOT_STYLE { \ + /* border_chars */ \ + { \ + ".", ".", ".", ".", \ + ":", ":", ":", \ + "\0", "\0", "\0", "\0", \ + ":", ".", ":", ":" \ + }, \ + /* header_border_chars */ \ + { \ + ".", ".", ".", ".", \ + ":", ":", ":", \ + ":", ".", ":", ":", \ + ":", ".", ":", ":" \ + }, \ + /* separator_chars */ \ + { \ + ":", ".", ":", ":", \ + }, \ +} + +#define EMPTY_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 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_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_SOLID_STYLE = SOLID_STYLE; +struct fort_border_style FORT_SOLID_ROUND_STYLE = SOLID_ROUND_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_options_t g_entire_table_options = { + 0, /* left_margin */ + 0, /* top_margin */ + 0, /* right_margin */ + 0, /* bottom_margin */ +}; + +static fort_status_t set_entire_table_option_internal(fort_entire_table_options_t *options, uint32_t option, int value) +{ + assert(options); + CHECK_NOT_NEGATIVE(value); + if (OPTION_IS_SET(option, FT_TOPT_LEFT_MARGIN)) { + options->left_margin = value; + } else if (OPTION_IS_SET(option, FT_TOPT_TOP_MARGIN)) { + options->top_margin = value; + } else if (OPTION_IS_SET(option, FT_TOPT_RIGHT_MARGIN)) { + options->right_margin = value; + } else if (OPTION_IS_SET(option, FT_TOPT_BOTTOM_MARGIN)) { + options->bottom_margin = value; + } else { + return FT_EINVAL; + } + return FT_SUCCESS; + +fort_fail: + return FT_EINVAL; +} + +fort_status_t set_entire_table_option(fort_table_options_t *table_options, uint32_t option, int value) +{ + assert(table_options); + return set_entire_table_option_internal(&table_options->entire_table_options, option, value); +} + +fort_status_t set_default_entire_table_option(uint32_t option, int value) +{ + return set_entire_table_option_internal(&g_entire_table_options, option, value); +} + +size_t max_border_elem_strlen(struct fort_table_options *options) +{ + assert(options); + size_t result = 1; + int i = 0; + for (i = 0; i < BorderItemPosSize; ++i) { + result = MAX(result, strlen(options->border_style.border_chars[i])); + } + + i = 0; + for (i = 0; i < BorderItemPosSize; ++i) { + result = MAX(result, strlen(options->border_style.header_border_chars[i])); + } + + i = 0; + for (i = 0; i < SepratorItemPosSize; ++i) { + result = MAX(result, strlen(options->border_style.separator_chars[i])); + } + return result; +} + + +fort_table_options_t g_table_options = { + /* border_style */ + BASIC_STYLE, + NULL, /* cell_options */ + /* entire_table_options */ + { + 0, /* left_margin */ + 0, /* top_margin */ + 0, /* right_margin */ + 0 /* bottom_margin */ + } +}; + + +fort_table_options_t *create_table_options(void) +{ + fort_table_options_t *options = (fort_table_options_t *)F_CALLOC(sizeof(fort_table_options_t), 1); + if (options == NULL) { + return NULL; + } + memcpy(options, &g_table_options, sizeof(fort_table_options_t)); + options->cell_options = create_cell_opt_container(); + if (options->cell_options == NULL) { + destroy_table_options(options); + return NULL; + } + memcpy(&options->entire_table_options, &g_entire_table_options, sizeof(fort_entire_table_options_t)); + return options; +} + +fort_table_options_t *copy_table_options(const fort_table_options_t *option) +{ + /* todo: normal implementation, do deep copy of col options */ + + fort_table_options_t *new_opt = create_table_options(); + if (new_opt == NULL) + return NULL; + + memcpy(new_opt, option, sizeof(fort_table_options_t)); + + if (option->cell_options) { + destroy_cell_opt_container(new_opt->cell_options); + new_opt->cell_options = copy_vector(option->cell_options); + if (new_opt->cell_options == NULL) { + destroy_table_options(new_opt); + new_opt = NULL; + } + } + return new_opt; +} + + +void destroy_table_options(fort_table_options_t *options) +{ + if (options == NULL) + return; + + if (options->cell_options != NULL) { + destroy_cell_opt_container(options->cell_options); + } + F_FREE(options); +} + + + + +/******************************************************** + End of file "options.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 */ +fort_status_t get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols); + + + +fort_row_t *get_row_implementation(ft_table_t *table, size_t row, enum PolicyOnNull policy) +{ + if (table == NULL || table->rows == NULL) { + return NULL; + } + + switch (policy) { + case DoNotCreate: + if (row < vector_size(table->rows)) { + return *(fort_row_t **)vector_at(table->rows, row); + } + return NULL; + break; + case Create: + while (row >= vector_size(table->rows)) { + fort_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 *(fort_row_t **)vector_at(table->rows, row); + break; + } + return NULL; +} + +fort_row_t *get_row(ft_table_t *table, size_t row) +{ + return get_row_implementation(table, row, DoNotCreate); +} + +const fort_row_t *get_row_c(const ft_table_t *table, size_t row) +{ + return get_row((ft_table_t *)table, row); +} + +fort_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row) +{ + return get_row_implementation(table, row, Create); +} + + + + +string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t *table) +{ + assert(table); + + fort_row_t *row = get_row_and_create_if_not_exists(table, table->cur_row); + if (row == NULL) + return NULL; + fort_cell_t *cell = get_cell_and_create_if_not_exists(row, table->cur_col); + if (cell == NULL) + return NULL; + + return cell_get_string_buffer(cell); +} + + +/* + * Returns number of cells (rows * cols) + */ +fort_status_t 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); + fort_row_t *row = NULL; + FOR_EACH(fort_row_t *, row, table->rows) { + size_t cols_in_row = columns_in_row(row); + if (cols_in_row > *cols) + *cols = cols_in_row; + } + } + return FT_SUCCESS; +} + +fort_status_t 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) +{ + if (table == NULL) { + return FT_ERROR; + } + + + + 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(sizeof(size_t), cols); + size_t *row_height_arr = (size_t *)F_CALLOC(sizeof(size_t), rows); + if (col_width_arr == NULL || row_height_arr == NULL) { + F_FREE(col_width_arr); + F_FREE(row_height_arr); + return FT_ERROR; + } + + int combined_cells_found = 0; + context_t context; + context.table_options = (table->options ? table->options : &g_table_options); + 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 fort_row_t *row_p = get_row_c(table, row); + const fort_cell_t *cell = get_cell_c(row_p, col); + context.column = col; + context.row = row; + if (cell) { + switch (get_cell_type(cell)) { + case CommonCell: + col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, &context)); + break; + case GroupMasterCell: + combined_cells_found = 1; + break; + case GroupSlaveCell: + ; /* Do nothing */ + break; + } + row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, &context)); + } + } + } + + if (combined_cells_found) { + col = 0; + for (col = 0; col < cols; ++col) { + size_t row = 0; + for (row = 0; row < rows; ++row) { + const fort_row_t *row_p = get_row_c(table, row); + const fort_cell_t *cell = get_cell_c(row_p, col); + context.column = col; + context.row = row; + if (cell) { + if (get_cell_type(cell) == GroupMasterCell) { + size_t hint_width = hint_width_cell(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->options) { + for (size_t i = 0; i < cols; ++i) { + col_width_arr[i] = MAX((int)col_width_arr[i], fort_options_column_width(table->options, 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 + */ +fort_status_t table_geometry(const ft_table_t *table, size_t *height, size_t *width) +{ + if (table == NULL) + return FT_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); + 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); + + if (table->options) { + *height += table->options->entire_table_options.top_margin; + *height += table->options->entire_table_options.bottom_margin; + *width += table->options->entire_table_options.left_margin; + *width += table->options->entire_table_options.right_margin; + } + + /* Take into account that border elements can be more than one byte long */ + fort_table_options_t *table_options = table->options ? table->options : &g_table_options; + size_t max_border_elem_len = max_border_elem_strlen(table_options); + *width *= max_border_elem_len; + + return FT_SUCCESS; + +} + + +/******************************************************** + End of file "table.c" + ********************************************************/ + + +/******************************************************** + Begin of file "fort_impl.c" + ********************************************************/ + +/* #include "fort_impl.h" */ /* Commented by amalgamation script */ +#ifdef FT_HAVE_WCHAR +#include +#endif + + + +/***************************************************************************** + * LIBFORT helpers + *****************************************************************************/ + +#ifndef FT_MICROSOFT_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; +} + +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) { +#ifndef FT_MICROSOFT_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; + } + +} + + +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; +} + +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; +} + +size_t number_of_columns_in_format_string(const char *fmt) +{ + int separator_counter = 0; + const char *pos = fmt; + while (1) { + pos = strchr(pos, FORT_COL_SEPARATOR); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} + +size_t number_of_columns_in_format_wstring(const wchar_t *fmt) +{ + int separator_counter = 0; + const wchar_t *pos = fmt; + while (1) { + pos = wcschr(pos, FORT_COL_SEPARATOR); + if (pos == NULL) + break; + + separator_counter++; + ++pos; + } + return separator_counter + 1; +} + + +//int snprint_n_chars(char *buf, size_t length, size_t n, char ch) +//{ +// if (length <= n) +// return -1; + +// if (n == 0) +// return 0; + +// /* To ensure valid return value it is safely not print such big strings */ +// if (n > INT_MAX) +// return -1; + +// int status = snprintf(buf, length, "%0*d", (int)n, 0); +// if (status < 0) +// return status; + +// size_t i = 0; +// for (i = 0; i < n; ++i) { +// *buf = ch; +// buf++; +// } +// return (int)n; +//} + +int snprint_n_strings(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; + + 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); +} + + +//int wsnprint_n_chars(wchar_t *buf, size_t length, size_t n, wchar_t ch) +//{ +// if (length <= n) +// return -1; + +// if (n == 0) +// return 0; + +// /* To ensure valid return value it is safely not print such big strings */ +// if (n > INT_MAX) +// return -1; + +// int status = swprintf(buf, length, L"%0*d", (int)n, 0); +// if (status < 0) +// return status; + +// size_t i = 0; +// for (i = 0; i < n; ++i) { +// *buf = ch; +// buf++; +// } +// return (int)n; +//} + +int wsnprint_n_string(wchar_t *buf, size_t length, size_t n, const char *str) +{ + size_t str_len = strlen(str); + + /* This function doesn't work properly with multibyte characters + * so it is better return an error in this case + */ + if (str_len > 1) + return -1; + + 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; + + 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); +} + + +/******************************************************** + End of file "fort_impl.c" + ********************************************************/ + + +/******************************************************** + Begin of file "vector.c" + ********************************************************/ + +/* #include "vector.h" */ /* Commented by amalgamation script */ +#include +#include + +/***************************************************************************** + * VECTOR IMPLEMENTATIONS + * ***************************************************************************/ + +struct vector { + size_t m_size; + void *m_data; + size_t m_capacity; + size_t m_item_size; +}; + +static int vector_reallocate_(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; +} + +/* ------------ Constructors & Destructors ----------------------------- */ + +vector_t *create_vector(size_t item_size, size_t capacity) +{ + vector_t *vector = (vector_t *)F_MALLOC(sizeof(vector_t)); + if (vector == NULL) { + SYS_LOG_ERROR("Failed to allocate memory for asock vector"); + return NULL; + } + + size_t init_size = MAX(item_size * capacity, 1); + vector->m_data = F_MALLOC(init_size); + if (vector->m_data == NULL) { + SYS_LOG_ERROR("Failed to allocate memory for asock vector inern. buffer"); + F_FREE(vector); + return NULL; + } + + vector->m_size = 0; + vector->m_capacity = capacity; + vector->m_item_size = item_size; + + return vector; +} + + +void destroy_vector(vector_t *vector) +{ + assert(vector); + F_FREE(vector->m_data); + F_FREE(vector); +} + +vector_t *copy_vector(vector_t *v) +{ + if (v == NULL) + return NULL; + + 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; +} + + + +/* ----------- Nonmodifying functions --------------------------------- */ + +size_t vector_size(const vector_t *vector) +{ + assert(vector); + return vector->m_size; +} + + +size_t vector_capacity(const vector_t *vector) +{ + assert(vector); + return vector->m_capacity; +} + +size_t vector_index_of(const 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; +} + + +/* ----------- Modifying functions ------------------------------------- */ + +int vector_push(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_ERROR; + vector->m_capacity = vector->m_capacity * 2; + } + + ptrdiff_t deviation = vector->m_size * vector->m_item_size; + memcpy((char *)vector->m_data + deviation, item, vector->m_item_size); + + ++(vector->m_size); + + return FT_SUCCESS; +} + + +int vector_erase(vector_t *vector, size_t index) +{ + assert(vector); + + if (vector->m_size == 0 || index >= vector->m_size) + return FT_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; +} + + +void vector_clear(vector_t *vector) +{ + vector->m_size = 0; +} + +const void *vector_at_c(const vector_t *vector, size_t index) +{ + if (index >= vector->m_size) + return NULL; + + return (char *)vector->m_data + index * vector->m_item_size; +} + + +void *vector_at(vector_t *vector, size_t index) +{ + if (index >= vector->m_size) + return NULL; + + return (char *)vector->m_data + index * vector->m_item_size; +} + +fort_status_t vector_swap(vector_t *cur_vec, 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 (cur_sz < min_targ_size) { + if (vector_reallocate_(cur_vec, min_targ_size) == -1) + return FT_ERROR; + cur_vec->m_capacity = min_targ_size; + } + + ptrdiff_t deviation = 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; + } + } + + memcpy(tmp, + (char *)cur_vec->m_data + deviation, + cur_vec->m_item_size * (cur_sz - pos)); + memcpy((char *)cur_vec->m_data + deviation, + mv_vec->m_data, + cur_vec->m_item_size * mv_sz); + memcpy(mv_vec->m_data, + tmp, + cur_vec->m_item_size * new_mv_sz); + + mv_vec->m_size = new_mv_sz; + F_FREE(tmp); + return FT_SUCCESS; +} + + + + +/******************************************************** + End of file "vector.c" + ********************************************************/ + + +/******************************************************** + Begin of file "string_buffer.c" + ********************************************************/ + +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +/* #include "options.h" */ /* Commented by amalgamation script */ +/* #include "wcwidth.h" */ /* Commented by amalgamation script */ +#include +#include +#include +/***************************************************************************** + * STRING BUFFER + * ***************************************************************************/ + + +static ptrdiff_t str_iter_width(const char *beg, const char *end) +{ + assert(end >= beg); + return (end - beg); +} + +static ptrdiff_t wcs_iter_width(const wchar_t *beg, const wchar_t *end) +{ + assert(end >= beg); + return mk_wcswidth(beg, (end - beg)); +} + + +static size_t buf_str_len(const string_buffer_t *buf) +{ + assert(buf); + if (buf->type == CharBuf) { + return strlen(buf->str.cstr); + } else { + return wcslen(buf->str.wstr); + } +} + +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; +} + +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; +} + +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; +} + +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; +} + +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; +} + +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; +} + + +string_buffer_t *create_string_buffer(size_t number_of_chars, enum str_buf_type type) +{ + size_t sz = (number_of_chars) * (type == CharBuf ? sizeof(char) : sizeof(wchar_t)); + string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(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 && type == CharBuf) { + result->str.cstr[0] = '\0'; + } else if (sz && type == WCharBuf) { + result->str.wstr[0] = L'\0'; + } + + return result; +} + +void destroy_string_buffer(string_buffer_t *buffer) +{ + if (buffer == NULL) + return; + F_FREE(buffer->str.data); + buffer->str.data = NULL; + F_FREE(buffer); +} + +fort_status_t realloc_string_buffer_without_copy(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; +} + +fort_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str) +{ + assert(buffer); + assert(str); + + size_t sz = strlen(str); + char *copy = F_STRDUP(str); + if (copy == NULL) + return FT_MEMORY_ERROR; + + while (sz >= string_buffer_capacity(buffer)) { + int status = realloc_string_buffer_without_copy(buffer); + if (!FT_IS_SUCCESS(status)) { + return status; + } + } + F_FREE(buffer->str.data); + buffer->str.cstr = copy; + buffer->type = CharBuf; + + return FT_SUCCESS; +} + +fort_status_t fill_buffer_from_wstring(string_buffer_t *buffer, const wchar_t *str) +{ + assert(buffer); + assert(str); + + size_t sz = wcslen(str); + wchar_t *copy = F_WCSDUP(str); + if (copy == NULL) + return FT_MEMORY_ERROR; + + while (sz >= string_buffer_capacity(buffer)) { + int status = realloc_string_buffer_without_copy(buffer); + if (!FT_IS_SUCCESS(status)) { + return status; + } + } + F_FREE(buffer->str.data); + buffer->str.wstr = copy; + buffer->type = WCharBuf; + + return FT_SUCCESS; +} + + + +size_t buffer_text_height(string_buffer_t *buffer) +{ + if (buffer == NULL || buffer->str.data == NULL || buf_str_len(buffer) == 0) { + return 0; + } + if (buffer->type == CharBuf) + return 1 + strchr_count(buffer->str.cstr, '\n'); + else + return 1 + wstrchr_count(buffer->str.wstr, L'\n'); +} + +size_t buffer_text_width(string_buffer_t *buffer) +{ + size_t max_length = 0; + int n = 0; + if (buffer->type == CharBuf) { + 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; + } + } else { + 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, end - beg); + if (line_width < 0) /* For safety */ + line_width = 0; + max_length = MAX(max_length, (size_t)line_width); + + ++n; + } + } +} + + + +int buffer_printf(string_buffer_t *buffer, size_t buffer_row, char *buf, size_t buf_len, const context_t *context) +{ +#define CHAR_TYPE char +#define NULL_CHAR '\0' +#define NEWLINE_CHAR '\n' +#define SPACE_CHAR " " +#define SNPRINTF_FMT_STR "%*s" +#define SNPRINTF snprintf +#define BUFFER_STR str.cstr +//#define SNPRINT_N_CHARS snprint_n_chars +#define SNPRINT_N_STRINGS snprint_n_strings +#define STR_N_SUBSTRING str_n_substring +#define STR_ITER_WIDTH str_iter_width + + if (buffer == NULL || buffer->str.data == NULL + || buffer_row >= buffer_text_height(buffer) || buf_len == 0) { + return -1; + } + + size_t content_width = buffer_text_width(buffer); + if ((buf_len - 1) < content_width) + return -1; + + size_t left = 0; + size_t right = 0; + + switch (get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TEXT_ALIGN)) { + case FT_ALIGNED_LEFT: + left = 0; + right = (buf_len - 1) - content_width; + break; + case FT_ALIGNED_CENTER: + left = ((buf_len - 1) - content_width) / 2; + right = ((buf_len - 1) - content_width) - left; + break; + case FT_ALIGNED_RIGHT: + left = (buf_len - 1) - content_width; + right = 0; + break; + default: + assert(0); + break; + } + + int written = 0; + int tmp = 0; + const CHAR_TYPE *beg = NULL; + const CHAR_TYPE *end = NULL; + CHAR_TYPE old_value; + + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, left, SPACE_CHAR)); + + STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); + if (beg == NULL || end == NULL) + return -1; + old_value = *end; + *(CHAR_TYPE *)end = NULL_CHAR; + + ptrdiff_t str_it_width = STR_ITER_WIDTH(beg, end); + if (str_it_width < 0 || content_width < (size_t)str_it_width) + return - 1; + + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); + *(CHAR_TYPE *)end = old_value; + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, right, SPACE_CHAR)); + return written; + +clear: + return -1; + +#undef CHAR_TYPE +#undef NULL_CHAR +#undef NEWLINE_CHAR +#undef SPACE_CHAR +#undef SNPRINTF_FMT_STR +#undef SNPRINTF +#undef BUFFER_STR +//#undef SNPRINT_N_CHARS +#undef SNPRINT_N_STRINGS +#undef STR_N_SUBSTRING +#undef STR_ITER_WIDTH +} + + +int buffer_wprintf(string_buffer_t *buffer, size_t buffer_row, wchar_t *buf, size_t buf_len, const context_t *context) +{ +#define CHAR_TYPE wchar_t +#define NULL_CHAR L'\0' +#define NEWLINE_CHAR L'\n' +#define SPACE_CHAR " " +#define SNPRINTF_FMT_STR L"%*ls" +#define SNPRINTF swprintf +#define BUFFER_STR str.wstr +//#define SNPRINT_N_CHARS wsnprint_n_chars +#define SNPRINT_N_STRINGS wsnprint_n_string +#define STR_N_SUBSTRING wstr_n_substring +#define STR_ITER_WIDTH wcs_iter_width + + if (buffer == NULL || buffer->str.data == NULL + || buffer_row >= buffer_text_height(buffer) || buf_len == 0) { + return -1; + } + + size_t content_width = buffer_text_width(buffer); + if ((buf_len - 1) < content_width) + return -1; + + size_t left = 0; + size_t right = 0; + + switch (get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TEXT_ALIGN)) { + case FT_ALIGNED_LEFT: + left = 0; + right = (buf_len - 1) - content_width; + break; + case FT_ALIGNED_CENTER: + left = ((buf_len - 1) - content_width) / 2; + right = ((buf_len - 1) - content_width) - left; + break; + case FT_ALIGNED_RIGHT: + left = (buf_len - 1) - content_width; + right = 0; + break; + default: + assert(0); + break; + } + + int written = 0; + int tmp = 0; + const CHAR_TYPE *beg = NULL; + const CHAR_TYPE *end = NULL; + CHAR_TYPE old_value; + + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, left, SPACE_CHAR)); + + STR_N_SUBSTRING(buffer->BUFFER_STR, NEWLINE_CHAR, buffer_row, &beg, &end); + if (beg == NULL || end == NULL) + return -1; + old_value = *end; + *(CHAR_TYPE *)end = NULL_CHAR; + + ptrdiff_t str_it_width = STR_ITER_WIDTH(beg, end); + if (str_it_width < 0 || content_width < (size_t)str_it_width) + return - 1; + + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINTF(buf + written, buf_len - written, SNPRINTF_FMT_STR, (int)(end - beg), beg)); + *(CHAR_TYPE *)end = old_value; + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, (content_width - (size_t)str_it_width), SPACE_CHAR)); + CHCK_RSLT_ADD_TO_WRITTEN(SNPRINT_N_STRINGS(buf + written, buf_len - written, right, SPACE_CHAR)); + return written; + +clear: + return -1; + +#undef CHAR_TYPE +#undef NULL_CHAR +#undef NEWLINE_CHAR +#undef SPACE_CHAR +#undef SNPRINTF_FMT_STR +#undef SNPRINTF +#undef BUFFER_STR +//#undef SNPRINT_N_CHARS +#undef SNPRINT_N_STRINGS +#undef STR_N_SUBSTRING +#undef STR_ITER_WIDTH +} + +size_t string_buffer_capacity(const string_buffer_t *buffer) +{ + assert(buffer); + if (buffer->type == CharBuf) + return buffer->data_sz; + else + return buffer->data_sz / sizeof(wchar_t); +} + +void *buffer_get_data(string_buffer_t *buffer) +{ + assert(buffer); + return buffer->str.data; +} + + +/******************************************************** + End of file "string_buffer.c" + ********************************************************/ + + +/******************************************************** + Begin of file "fort.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 +#include +#include +#include "fort.h" +#include +#include +#include +#include + +/* #include "vector.h" */ /* Commented by amalgamation script */ +/* #include "fort_impl.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 "options.h" */ /* Commented by amalgamation script */ + + + + +/***************************************************************************** + * LIBFORT + * ***************************************************************************/ + +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(fort_row_t *), DEFAULT_VECTOR_CAPACITY); + if (result->rows == NULL) { + F_FREE(result); + return NULL; + } + result->separators = create_vector(sizeof(separator_t *), DEFAULT_VECTOR_CAPACITY); + if (result->separators == NULL) { + destroy_vector(result->rows); + F_FREE(result); + return NULL; + } + result->options = 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(*(fort_row_t **)vector_at(table->rows, i)); + } + destroy_vector(table->rows); + } + if (table->separators) { + size_t row_n = vector_size(table->separators); + for (i = 0; i < row_n; ++i) { + destroy_separator(*(separator_t **)vector_at(table->separators, i)); + } + destroy_vector(table->separators); + } + destroy_table_options(table->options); + destroy_string_buffer(table->conv_buffer); + F_FREE(table); +} + +void ft_ln(ft_table_t *table) +{ + assert(table); + table->cur_col = 0; + table->cur_row++; +} + +size_t ft_cur_row(ft_table_t *table) +{ + assert(table); + return table->cur_row; +} + +size_t ft_cur_col(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; +} + + +static int ft_row_printf_impl(ft_table_t *table, size_t row, const char *fmt, va_list *va) +{ +#define CREATE_ROW_FROM_FMT_STRING create_row_from_fmt_string + size_t i = 0; + size_t new_cols = 0; + + if (table == NULL) + return -1; + + fort_row_t *new_row = CREATE_ROW_FROM_FMT_STRING(fmt, va); + + if (new_row == NULL) { + return -1; + } + + fort_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) { + fort_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 = (fort_row_t **)vector_at(table->rows, row); + swap_row(*cur_row_p, new_row, table->cur_col); + + table->cur_col += new_cols; + destroy_row(new_row); + return new_cols; + +clear: + destroy_row(new_row); + return -1; +#undef CREATE_ROW_FROM_FMT_STRING +} + +#ifdef FT_HAVE_WCHAR +static int ft_row_wprintf_impl(ft_table_t *table, size_t row, const wchar_t *fmt, va_list *va) +{ +#define CREATE_ROW_FROM_FMT_STRING create_row_from_fmt_wstring + size_t i = 0; + size_t new_cols = 0; + + if (table == NULL) + return -1; + + fort_row_t *new_row = CREATE_ROW_FROM_FMT_STRING(fmt, va); + + if (new_row == NULL) { + return -1; + } + + fort_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) { + fort_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 = (fort_row_t **)vector_at(table->rows, row); + swap_row(*cur_row_p, new_row, table->cur_col); + + table->cur_col += new_cols; + destroy_row(new_row); + return new_cols; + +clear: + destroy_row(new_row); + return -1; +#undef CREATE_ROW_FROM_FMT_STRING +} +#endif + +#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); + int result = ft_row_printf_impl(table, table->cur_row, fmt, &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); + int result = ft_row_printf_impl(table, table->cur_row, fmt, &va); + if (result >= 0) { + ft_ln(table); + } + va_end(va); + return result; +} + +#undef FT_PRINTF +#undef FT_PRINTF_LN +#undef FT_HDR_PRINTF +#undef FT_HDR_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); + int result = ft_row_wprintf_impl(table, table->cur_row, fmt, &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); + int result = ft_row_wprintf_impl(table, table->cur_row, fmt, &va); + if (result >= 0) { + ft_ln(table); + } + va_end(va); + return result; +} + +#endif + + +static int ft_write_impl(ft_table_t *table, const char *cell_content) +{ + assert(table); + string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table); + if (str_buffer == NULL) + return FT_ERROR; + + int status = fill_buffer_from_string(str_buffer, cell_content); + if (FT_IS_SUCCESS(status)) { + table->cur_col++; + } + return status; +} + + +#ifdef FT_HAVE_WCHAR + +static int ft_wwrite_impl(ft_table_t *table, const wchar_t *cell_content) +{ + assert(table); + string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table); + if (str_buffer == NULL) + return FT_ERROR; + + int status = fill_buffer_from_wstring(str_buffer, cell_content); + if (FT_IS_SUCCESS(status)) { + table->cur_col++; + } + return status; +} + +#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)) + 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)) + 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 + + + + + + +/***************************************************************************** + * TABLE + * ***************************************************************************/ + + + +const char *ft_to_string(const ft_table_t *table) +{ + typedef char char_type; + const char_type *empty_string = ""; + const enum str_buf_type buf_type = CharBuf; + const char *space_char = " "; + const char *new_line_char = "\n"; +#define cur_F_STRDUP F_STRDUP + int (*snprintf_row_)(const fort_row_t *, char *, size_t, size_t *, size_t, size_t, const context_t *) = snprintf_row; + int (*print_row_separator_)(char *, size_t, + const size_t *, size_t, + const fort_row_t *, const fort_row_t *, + enum HorSeparatorPos, const separator_t *, + const context_t *) = print_row_separator; +// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; + int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; + assert(table); + + /* Determing size of table string representation */ + size_t height = 0; + size_t width = 0; + int status = table_geometry(table, &height, &width); + if (FT_IS_ERROR(status)) { + return NULL; + } + size_t sz = height * width + 1; + + /* Allocate string buffer for string representation */ + if (table->conv_buffer == NULL) { + ((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); + if (table->conv_buffer == NULL) + return NULL; + } + while (string_buffer_capacity(table->conv_buffer) < sz) { + if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { + return NULL; + } + } + char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); + + + size_t cols = 0; + size_t rows = 0; + size_t *col_width_arr = NULL; + size_t *row_height_arr = NULL; + status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows); + if (FT_IS_ERROR(status)) + return NULL; + + if (rows == 0) + return cur_F_STRDUP(empty_string); + + int written = 0; + int tmp = 0; + size_t i = 0; + context_t context; + context.table_options = (table->options ? table->options : &g_table_options); + fort_row_t *prev_row = NULL; + fort_row_t *cur_row = NULL; + separator_t *cur_sep = NULL; + size_t sep_size = vector_size(table->separators); + + /* Print top margin */ + for (i = 0; i < context.table_options->entire_table_options.top_margin; ++i) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); + } + + for (i = 0; i < rows; ++i) { + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + cur_row = *(fort_row_t **)vector_at(table->rows, i); + enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; + context.row = i; + CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); + CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); + prev_row = cur_row; + } + cur_row = NULL; + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); + + /* Print bottom margin */ + for (i = 0; i < context.table_options->entire_table_options.bottom_margin; ++i) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); + } + + + F_FREE(col_width_arr); + F_FREE(row_height_arr); + return buffer; + +clear: + F_FREE(col_width_arr); + F_FREE(row_height_arr); +// F_FREE(buffer); + return NULL; +#undef cur_F_STRDUP +} + + +#ifdef FT_HAVE_WCHAR + +const wchar_t *ft_to_wstring(const ft_table_t *table) +{ + typedef wchar_t char_type; + const char_type *empty_string = L""; + const enum str_buf_type buf_type = WCharBuf; + const char *space_char = " "; + const char *new_line_char = "\n"; +#define cur_F_STRDUP F_WCSDUP + int (*snprintf_row_)(const fort_row_t *, wchar_t *, size_t, size_t *, size_t, size_t, const context_t *) = wsnprintf_row; + int (*print_row_separator_)(wchar_t *, size_t, + const size_t *, size_t, + const fort_row_t *, const fort_row_t *, + enum HorSeparatorPos, const separator_t *, + const context_t *) = wprint_row_separator; +// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; + int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; + + + assert(table); + + /* Determing size of table string representation */ + size_t height = 0; + size_t width = 0; + int status = table_geometry(table, &height, &width); + if (FT_IS_ERROR(status)) { + return NULL; + } + size_t sz = height * width + 1; + + /* Allocate string buffer for string representation */ + if (table->conv_buffer == NULL) { + ((ft_table_t *)table)->conv_buffer = create_string_buffer(sz, buf_type); + if (table->conv_buffer == NULL) + return NULL; + } + while (string_buffer_capacity(table->conv_buffer) < sz) { + if (FT_IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) { + return NULL; + } + } + char_type *buffer = (char_type *)buffer_get_data(table->conv_buffer); + + + size_t cols = 0; + size_t rows = 0; + size_t *col_width_arr = NULL; + size_t *row_height_arr = NULL; + status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows); + + if (rows == 0) + return cur_F_STRDUP(empty_string); + + if (FT_IS_ERROR(status)) + return NULL; + + int written = 0; + int tmp = 0; + size_t i = 0; + context_t context; + context.table_options = (table->options ? table->options : &g_table_options); + fort_row_t *prev_row = NULL; + fort_row_t *cur_row = NULL; + separator_t *cur_sep = NULL; + size_t sep_size = vector_size(table->separators); + + /* Print top margin */ + for (i = 0; i < context.table_options->entire_table_options.top_margin; ++i) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); + } + + for (i = 0; i < rows; ++i) { + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + cur_row = *(fort_row_t **)vector_at(table->rows, i); + enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator; + context.row = i; + CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, separatorPos, cur_sep, &context)); + CHCK_RSLT_ADD_TO_WRITTEN(snprintf_row_(cur_row, buffer + written, sz - written, col_width_arr, cols, row_height_arr[i], &context)); + prev_row = cur_row; + } + cur_row = NULL; + cur_sep = (i < sep_size) ? (*(separator_t **)vector_at(table->separators, i)) : NULL; + CHCK_RSLT_ADD_TO_WRITTEN(print_row_separator_(buffer + written, sz - written, col_width_arr, cols, prev_row, cur_row, BottomSeparator, cur_sep, &context)); + + /* Print bottom margin */ + for (i = 0; i < context.table_options->entire_table_options.bottom_margin; ++i) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, width - 1/* minus new_line*/, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, sz - written, 1, new_line_char)); + } + + F_FREE(col_width_arr); + F_FREE(row_height_arr); + return buffer; + +clear: + F_FREE(col_width_arr); + F_FREE(row_height_arr); +// F_FREE(buffer); + return NULL; +#undef cur_F_STRDUP +} + +#endif + + +/* + * TMP + */ +//static int dummy_function(void) +//#if defined(FT_CLANG_COMPILER) || defined(FT_GCC_COMPILER) +//__attribute__ ((unused)) +//#endif +//; + +//static int dummy_function(void) +//{ +// if (0) { +// vector_t *v = create_vector(1, DEFAULT_VECTOR_CAPACITY); +// vector_clear(v); +// vector_erase(v, 0); +// vector_index_of(v, NULL); +// vector_capacity(v); +// } +// return 0; +//} + + + + +int ft_add_separator(ft_table_t *table) +{ + assert(table); + assert(table->separators); + + while (vector_size(table->separators) <= table->cur_row) { + 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; + } + + separator_t **sep_p = (separator_t **)vector_at(table->separators, table->cur_row); + if (*sep_p == NULL) + *sep_p = create_separator(F_TRUE); + else + (*sep_p)->enabled = F_TRUE; + + if (*sep_p == NULL) + return FT_ERROR; + return FT_SUCCESS; +} + + + + +/* ******************************************************************************* */ + + +struct ft_border_style *FT_BASIC_STYLE = (struct ft_border_style *) &FORT_BASIC_STYLE; +struct ft_border_style *FT_SIMPLE_STYLE = (struct ft_border_style *) &FORT_SIMPLE_STYLE; +struct ft_border_style *FT_PLAIN_STYLE = (struct ft_border_style *) &FORT_PLAIN_STYLE; +struct ft_border_style *FT_DOT_STYLE = (struct ft_border_style *) &FORT_DOT_STYLE; +struct ft_border_style *FT_EMPTY_STYLE = (struct ft_border_style *) &FORT_EMPTY_STYLE; +struct ft_border_style *FT_SOLID_STYLE = (struct ft_border_style *) &FORT_SOLID_STYLE; +struct ft_border_style *FT_SOLID_ROUND_STYLE = (struct ft_border_style *) &FORT_SOLID_ROUND_STYLE; +struct ft_border_style *FT_DOUBLE_STYLE = (struct ft_border_style *) &FORT_DOUBLE_STYLE; +struct ft_border_style *FT_DOUBLE2_STYLE = (struct ft_border_style *) &FORT_DOUBLE2_STYLE; +struct ft_border_style *FT_BOLD_STYLE = (struct ft_border_style *) &FORT_BOLD_STYLE; +struct ft_border_style *FT_BOLD2_STYLE = (struct ft_border_style *) &FORT_BOLD2_STYLE; +struct ft_border_style *FT_FRAME_STYLE = (struct ft_border_style *) &FORT_FRAME_STYLE; + + + +static void set_border_options_for_options(fort_table_options_t *options, struct ft_border_style *style) +{ + if ((struct fort_border_style *)style == &FORT_BASIC_STYLE + || (struct fort_border_style *)style == &FORT_SIMPLE_STYLE + || (struct fort_border_style *)style == &FORT_DOT_STYLE + || (struct fort_border_style *)style == &FORT_PLAIN_STYLE + || (struct fort_border_style *)style == &FORT_EMPTY_STYLE + || (struct fort_border_style *)style == &FORT_SOLID_STYLE + || (struct fort_border_style *)style == &FORT_SOLID_ROUND_STYLE + || (struct fort_border_style *)style == &FORT_DOUBLE_STYLE + || (struct fort_border_style *)style == &FORT_DOUBLE2_STYLE + || (struct fort_border_style *)style == &FORT_BOLD_STYLE + || (struct fort_border_style *)style == &FORT_BOLD2_STYLE + || (struct fort_border_style *)style == &FORT_FRAME_STYLE) { + memcpy(&(options->border_style), (struct fort_border_style *)style, sizeof(struct fort_border_style)); + return; + } + + struct ft_border_chars *border_chs = &(style->border_chs); + struct ft_border_chars *header_border_chs = &(style->header_border_chs); + +#define BOR_CHARS options->border_style.border_chars +#define H_BOR_CHARS options->border_style.header_border_chars +#define SEP_CHARS options->border_style.separator_chars + + /* + BOR_CHARS[TL_bip] = BOR_CHARS[TT_bip] = BOR_CHARS[TV_bip] = BOR_CHARS[TR_bip] = border_chs->top_border_ch; + BOR_CHARS[LH_bip] = BOR_CHARS[IH_bip] = BOR_CHARS[II_bip] = BOR_CHARS[RH_bip] = border_chs->separator_ch; + BOR_CHARS[BL_bip] = BOR_CHARS[BB_bip] = BOR_CHARS[BV_bip] = BOR_CHARS[BR_bip] = border_chs->bottom_border_ch; + BOR_CHARS[LL_bip] = BOR_CHARS[IV_bip] = BOR_CHARS[RR_bip] = border_chs->side_border_ch; + + H_BOR_CHARS[TL_bip] = H_BOR_CHARS[TT_bip] = H_BOR_CHARS[TV_bip] = H_BOR_CHARS[TR_bip] = header_border_chs->top_border_ch; + H_BOR_CHARS[LH_bip] = H_BOR_CHARS[IH_bip] = H_BOR_CHARS[II_bip] = H_BOR_CHARS[RH_bip] = header_border_chs->separator_ch; + H_BOR_CHARS[BL_bip] = H_BOR_CHARS[BB_bip] = H_BOR_CHARS[BV_bip] = H_BOR_CHARS[BR_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; + */ + + 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; + +// if (border_chs->separator_ch == '\0' && border_chs->in_intersect_ch == '\0') { +// BOR_CHARS[LH_bip] = BOR_CHARS[RH_bip] = '\0'; +// } + 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; + +// if (header_border_chs->separator_ch == '\0' && header_border_chs->in_intersect_ch == '\0') { +// H_BOR_CHARS[LH_bip] = H_BOR_CHARS[RH_bip] = '\0'; +// } + 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[IH_sip] = style->hor_separator_char; + + +#undef BOR_CHARS +#undef H_BOR_CHARS +#undef SEP_CHARS +} + + +int ft_set_default_border_style(struct ft_border_style *style) +{ + set_border_options_for_options(&g_table_options, style); + return FT_SUCCESS; +} + +int ft_set_border_style(ft_table_t *table, struct ft_border_style *style) +{ + assert(table); + if (table->options == NULL) { + table->options = create_table_options(); + if (table->options == NULL) + return FT_MEMORY_ERROR; + } + set_border_options_for_options(table->options, style); + return FT_SUCCESS; +} + + + +int ft_set_cell_option(ft_table_t *table, size_t row, size_t col, uint32_t option, int value) +{ + assert(table); + + if (table->options == NULL) { + table->options = create_table_options(); + if (table->options == NULL) + return FT_MEMORY_ERROR; + } + if (table->options->cell_options == NULL) { + table->options->cell_options = create_cell_opt_container(); + if (table->options->cell_options == NULL) { + return FT_ERROR; + } + } + + if (row == FT_CUR_ROW) + row = table->cur_row; + if (row == FT_CUR_COLUMN) + col = table->cur_col; + + return set_cell_option(table->options->cell_options, row, col, option, value); +} + +int ft_set_default_cell_option(uint32_t option, int value) +{ + return set_default_cell_option(option, value); +} + + +int ft_set_default_tbl_option(uint32_t option, int value) +{ + return set_default_entire_table_option(option, value); +} + +int ft_set_tbl_option(ft_table_t *table, uint32_t option, int value) +{ + assert(table); + + if (table->options == NULL) { + table->options = create_table_options(); + if (table->options == NULL) + return FT_MEMORY_ERROR; + } + return set_entire_table_option(table->options, option, value); +} + +void ft_set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void *ptr)) +{ + set_memory_funcs(f_malloc, f_free); +} + +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; + + fort_row_t *row_p = get_row_and_create_if_not_exists(table, row); + if (row_p == NULL) + return FT_ERROR; + + return row_set_cell_span(row_p, col, hor_span); +} + +/******************************************************** + End of file "fort.c" + ********************************************************/ + + +/******************************************************** + Begin of file "row.c" + ********************************************************/ + +/* #include "row.h" */ /* Commented by amalgamation script */ +/* #include "cell.h" */ /* Commented by amalgamation script */ +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +#include "assert.h" +/* #include "vector.h" */ /* Commented by amalgamation script */ +#include "ctype.h" + +struct fort_row { + vector_t *cells; + /*enum ft_row_type type;*/ +}; + + + +fort_row_t *create_row(void) +{ + fort_row_t *row = (fort_row_t *)F_CALLOC(sizeof(fort_row_t), 1); + if (row == NULL) + return NULL; + row->cells = create_vector(sizeof(fort_cell_t *), DEFAULT_VECTOR_CAPACITY); + if (row->cells == NULL) { + F_FREE(row); + return NULL; + } + + /* + row->is_header = F_FALSE; + row->type = FT_ROW_COMMON; + */ + return row; +} + +void destroy_row(fort_row_t *row) +{ + size_t i = 0; + if (row == NULL) + return; + + if (row->cells) { + size_t cells_n = vector_size(row->cells); + for (i = 0; i < cells_n; ++i) { + fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, i); + destroy_cell(cell); + } + destroy_vector(row->cells); + } + + F_FREE(row); +} + + + + + +size_t columns_in_row(const fort_row_t *row) +{ + if (row == NULL || row->cells == NULL) + return 0; + + return vector_size(row->cells); +} + + +fort_cell_t *get_cell_implementation(fort_row_t *row, size_t col, enum PolicyOnNull policy) +{ + if (row == NULL || row->cells == NULL) { + return NULL; + } + + switch (policy) { + case DoNotCreate: + if (col < columns_in_row(row)) { + return *(fort_cell_t **)vector_at(row->cells, col); + } + return NULL; + break; + case Create: + while (col >= columns_in_row(row)) { + fort_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 *(fort_cell_t **)vector_at(row->cells, col); + break; + } + return NULL; +} + + +fort_cell_t *get_cell(fort_row_t *row, size_t col) +{ + return get_cell_implementation(row, col, DoNotCreate); +} + +const fort_cell_t *get_cell_c(const fort_row_t *row, size_t col) +{ + return get_cell((fort_row_t *)row, col); +} + +fort_cell_t *get_cell_and_create_if_not_exists(fort_row_t *row, size_t col) +{ + return get_cell_implementation(row, col, Create); +} + +fort_status_t swap_row(fort_row_t *cur_row, fort_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) { + fort_row_t tmp; + memcpy(&tmp, cur_row, sizeof(fort_row_t)); + memcpy(cur_row, ins_row, sizeof(fort_row_t)); + memcpy(ins_row, &tmp, sizeof(fort_row_t)); + return FT_SUCCESS; + } + + return vector_swap(cur_row->cells, ins_row->cells, pos); +} + +size_t group_cell_number(const fort_row_t *row, size_t master_cell_col) +{ + assert(row); + const fort_cell_t *cell = get_cell_c(row, master_cell_col); + if (cell == NULL) + return 0; + + size_t total_cols = vector_size(row->cells); + size_t slave_col = master_cell_col + 1; + while (slave_col < total_cols) { + const fort_cell_t *cell = get_cell_c(row, slave_col); + if (cell && get_cell_type(cell) == GroupSlaveCell) { + ++slave_col; + } else { + break; + } + } + return slave_col - master_cell_col; +} + +int get_row_cell_types(const fort_row_t *row, enum CellType *types, size_t types_sz) +{ + assert(row); + assert(types); + size_t i = 0; + for (i = 0; i < types_sz; ++i) { + const fort_cell_t *cell = get_cell_c(row, i); + if (cell) { + types[i] = get_cell_type(cell); + } else { + types[i] = CommonCell; + } + } + return FT_SUCCESS; +} + +fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_span) +{ + assert(row); + + if (hor_span < 2) + return FT_EINVAL; + + fort_cell_t *main_cell = get_cell_and_create_if_not_exists(row, cell_column); + if (main_cell == NULL) { + return FT_ERROR; + } + set_cell_type(main_cell, GroupMasterCell); + --hor_span; + ++cell_column; + + while (hor_span) { + fort_cell_t *slave_cell = get_cell_and_create_if_not_exists(row, cell_column); + if (slave_cell == NULL) { + return FT_ERROR; + } + set_cell_type(slave_cell, GroupSlaveCell); + --hor_span; + ++cell_column; + } + + return FT_SUCCESS; +} + + + + +int print_row_separator(char *buffer, size_t buffer_sz, + const size_t *col_width_arr, size_t cols, + const fort_row_t *upper_row, const fort_row_t *lower_row, + enum HorSeparatorPos separatorPos, + const separator_t *sep, const context_t *context) +{ +// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; + int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; + + assert(buffer); + assert(context); + + const char *space_char = " "; + int status = -1; + + /* Get cell types + * + * Regions above top row and below bottom row areconsidered full of virtual + * GroupSlaveCell cells + */ + enum CellType *top_row_types = F_MALLOC(sizeof(enum CellType) * cols * 2); + if (top_row_types == NULL) { + return FT_MEMORY_ERROR; + } + enum CellType *bottom_row_types = top_row_types + cols; + if (upper_row) { + get_row_cell_types(upper_row, top_row_types, cols); + } else { + size_t i = 0; + for (i = 0; i < cols; ++i) + top_row_types[i] = GroupSlaveCell; + } + if (lower_row) { + get_row_cell_types(lower_row, bottom_row_types, cols); + } else { + size_t i = 0; + for (i = 0; i < cols; ++i) + bottom_row_types[i] = GroupSlaveCell; + } + + + int 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_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_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_opt_value_hierarcial(context->table_options, context->row - 1, FT_ANY_COLUMN, FT_COPT_ROW_TYPE); + } + + /* Row separator anatomy + * + * | C11 | C12 C13 | C14 C15 | + * L I I I IV I I IT I I I IB I I II I I R + * | C21 | C22 | C23 C24 C25 | + */ + const char **L = NULL; + const char **I = NULL; + const char **IV = NULL; + const char **R = NULL; + const char **IT = NULL; + const char **IB = NULL; + const char **II = NULL; + + + typedef const char *(*border_chars_point_t)[BorderItemPosSize]; + const char *(*border_chars)[BorderItemPosSize] = NULL; + border_chars = (border_chars_point_t)&context->table_options->border_style.border_chars; + if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) { + border_chars = (border_chars_point_t)&context->table_options->border_style.header_border_chars; + } + + if (sep && sep->enabled) { + L = &(context->table_options->border_style.separator_chars[LH_sip]); + I = &(context->table_options->border_style.separator_chars[IH_sip]); + IV = &(context->table_options->border_style.separator_chars[II_sip]); + R = &(context->table_options->border_style.separator_chars[RH_sip]); + + IT = &(context->table_options->border_style.separator_chars[II_sip]); + IB = &(context->table_options->border_style.separator_chars[II_sip]); + II = &(context->table_options->border_style.separator_chars[IH_sip]); + } else { + switch (separatorPos) { + case TopSeparator: + L = &(*border_chars)[TL_bip]; + I = &(*border_chars)[TT_bip]; + IV = &(*border_chars)[TV_bip]; + R = &(*border_chars)[TR_bip]; + + IT = &(*border_chars)[TV_bip]; + IB = &(*border_chars)[TV_bip]; + II = &(*border_chars)[TT_bip]; + break; + case InsideSeparator: + L = &(*border_chars)[LH_bip]; + I = &(*border_chars)[IH_bip]; + IV = &(*border_chars)[II_bip]; + R = &(*border_chars)[RH_bip]; + + IT = &(*border_chars)[TV_bip]; + IB = &(*border_chars)[BV_bip]; + II = &(*border_chars)[IH_bip]; + break; + case BottomSeparator: + L = &(*border_chars)[BL_bip]; + I = &(*border_chars)[BB_bip]; + IV = &(*border_chars)[BV_bip]; + R = &(*border_chars)[BR_bip]; + + IT = &(*border_chars)[BV_bip]; + IB = &(*border_chars)[BV_bip]; + II = &(*border_chars)[BB_bip]; + break; + default: + break; + } + } + + /* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */ +// if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) { +// status = 0; +// goto clear; +// } + if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L))) + && (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I))) + && (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV))) + && (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) { + status = 0; + goto clear; + } + + size_t i = 0; + + /* Print left margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.left_margin, space_char)); + + for (i = 0; i < cols; ++i) { + if (i == 0) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); + } else { + if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) + && (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); + } else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); + } else if (top_row_types[i] == GroupSlaveCell) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); + } else { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); + } + } + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); + } + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); + + /* Print right margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.right_margin, space_char)); + + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); + + status = written; + +clear: + F_FREE(top_row_types); + return status; +} + + + +int wprint_row_separator(wchar_t *buffer, size_t buffer_sz, + const size_t *col_width_arr, size_t cols, + const fort_row_t *upper_row, const fort_row_t *lower_row, + enum HorSeparatorPos separatorPos, const separator_t *sep, + const context_t *context) +{ +// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; + int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; + + assert(buffer); + assert(context); + + const char *space_char = " "; + int status = -1; + + /* Get cell types + * + * Regions above top row and below bottom row areconsidered full of virtual + * GroupSlaveCell cells + */ + enum CellType *top_row_types = F_MALLOC(sizeof(enum CellType) * cols * 2); + if (top_row_types == NULL) { + return FT_MEMORY_ERROR; + } + enum CellType *bottom_row_types = top_row_types + cols; + if (upper_row) { + get_row_cell_types(upper_row, top_row_types, cols); + } else { + size_t i = 0; + for (i = 0; i < cols; ++i) + top_row_types[i] = GroupSlaveCell; + } + if (lower_row) { + get_row_cell_types(lower_row, bottom_row_types, cols); + } else { + size_t i = 0; + for (i = 0; i < cols; ++i) + bottom_row_types[i] = GroupSlaveCell; + } + + + int 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_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_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_opt_value_hierarcial(context->table_options, context->row - 1, FT_ANY_COLUMN, FT_COPT_ROW_TYPE); + } + + /* Row separator anatomy + * + * | C11 | C12 C13 | C14 C15 | + * L I I I IV I I IT I I I IB I I II I I R + * | C21 | C22 | C23 C24 C25 | + */ + const char **L = NULL; + const char **I = NULL; + const char **IV = NULL; + const char **R = NULL; + const char **IT = NULL; + const char **IB = NULL; + const char **II = NULL; + + + typedef const char *(*border_chars_point_t)[BorderItemPosSize]; + const char *(*border_chars)[BorderItemPosSize] = NULL; + border_chars = (border_chars_point_t)&context->table_options->border_style.border_chars; + if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) { + border_chars = (border_chars_point_t)&context->table_options->border_style.header_border_chars; + } + + if (sep && sep->enabled) { + L = &(context->table_options->border_style.separator_chars[LH_sip]); + I = &(context->table_options->border_style.separator_chars[IH_sip]); + IV = &(context->table_options->border_style.separator_chars[II_sip]); + R = &(context->table_options->border_style.separator_chars[RH_sip]); + + IT = &(context->table_options->border_style.separator_chars[II_sip]); + IB = &(context->table_options->border_style.separator_chars[II_sip]); + II = &(context->table_options->border_style.separator_chars[IH_sip]); + } else { + switch (separatorPos) { + case TopSeparator: + L = &(*border_chars)[TL_bip]; + I = &(*border_chars)[TT_bip]; + IV = &(*border_chars)[TV_bip]; + R = &(*border_chars)[TR_bip]; + + IT = &(*border_chars)[TV_bip]; + IB = &(*border_chars)[TV_bip]; + II = &(*border_chars)[TT_bip]; + break; + case InsideSeparator: + L = &(*border_chars)[LH_bip]; + I = &(*border_chars)[IH_bip]; + IV = &(*border_chars)[II_bip]; + R = &(*border_chars)[RH_bip]; + + IT = &(*border_chars)[TV_bip]; + IB = &(*border_chars)[BV_bip]; + II = &(*border_chars)[IH_bip]; + break; + case BottomSeparator: + L = &(*border_chars)[BL_bip]; + I = &(*border_chars)[BB_bip]; + IV = &(*border_chars)[BV_bip]; + R = &(*border_chars)[BR_bip]; + + IT = &(*border_chars)[BV_bip]; + IB = &(*border_chars)[BV_bip]; + II = &(*border_chars)[BB_bip]; + break; + default: + break; + } + } + + /* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */ +// if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) { +// status = 0; +// goto clear; +// } + if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L))) + && (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I))) + && (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV))) + && (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) { + status = 0; + goto clear; + } + + size_t i = 0; + + /* Print left margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.left_margin, space_char)); + + for (i = 0; i < cols; ++i) { + if (i == 0) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L)); + } else { + if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell) + && (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV)); + } else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II)); + } else if (top_row_types[i] == GroupSlaveCell) { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT)); + } else { + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB)); + } + } + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I)); + } + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R)); + + /* Print right margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.right_margin, space_char)); + + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n")); + + status = written; + +clear: + F_FREE(top_row_types); + return status; +} + + + +fort_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'; + fort_status_t (*fill_cell_from_string_)(fort_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; + unsigned int number_of_separators = 0; + + fort_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, FORT_COL_SEPARATOR); + if (pos != NULL) { + *(pos) = zero_char; + ++pos; + number_of_separators++; + } + + fort_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)) { + fort_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 +fort_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'; + fort_status_t (*fill_cell_from_string_)(fort_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; + unsigned int number_of_separators = 0; + + fort_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, FORT_COL_SEPARATOR); + if (pos != NULL) { + *(pos) = zero_char; + ++pos; + number_of_separators++; + } + + fort_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)) { + fort_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 + + +fort_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args) +{ +#define VSNPRINTF vsnprintf +#define STR_FILED cstr +#define CREATE_ROW_FROM_STRING create_row_from_string +#define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_string + + string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); + 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->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); + va_end(va); + /* If error encountered */ + if (virtual_sz < 0) + goto clear; + + /* Successful write */ + if ((size_t)virtual_sz < string_buffer_capacity(buffer)) + 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_STRING(buffer->str.STR_FILED); + if (cols == cols_origin) { + + fort_row_t *row = CREATE_ROW_FROM_STRING(buffer->str.STR_FILED); + if (row == NULL) { + goto clear; + } + + destroy_string_buffer(buffer); + return row; + } + + /* todo: add processing of cols != cols_origin */ + +clear: + destroy_string_buffer(buffer); + return NULL; +#undef VSNPRINTF +#undef STR_FILED +#undef CREATE_ROW_FROM_STRING +#undef NUMBER_OF_COLUMNS_IN_FORMAT_STRING +} + +#ifdef FT_HAVE_WCHAR +fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args) +{ +#define VSNPRINTF vswprintf +#define STR_FILED wstr +#define CREATE_ROW_FROM_STRING create_row_from_wstring +#define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_wstring + + + string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); + 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->str.STR_FILED, string_buffer_capacity(buffer), fmt, va); + va_end(va); + /* If error encountered */ + if (virtual_sz < 0) + goto clear; + + /* Successful write */ + if ((size_t)virtual_sz < string_buffer_capacity(buffer)) + 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_STRING(buffer->str.STR_FILED); + if (cols == cols_origin) { + + fort_row_t *row = CREATE_ROW_FROM_STRING(buffer->str.STR_FILED); + if (row == NULL) { + goto clear; + } + + destroy_string_buffer(buffer); + return row; + } + + /* todo: add processing of cols != cols_origin */ + +clear: + destroy_string_buffer(buffer); + return NULL; +#undef VSNPRINTF +#undef STR_FILED +#undef CREATE_ROW_FROM_STRING +#undef NUMBER_OF_COLUMNS_IN_FORMAT_STRING +} +#endif + + +int snprintf_row(const fort_row_t *row, char *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height, const context_t *context) +{ +// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; + int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; + int (*cell_printf_)(fort_cell_t *, size_t, char *, size_t, const context_t *) = cell_printf; + + + assert(context); + const char *space_char = " "; + const char *new_line_char = "\n"; + + if (row == NULL) + return -1; + + size_t cols_in_row = columns_in_row(row); + if (cols_in_row > col_width_arr_sz) + return -1; + + /* Row separator anatomy + * + * L data IV data IV data R + */ + + typedef const char *(*border_chars_point_t)[BorderItemPosSize]; + enum ft_row_type row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_ROW_TYPE); + const char *(*bord_chars)[BorderItemPosSize] = (row_type == FT_ROW_HEADER) + ? (border_chars_point_t)(&context->table_options->border_style.header_border_chars) + : (border_chars_point_t)(&context->table_options->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]; + + + int written = 0; + int tmp = 0; + size_t i = 0; + for (i = 0; i < row_height; ++i) { + /* Print left margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.left_margin, space_char)); + + /* Print left table boundary */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); + size_t j = 0; + while (j < col_width_arr_sz) { + if (j < cols_in_row) { + ((context_t *)context)->column = j; + fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j); + size_t cell_width = 0; + + size_t group_slave_sz = group_cell_number(row, j); + cell_width = col_width_arr[j]; + size_t slave_j = 0; + size_t master_j = j; + for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { + cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; + ++j; + } + + CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); + } else { + /* Print empty cell */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); + } + + /* Print boundary between cells */ + if (j < col_width_arr_sz - 1) + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); + + ++j; + } + + /* Print right table boundary */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); + + /* Print right margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.right_margin, space_char)); + + /* Print new line character */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); + } + return written; + +clear: + return -1; +} + + +#ifdef FT_HAVE_WCHAR + +int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz, + size_t row_height, const context_t *context) +{ +// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; + int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; + int (*cell_printf_)(fort_cell_t *, size_t, wchar_t *, size_t, const context_t *) = cell_wprintf; + + + assert(context); + const char *space_char = " "; + const char *new_line_char = "\n"; + + if (row == NULL) + return -1; + + size_t cols_in_row = columns_in_row(row); + if (cols_in_row > col_width_arr_sz) + return -1; + + /* Row separator anatomy + * + * L data IV data IV data R + */ + + typedef const char *(*border_chars_point_t)[BorderItemPosSize]; + enum ft_row_type row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_ROW_TYPE); + const char *(*bord_chars)[BorderItemPosSize] = (row_type == FT_ROW_HEADER) + ? (border_chars_point_t)(&context->table_options->border_style.header_border_chars) + : (border_chars_point_t)(&context->table_options->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]; + + + int written = 0; + int tmp = 0; + size_t i = 0; + for (i = 0; i < row_height; ++i) { + /* Print left margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.left_margin, space_char)); + + /* Print left table boundary */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L)); + size_t j = 0; + while (j < col_width_arr_sz) { + if (j < cols_in_row) { + ((context_t *)context)->column = j; + fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j); + size_t cell_width = 0; + + size_t group_slave_sz = group_cell_number(row, j); + cell_width = col_width_arr[j]; + size_t slave_j = 0; + size_t master_j = j; + for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) { + cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH; + ++j; + } + + CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context)); + } else { + /* Print empty cell */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char)); + } + + /* Print boundary between cells */ + if (j < col_width_arr_sz - 1) + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV)); + + ++j; + } + + /* Print right table boundary */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R)); + + /* Print right margin */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.right_margin, space_char)); + + /* Print new line character */ + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char)); + } + return written; + +clear: + return -1; +} + +#endif + + + +/******************************************************** + End of file "row.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 + +struct interval { + int first; + int last; +}; + +/* auxiliary function for binary search in interval table */ +static int bisearch(wchar_t ucs, const struct interval *table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + 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. + */ + +int mk_wcwidth(wchar_t ucs) +{ + /* 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 } + }; + + /* 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))); +} + + +int mk_wcswidth(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcswidth(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +int mk_wcwidth_cjk(wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, + { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, + { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, + { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, + { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, + { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, + { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, + { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, + { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, + { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, + { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, + { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, + { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, + { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, + { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, + { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, + { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, + { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, + { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, + { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, + { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, + { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, + { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, + { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, + { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, + { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } + }; + + /* binary search in table of non-spacing characters */ + if (bisearch(ucs, ambiguous, + sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return mk_wcwidth(ucs); +} + + +int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (; *pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + +/******************************************************** + End of file "wcwidth.c" + ********************************************************/ + + +/******************************************************** + Begin of file "cell.c" + ********************************************************/ + +/* #include "cell.h" */ /* Commented by amalgamation script */ +/* #include "options.h" */ /* Commented by amalgamation script */ +/* #include "string_buffer.h" */ /* Commented by amalgamation script */ +#include + +/***************************************************************************** + * CELL + * ***************************************************************************/ + +struct fort_cell { + string_buffer_t *str_buffer; + enum CellType cell_type; +}; + +fort_cell_t *create_cell(void) +{ + fort_cell_t *cell = (fort_cell_t *)F_CALLOC(sizeof(fort_cell_t), 1); + if (cell == NULL) + return NULL; + cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf); + if (cell->str_buffer == NULL) { + F_FREE(cell); + return NULL; + } + cell->cell_type = CommonCell; + return cell; +} + +void destroy_cell(fort_cell_t *cell) +{ + if (cell == NULL) + return; + destroy_string_buffer(cell->str_buffer); + F_FREE(cell); +} + +void set_cell_type(fort_cell_t *cell, enum CellType type) +{ + assert(cell); + cell->cell_type = type; +} + +enum CellType get_cell_type(const fort_cell_t *cell) +{ + assert(cell); + return cell->cell_type; +} + + +size_t hint_width_cell(const fort_cell_t *cell, const 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); + size_t cell_padding_left = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_LEFT_PADDING); + size_t cell_padding_right = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_RIGHT_PADDING); + size_t result = cell_padding_left + cell_padding_right; + if (cell->str_buffer && cell->str_buffer->str.data) { + result += buffer_text_width(cell->str_buffer); + } + result = MAX(result, (size_t)get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_MIN_WIDTH)); + return result; +} + +size_t hint_height_cell(const fort_cell_t *cell, const context_t *context) +{ + assert(cell); + assert(context); + size_t cell_padding_top = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TOP_PADDING); + size_t cell_padding_bottom = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_BOTTOM_PADDING); + size_t cell_empty_string_height = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_EMPTY_STR_HEIGHT); + size_t result = cell_padding_top + cell_padding_bottom; + if (cell->str_buffer && cell->str_buffer->str.data) { + size_t text_height = buffer_text_height(cell->str_buffer); + result += text_height == 0 ? cell_empty_string_height : text_height; + } + return result; +} + + +/* + * Returns number of lines in cell. If cell is empty or + * contains empty string, then 0 is returned. + */ +/* +static int lines_number_cell(fort_cell_t *cell) +{ + assert(cell); + if (cell->str_buffer == NULL || cell->str_buffer->str == NULL || cell->str_buffer->str[0] == '\0') { + return 0; + } + + int result = 0; + char *pos = cell->str_buffer->str; + while ((pos = strchr(pos, '\n')) != NULL) { + result++; + pos++; + } + return result + 1; +} +*/ + +int cell_printf(fort_cell_t *cell, size_t row, char *buf, size_t buf_len, const context_t *context) +{ + const char *space_char = " "; + int (*buffer_printf_)(string_buffer_t *, size_t, char *, size_t, const context_t *) = buffer_printf; +// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars; + int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings; + + + + if (cell == NULL || buf_len == 0 + || (buf_len <= hint_width_cell(cell, context))) { + return -1; + } + + unsigned int cell_padding_top = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TOP_PADDING); + unsigned int cell_padding_left = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_LEFT_PADDING); + unsigned int cell_padding_right = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_RIGHT_PADDING); + + if (row >= hint_height_cell(cell, context) + || row < cell_padding_top + || row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { + return snprint_n_strings_(buf, buf_len, buf_len - 1, space_char); + } + + + int written = 0; + int tmp = 0; + int left = cell_padding_left; + int right = cell_padding_right; + + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + written, buf_len - written, left, space_char)); + + if (cell->str_buffer) + CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + written, buf_len - written - right, context)); + else + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + written, buf_len - written, buf_len - written - right, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + written, buf_len - written, right, space_char)); + + return written; + +clear: + return -1; +} + +#ifdef FT_HAVE_WCHAR +int cell_wprintf(fort_cell_t *cell, size_t row, wchar_t *buf, size_t buf_len, const context_t *context) +{ + const char *space_char = " "; + int (*buffer_printf_)(string_buffer_t *, size_t, wchar_t *, size_t, const context_t *) = buffer_wprintf; +// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars; + int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string; + + + + if (cell == NULL || buf_len == 0 + || (buf_len <= hint_width_cell(cell, context))) { + return -1; + } + + unsigned int cell_padding_top = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_TOP_PADDING); + unsigned int cell_padding_left = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_LEFT_PADDING); + unsigned int cell_padding_right = get_cell_opt_value_hierarcial(context->table_options, context->row, context->column, FT_COPT_RIGHT_PADDING); + + if (row >= hint_height_cell(cell, context) + || row < cell_padding_top + || row >= (cell_padding_top + buffer_text_height(cell->str_buffer))) { + return snprint_n_strings_(buf, buf_len, buf_len - 1, space_char); + } + + int written = 0; + int tmp = 0; + int left = cell_padding_left; + int right = cell_padding_right; + + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + written, buf_len - written, left, space_char)); + + if (cell->str_buffer) + CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf_(cell->str_buffer, row - cell_padding_top, buf + written, buf_len - written - right, context)); + else + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + written, buf_len - written, buf_len - written - right, space_char)); + CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buf + written, buf_len - written, right, space_char)); + + return written; + +clear: + return -1; +} +#endif + + +fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str) +{ + assert(str); + assert(cell); + + return fill_buffer_from_string(cell->str_buffer, str); +} + +#ifdef FT_HAVE_WCHAR +fort_status_t fill_cell_from_wstring(fort_cell_t *cell, const wchar_t *str) +{ + assert(str); + assert(cell); + + return fill_buffer_from_wstring(cell->str_buffer, str); +} + +#endif + +string_buffer_t *cell_get_string_buffer(fort_cell_t *cell) +{ + assert(cell); + assert(cell->str_buffer); + return cell->str_buffer; +} + + +/******************************************************** + End of file "cell.c" + ********************************************************/ + diff --git a/tests/test_memory_errors.c b/tests/bb_tests/test_memory_errors.c similarity index 100% rename from tests/test_memory_errors.c rename to tests/bb_tests/test_memory_errors.c diff --git a/tests/test_table_basic.c b/tests/bb_tests/test_table_basic.c similarity index 100% rename from tests/test_table_basic.c rename to tests/bb_tests/test_table_basic.c diff --git a/tests/test_table_border_style.c b/tests/bb_tests/test_table_border_style.c similarity index 100% rename from tests/test_table_border_style.c rename to tests/bb_tests/test_table_border_style.c diff --git a/tests/test_table_options.c b/tests/bb_tests/test_table_options.c similarity index 100% rename from tests/test_table_options.c rename to tests/bb_tests/test_table_options.c diff --git a/tests/main_test.c b/tests/main_test.c new file mode 100644 index 0000000..5f61450 --- /dev/null +++ b/tests/main_test.c @@ -0,0 +1,16 @@ +#include "tests.h" +#include +#include "fort.h" + +int main(void) +{ + int status = 0; + +#ifdef FORT_WB_TESTING_ENABLED + status |= run_wb_test_suit(); + fprintf(stderr, "\n"); +#endif + status |= run_bb_test_suit(); + + return status; +} diff --git a/tests/test.c b/tests/test.c deleted file mode 100644 index abebe02..0000000 --- a/tests/test.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "tests.h" -#include -#include "fort.h" - -void run_test_suit(const char *test_suit_name, int n_tests, struct test_case test_suit[]) -{ - fprintf(stderr, " == RUNNING %s ==\n", test_suit_name); - fprintf(stderr, "[==========] Running %d test(s).\n", n_tests); - int i; - for (i = 0; i < n_tests; ++i) { - fprintf(stderr, "[ RUN ] %s\n", test_suit[i].name); - test_suit[i].test(); - fprintf(stderr, "[ OK ] %s\n", test_suit[i].name); - } - fprintf(stderr, "[==========] %d test(s) run.\n", n_tests); - fprintf(stderr, "[ PASSED ] %d test(s).\n", n_tests); -} - - - - -struct test_case wb_test_suit [] = { - {"test_vector_basic", test_vector_basic}, - {"test_vector_stress", test_vector_stress}, - {"test_string_buffer", test_string_buffer}, - {"test_table_sizes", test_table_sizes}, - {"test_table_geometry", test_table_geometry}, -}; - -struct test_case bb_test_suit [] = { - {"test_table_basic", test_table_basic}, -#ifdef FT_HAVE_WCHAR - {"test_wcs_table_boundaries", test_wcs_table_boundaries}, -#endif - {"test_table_write", test_table_write}, - {"test_table_border_style", test_table_border_style}, - {"test_table_cell_options", test_table_cell_options}, - {"test_table_tbl_options", test_table_tbl_options}, - {"test_memory_errors", test_memory_errors}, -}; - - -int main(void) -{ - int wb_n_tests = sizeof(wb_test_suit) / sizeof(wb_test_suit[0]); - run_test_suit("WHITE BOX TEST SUITE", wb_n_tests, wb_test_suit); - - fprintf(stderr, "\n"); - int bb_n_tests = sizeof(bb_test_suit) / sizeof(bb_test_suit[0]); - run_test_suit("BLACK BOX TEST SUITE", bb_n_tests, bb_test_suit); - - return 0; -} diff --git a/tests/test_utility.c b/tests/test_common.c similarity index 73% rename from tests/test_utility.c rename to tests/test_common.c index 1780c59..6706096 100644 --- a/tests/test_utility.c +++ b/tests/test_common.c @@ -132,3 +132,62 @@ struct ft_table *create_test_int_wtable(int set_test_opts) return table; } #endif + + + + +void run_test_suit(const char *test_suit_name, int n_tests, struct test_case test_suit[]) +{ + fprintf(stderr, " == RUNNING %s ==\n", test_suit_name); + fprintf(stderr, "[==========] Running %d test(s).\n", n_tests); + int i; + for (i = 0; i < n_tests; ++i) { + fprintf(stderr, "[ RUN ] %s\n", test_suit[i].name); + test_suit[i].test(); + fprintf(stderr, "[ OK ] %s\n", test_suit[i].name); + } + fprintf(stderr, "[==========] %d test(s) run.\n", n_tests); + fprintf(stderr, "[ PASSED ] %d test(s).\n", n_tests); +} + + + +#ifdef FORT_WB_TESTING_ENABLED +struct test_case wb_test_suit [] = { + {"test_vector_basic", test_vector_basic}, + {"test_vector_stress", test_vector_stress}, + {"test_string_buffer", test_string_buffer}, + {"test_table_sizes", test_table_sizes}, + {"test_table_geometry", test_table_geometry}, +}; +#endif + + +struct test_case bb_test_suit [] = { + {"test_table_basic", test_table_basic}, +#ifdef FT_HAVE_WCHAR + {"test_wcs_table_boundaries", test_wcs_table_boundaries}, +#endif + {"test_table_write", test_table_write}, + {"test_table_border_style", test_table_border_style}, + {"test_table_cell_options", test_table_cell_options}, + {"test_table_tbl_options", test_table_tbl_options}, + {"test_memory_errors", test_memory_errors}, +}; + +#ifdef FORT_WB_TESTING_ENABLED +int run_wb_test_suit(void) +{ + int wb_n_tests = sizeof(wb_test_suit) / sizeof(wb_test_suit[0]); + run_test_suit("WHITE BOX TEST SUITE", wb_n_tests, wb_test_suit); + return 0; +} +#endif + + +int run_bb_test_suit(void) +{ + int bb_n_tests = sizeof(bb_test_suit) / sizeof(bb_test_suit[0]); + run_test_suit("BLACK BOX TEST SUITE", bb_n_tests, bb_test_suit); + return 0; +} diff --git a/tests/tests.h b/tests/tests.h index 88b19e8..6d4e477 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -75,7 +75,10 @@ struct ft_table *create_test_int_wtable(int set_test_opts); void run_test_suit(const char *test_suit_name, int n_tests, struct test_case test_suit []); - +#ifdef FORT_WB_TESTING_ENABLED +int run_wb_test_suit(void); +#endif +int run_bb_test_suit(void); #endif // TESTS_H diff --git a/tests/test_string_buffer.c b/tests/wb_tests/test_string_buffer.c similarity index 100% rename from tests/test_string_buffer.c rename to tests/wb_tests/test_string_buffer.c diff --git a/tests/test_table_geometry.c b/tests/wb_tests/test_table_geometry.c similarity index 100% rename from tests/test_table_geometry.c rename to tests/wb_tests/test_table_geometry.c diff --git a/tests/test_vector.c b/tests/wb_tests/test_vector.c similarity index 100% rename from tests/test_vector.c rename to tests/wb_tests/test_vector.c