From d2798da48daa51035d46b7f413499b8e6d9bb9b8 Mon Sep 17 00:00:00 2001 From: seleznevae Date: Wed, 17 Jan 2018 21:22:57 +0300 Subject: [PATCH] [C] Separated code in different files --- CMakeLists.txt | 13 +- include/fort.h | 77 +-- src/cell.c | 128 +++++ src/cell.h | 38 ++ src/fort.c | 1201 +------------------------------------------ src/fort_impl.c | 51 ++ src/fort_impl.h | 104 ++++ src/options.c | 132 +++++ src/options.h | 105 ++++ src/row.c | 363 +++++++++++++ src/row.h | 48 ++ src/string_buffer.c | 132 +++++ src/string_buffer.h | 30 ++ src/table.c | 186 +++++++ src/table.h | 33 ++ src/vector.c | 144 ++++++ src/vector.h | 35 ++ tests/test_table.c | 5 +- tests/test_vector.c | 4 +- 19 files changed, 1555 insertions(+), 1274 deletions(-) create mode 100644 src/cell.c create mode 100644 src/cell.h create mode 100644 src/fort_impl.c create mode 100644 src/fort_impl.h create mode 100644 src/options.c create mode 100644 src/options.h create mode 100644 src/row.c create mode 100644 src/row.h create mode 100644 src/string_buffer.c create mode 100644 src/string_buffer.h create mode 100644 src/table.c create mode 100644 src/table.h create mode 100644 src/vector.c create mode 100644 src/vector.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 741da3c..005077d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,11 +15,19 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g") -FILE(GLOB_RECURSE FortHeaders "include/*.h" "tests/*.h") +FILE(GLOB_RECURSE FortHeaders "include/*.h" "tests/*.h" "src/*.h" "src/*.h" "src/*.h" "src/*.h" "src/*.h" "src/*.h") add_custom_target(headers SOURCES ${FortHeaders}) -set(FORT_SOURCES src/fort.c) +set(FORT_SOURCES + src/fort.c + src/vector.c + src/string_buffer.c + src/options.c + src/cell.c + src/row.c + src/table.c + src/fort_impl.c) add_executable(${PROJECT_NAME} @@ -31,6 +39,7 @@ include_directories(tests/cmocka-1.1.0/include) link_directories(${CMAKE_SOURCE_DIR}/tests/cmocka-1.1.0/build/src) add_executable(TEST + ${FORT_SOURCES} tests/test.c tests/test_vector.c tests/test_table.c) diff --git a/include/fort.h b/include/fort.h index 02eb32b..93216d8 100644 --- a/include/fort.h +++ b/include/fort.h @@ -30,6 +30,9 @@ SOFTWARE. #include #include + +#include "options.h" + /* * Determine compiler */ @@ -111,82 +114,8 @@ FORT_EXTERN const char* ft_to_string(const FTABLE *FORT_RESTRICT table); -enum TextAlignment -{ - LeftAligned, - CenterAligned, - RightAligned -}; -/***************************************************************************** - * 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 -}; - -struct vector; -typedef struct vector vector_t; - -struct fort_column_options -{ - int col_min_width; - enum TextAlignment align; -}; -typedef struct fort_column_options fort_column_options_t; - -struct fort_table_options -{ - int cell_padding_top; - int cell_padding_bottom; - int cell_padding_left; - int cell_padding_right; - int cell_empty_string_height; - - char border_chars[BorderItemPosSize]; - char header_border_chars[BorderItemPosSize]; - vector_t *col_options; - -}; -typedef struct fort_table_options fort_table_options_t; -typedef fort_table_options_t context_t; FORT_EXTERN int ft_set_default_options(const fort_table_options_t *options); FORT_EXTERN int ft_get_default_options(fort_table_options_t *options); diff --git a/src/cell.c b/src/cell.c new file mode 100644 index 0000000..3475bc1 --- /dev/null +++ b/src/cell.c @@ -0,0 +1,128 @@ +#include "cell.h" +#include "options.h" +#include "string_buffer.h" +#include "assert.h" + +/***************************************************************************** + * CELL + * ***************************************************************************/ + +struct fort_cell +{ + string_buffer_t *str_buffer; + fort_table_options_t *options; +}; + +fort_cell_t * create_cell() +{ + fort_cell_t *cell = F_CALLOC(sizeof(fort_cell_t), 1); + if (cell == NULL) + return NULL; + cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE); + if (cell->str_buffer == NULL) { + F_FREE(cell); + return NULL; + } + cell->options = NULL; +// init_cell_options(&(cell->options)); + return cell; +} + +void destroy_cell(fort_cell_t *cell) +{ + if (cell == NULL) + return; + destroy_string_buffer(cell->str_buffer); + F_FREE(cell->options); + F_FREE(cell); +} + +int hint_width_cell(const fort_cell_t *cell, const context_t *context) +{ + assert(cell); + assert(context); + int result = context->cell_padding_left + context->cell_padding_right; + if (cell->str_buffer && cell->str_buffer->str) { + result += buffer_text_width(cell->str_buffer); + } + return result; +} + +int hint_height_cell(const fort_cell_t *cell, const context_t *context) +{ + assert(cell); + assert(context); + int result = context->cell_padding_top + context->cell_padding_bottom; + if (cell->str_buffer && cell->str_buffer->str) { + size_t text_height = buffer_text_height(cell->str_buffer); + result += text_height == 0 ? context->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, size_t column, char *buf, size_t buf_len, const context_t *context) +{ + if (cell == NULL || buf_len == 0 || row >= hint_height_cell(cell, context) + || (buf_len <= hint_width_cell(cell, context))) { + return -1; + } + + if (row < context->cell_padding_top + || row >= (context->cell_padding_top + buffer_text_height(cell->str_buffer))) { + int k = snprint_n_chars(buf, buf_len, buf_len - 1, ' '); + return k; + } else { + int written = 0; + int left = context->cell_padding_left; + int right = context->cell_padding_right; + + written += snprint_n_chars(buf + written, buf_len - written, left, ' '); + + if (cell->str_buffer) + written += buffer_printf(cell->str_buffer, row - context->cell_padding_top, column, buf + written, buf_len - written - right, context); + else + written += snprint_n_chars(buf + written, buf_len - written, buf_len - written - right, ' '); + written += snprint_n_chars(buf + written, buf_len - written, right, ' '); + + return written; + } + +} + + + +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); +} + +string_buffer_t *cell_get_string_buffer(fort_cell_t *cell) +{ + assert(cell); + assert(cell->str_buffer); + return cell->str_buffer; +} diff --git a/src/cell.h b/src/cell.h new file mode 100644 index 0000000..c26fd67 --- /dev/null +++ b/src/cell.h @@ -0,0 +1,38 @@ +#ifndef CELL_H +#define CELL_H + +#include "fort_impl.h" + + + + +/***************************************************************************** + * CELL + * ***************************************************************************/ +//struct fort_cell +//{ +// string_buffer_t *str_buffer; +// fort_table_options_t *options; +//}; + +fort_cell_t * create_cell(); + + +void destroy_cell(fort_cell_t *cell); +int hint_width_cell(const fort_cell_t *cell, const context_t *context); +int hint_height_cell(const fort_cell_t *cell, const context_t *context); + + +/* + * 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, size_t column, char *buf, size_t buf_len, const context_t *context); + +fort_status_t fill_cell_from_string(fort_cell_t *cell, const char *str); + +string_buffer_t* cell_get_string_buffer(fort_cell_t *cell); + +#endif // CELL_H diff --git a/src/fort.c b/src/fort.c index 8c95ca6..8e3a2af 100644 --- a/src/fort.c +++ b/src/fort.c @@ -33,735 +33,30 @@ SOFTWARE. #include "wchar.h" #include -#define FORT_COL_SEPARATOR '|' +#include "vector.h" +#include "fort_impl.h" +#include "string_buffer.h" +#include "table.h" +#include "row.h" +#include "options.h" -#define FORT_UNUSED __attribute__((unused)) - -#define F_CALLOC calloc -#define F_MALLOC malloc -#define F_REALLOC realloc -#define F_FREE free -#define F_STRDUP fort_strdup - -#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 STR_2_CAT_(arg1, arg2) \ - arg1##arg2 -#define STR_2_CAT(arg1, arg2) \ - STR_2_CAT_(arg1, arg2) - -#define UNIQUE_NAME_(prefix) \ - STR_2_CAT(prefix,__COUNTER__) -#define UNIQUE_NAME(prefix) \ - UNIQUE_NAME_(prefix) - - -/***************************************************************************** - * LOGGER - *****************************************************************************/ -#define SYS_LOG_ERROR(...) - - -/***************************************************************************** - * RETURN CODES - * ***************************************************************************/ -typedef int fort_status_t; -#define F_SUCCESS 0 -#define F_MEMORY_ERROR 1 -#define F_ERROR 2 -#define IS_SUCCESS(arg) ((arg) == F_SUCCESS) -#define IS_ERROR(arg) (!IS_SUCCESS(arg)) - -/***************************************************************************** - * DEFAULT_SIZES - * ***************************************************************************/ -#define DEFAULT_STR_BUF_SIZE 1024 -#define DEFAULT_VECTOR_CAPACITY 10 /***************************************************************************** * UTILITIES * ***************************************************************************/ -static int snprint_n_chars(char *buf, size_t length, size_t n, char ch) -{ - if (length <= n) - return -1; - int status = snprintf(buf, length, "%0*d", (int)n, 0); - if (status < 0) - return status; - for (size_t i = 0; i < n; ++i) { - *buf = ch; - buf++; - } - return n; -} -/***************************************************************************** - * VECTOR - * ***************************************************************************/ -struct vector; -typedef struct vector vector_t; - -#define INVALID_VEC_INDEX ((size_t) -1) - -static vector_t* create_vector(size_t item_size, size_t capacity); -static void destroy_vector(vector_t*); - -static size_t vector_size(const vector_t*); -static size_t vector_capacity(const vector_t*); -static size_t vector_index_of(const vector_t*, const void *item); - -static int vector_push(vector_t*, const void *item); -static int vector_erase(vector_t*, size_t index); -static void vector_clear(vector_t*); -static void* vector_at(vector_t*, size_t index); - - - - -/***************************************************************************** - * COLUMN OPTIONS - * ***************************************************************************/ - -static fort_column_options_t g_column_options = -{ - 0, /* col_min_width*/ - RightAligned, /* align */ -}; - -static fort_column_options_t create_column_options() -{ - fort_column_options_t result; - memset(&result, '\0', sizeof(result)); - memcpy(&result, &g_column_options, sizeof(result)); - return result; -} - -/***************************************************************************** - * OPTIONS - * ***************************************************************************/ - -static fort_table_options_t g_table_options = { - 1, /* cell_padding_top */ - 1, /* cell_padding_bottom */ - 1, /* cell_padding_left */ - 1, /* cell_padding_right */ - 1, /* cell_empty_string_height */ - - /* border_chars */ - { - '+', '-', '+', '+', - '|', '|', '|', - '+', '-', '+', '+', - '+', '-', '+', '+' - }, - - /* header_border_chars */ - { - '+', '-', '+', '+', - '|', '|', '|', - '+', '-', '+', '+', - '+', '-', '+', '+' - }, - - NULL, /* col_options */ -}; - - -static fort_table_options_t* create_table_options() -{ - fort_table_options_t* options = F_CALLOC(sizeof(fort_table_options_t), 1); - if (options == NULL) { - return NULL; - } - memcpy(options, &g_table_options, sizeof(fort_table_options_t)); - return options; -} - -static void destroy_table_options(fort_table_options_t* options) -{ - if (options == NULL) - return; - - if (options->col_options != NULL) { - destroy_vector(options->col_options); - } - F_FREE(options); -} - - -#define FORT_OPTIONS_SET_COLUMN_OPTION(options, column, opt_name, opt_value) \ - assert(options);\ -\ - if (options->col_options == NULL) {\ - options->col_options = create_vector(sizeof(fort_column_options_t), DEFAULT_VECTOR_CAPACITY);\ - if (options->col_options == NULL) \ - return F_MEMORY_ERROR; \ - } \ -\ - while (vector_size(options->col_options) <= column) {\ - fort_column_options_t def_option = create_column_options();\ - vector_push(options->col_options, &def_option);\ - }\ -\ - fort_column_options_t *col_option = (fort_column_options_t*)vector_at(options->col_options, column);\ - col_option->opt_name = opt_value;\ -\ - return F_SUCCESS; - -static fort_status_t fort_options_set_column_min_width(fort_table_options_t *options, size_t column, size_t width) -{ - FORT_OPTIONS_SET_COLUMN_OPTION(options, column, col_min_width, width); -} - -static fort_status_t fort_options_set_column_alignment(fort_table_options_t *options, size_t column, enum TextAlignment al) -{ - FORT_OPTIONS_SET_COLUMN_OPTION(options, column, align, al); -} - -static int fort_options_column_width(const fort_table_options_t *options, size_t column) -{ - assert(options); - if (options->col_options == NULL) - return -1; - - if (vector_size(options->col_options) <= column) - return -1; - - return ((fort_column_options_t*)vector_at(options->col_options, column))->col_min_width; -} - -static int fort_options_column_alignment(const fort_table_options_t *options, size_t column) -{ - assert(options); - - enum TextAlignment align = g_column_options.align; - if (options->col_options == NULL) - return align; - - if (vector_size(options->col_options) <= column) - return align; - - return ((fort_column_options_t*)vector_at(options->col_options, column))->align; -} - - -/***************************************************************************** - * STRING BUFFER - * ***************************************************************************/ -struct string_buffer; -typedef struct string_buffer string_buffer_t; -struct string_buffer -{ - char *str; - size_t str_sz; -}; - -static string_buffer_t* create_string_buffer(size_t sz); -static void destroy_string_buffer(string_buffer_t *buffer); -static fort_status_t realloc_string_buffer_without_copy(string_buffer_t *buffer); -static fort_status_t fill_buffer_from_string(string_buffer_t *buffer, const char *str); - -static size_t buffer_text_height(string_buffer_t *buffer) -{ - if (buffer == NULL || buffer->str == NULL || strlen(buffer->str) == 0) { - return 0; - } - return 1; -} - -static size_t buffer_text_width(string_buffer_t *buffer) -{ - if (buffer == NULL || buffer->str == NULL) { - return 0; - } - return strlen(buffer->str); -} - -static int buffer_printf(string_buffer_t *buffer, size_t buffer_row, size_t table_column, char *buf, size_t buf_len, const context_t *context) -{ - if (buffer == NULL || buffer->str == 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; - - int left = 0; - int right = 0; - - switch (fort_options_column_alignment(context, table_column)) { - case LeftAligned: - left = 0; - right = (buf_len - 1) - content_width; - break; - case CenterAligned: - left = ((buf_len - 1) - content_width) / 2; - right = ((buf_len - 1) - content_width) - left; - break; - case RightAligned: - left = (buf_len - 1) - content_width; - right = 0; - break; - default: - assert(0); - break; - } - if (left < 0 || right < 0) - return -1; - - - int written = 0; - written += snprint_n_chars(buf + written, buf_len - written, left, ' '); - if (written < 0) - return written; - - written += snprintf(buf + written, buf_len - written, "%*s", (int)content_width, buffer->str); - if (written < 0) - return written; - - written += snprint_n_chars(buf + written, buf_len - written, right, ' '); - return written; -} - - -/***************************************************************************** - * CELL - * ***************************************************************************/ -struct fort_cell; -typedef struct fort_cell fort_cell_t; -struct fort_cell -{ - string_buffer_t *str_buffer; - fort_table_options_t *options; -}; - -static fort_cell_t * create_cell() -{ - fort_cell_t *cell = F_CALLOC(sizeof(fort_cell_t), 1); - if (cell == NULL) - return NULL; - cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE); - if (cell->str_buffer == NULL) { - F_FREE(cell); - return NULL; - } - cell->options = NULL; -// init_cell_options(&(cell->options)); - return cell; -} - -static void destroy_cell(fort_cell_t *cell) -{ - if (cell == NULL) - return; - destroy_string_buffer(cell->str_buffer); - F_FREE(cell->options); - F_FREE(cell); -} - -static int hint_width_cell(const fort_cell_t *cell, const context_t *context) -{ - assert(cell); - assert(context); - int result = context->cell_padding_left + context->cell_padding_right; - if (cell->str_buffer && cell->str_buffer->str) { - result += buffer_text_width(cell->str_buffer); - } - return result; -} - -static int hint_height_cell(const fort_cell_t *cell, const context_t *context) -{ - assert(cell); - assert(context); - int result = context->cell_padding_top + context->cell_padding_bottom; - if (cell->str_buffer && cell->str_buffer->str) { - size_t text_height = buffer_text_height(cell->str_buffer); - result += text_height == 0 ? context->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; -//} - -static int cell_printf(fort_cell_t *cell, size_t row, size_t column, char *buf, size_t buf_len, const context_t *context) -{ - if (cell == NULL || buf_len == 0 || row >= hint_height_cell(cell, context) - || (buf_len <= hint_width_cell(cell, context))) { - return -1; - } - - if (row < context->cell_padding_top - || row >= (context->cell_padding_top + buffer_text_height(cell->str_buffer))) { - int k = snprint_n_chars(buf, buf_len, buf_len - 1, ' '); - return k; - } else { - int written = 0; - int left = context->cell_padding_left; - int right = context->cell_padding_right; - - written += snprint_n_chars(buf + written, buf_len - written, left, ' '); - - if (cell->str_buffer) - written += buffer_printf(cell->str_buffer, row - context->cell_padding_top, column, buf + written, buf_len - written - right, context); - else - written += snprint_n_chars(buf + written, buf_len - written, buf_len - written - right, ' '); - written += snprint_n_chars(buf + written, buf_len - written, right, ' '); - - return written; - } - -} - -/***************************************************************************** - * ROW - * ***************************************************************************/ -struct fort_row; -typedef struct fort_row fort_row_t; -struct fort_row -{ - vector_t *cells; - enum F_BOOL is_header; -}; - - -static fort_row_t * create_row() -{ - fort_row_t * row = 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; - return row; -} - -static void destroy_row(fort_row_t *row) -{ - if (row == NULL) - return; - - if (row->cells) { - size_t cells_n = vector_size(row->cells); - for (size_t 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); -} - -static fort_row_t * create_row_from_string(const char *str); -static fort_row_t* create_row_from_fmt_string(const char* FORT_RESTRICT fmt, va_list *va_args); - -static int columns_in_row(const fort_row_t *row) -{ - if (row == NULL || row->cells == NULL) - return 0; - - return vector_size(row->cells); -} - - -static 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 (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; -} - -static fort_cell_t *get_cell(fort_row_t *row, size_t col) -{ - return get_cell_implementation(row, col, DoNotCreate); -} - -static const fort_cell_t *get_cell_c(const fort_row_t *row, size_t col) -{ - return get_cell((fort_row_t *)row, col); -} - -static fort_cell_t *get_cell_and_create_if_not_exists(fort_row_t *row, size_t col) -{ - return get_cell_implementation(row, col, Create); -} - - -static 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 context_t *context) -{ -#define CHECK_RESULT_AND_MOVE_DEV(statement) \ - k = statement; \ - if (k < 0) {\ - goto clear; \ - } \ - dev += k; - - assert(buffer); - assert(context); - - int dev = 0; - int k = 0; - - const fort_row_t *main_row = NULL; - if (upper_row != NULL && lower_row != NULL) { - main_row = lower_row->is_header == F_TRUE ? lower_row : upper_row; - } else if (upper_row != NULL && lower_row == NULL) { - main_row = upper_row; - } else if (upper_row == NULL && lower_row != NULL) { - main_row = lower_row; - } else if (upper_row == NULL && lower_row == NULL) { - main_row = NULL; - } - - /* Row separator anatomy - * - * L I I I IV I I I R - */ - const char *L = NULL; - const char *I = NULL; - const char *IV = NULL; - const char *R = NULL; - - const char (*border_chars)[BorderItemPosSize] = NULL; - if (main_row && main_row->is_header == F_TRUE) { - border_chars = &context->header_border_chars; - } else { - border_chars = &context->border_chars; - } - - switch (separatorPos) { - case TopSeparator: - L = &(*border_chars)[TL_bip]; - I = &(*border_chars)[TT_bip]; - IV = &(*border_chars)[TV_bip]; - R = &(*border_chars)[TR_bip]; - break; - case InsideSeparator: - L = &(*border_chars)[LH_bip]; - I = &(*border_chars)[IH_bip]; - IV = &(*border_chars)[II_bip]; - R = &(*border_chars)[RH_bip]; - break; - case BottomSeparator: - L = &(*border_chars)[BL_bip]; - I = &(*border_chars)[BB_bip]; - IV = &(*border_chars)[BV_bip]; - R = &(*border_chars)[BR_bip]; - break; - default: - break; - } - - /* If all chars are not printable, skip line separator */ - if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) - return 0; - - for (size_t i = 0; i < cols; ++i) { - if (i == 0) { - CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, *L)); - } else { - CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, *IV)); - } - CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, col_width_arr[i], *I)); - } - CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, *R)); - - CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, '\n')); - - return dev; - -clear: - return -1; - -#undef CHECK_RESULT_AND_MOVE_DEV -} /***************************************************************************** * TABLE *****************************************************************************/ -struct fort_table; -typedef struct fort_table fort_table_t; -struct fort_table -{ - vector_t *rows; - fort_table_options_t *options; - string_buffer_t *conv_buffer; - size_t cur_row; - size_t cur_col; -}; - -static fort_status_t get_table_sizes(const FTABLE *table, size_t *rows, size_t *cols); -static fort_row_t *get_row_implementation(fort_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 (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; -} - -static fort_row_t *get_row(fort_table_t *table, size_t row) -{ - return get_row_implementation(table, row, DoNotCreate); -} - -static const fort_row_t *get_row_c(const fort_table_t *table, size_t row) -{ - return get_row((fort_table_t *)table, row); -} - -static fort_row_t *get_row_and_create_if_not_exists(fort_table_t *table, size_t row) -{ - return get_row_implementation(table, row, Create); -} - - - -static string_buffer_t * get_cur_str_buffer_and_create_if_not_exists(FTABLE *FORT_RESTRICT 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; - - if (cell->str_buffer == NULL) { - cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE); - if (cell->str_buffer == NULL) - return NULL; - } - - return cell->str_buffer; -} - -/***************************************************************************** - * LIBFORT helpers - *****************************************************************************/ -static char *fort_strdup(const char* str) -{ - if (str == NULL) - return NULL; - - size_t sz = strlen(str); - char *str_copy = (char*)F_MALLOC(sz + 1); - if (str_copy == NULL) - return NULL; - - strcpy(str_copy, str); - return str_copy; -} - -static 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; -} /***************************************************************************** * LIBFORT @@ -953,495 +248,11 @@ int ft_set_column_alignment(FTABLE * FORT_RESTRICT table, size_t column, enum Te -/***************************************************************************** - * STRING BUFFER - * ***************************************************************************/ -string_buffer_t* create_string_buffer(size_t sz) -{ - string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t)); - if (result == NULL) - return NULL; - result->str = F_MALLOC(sz); - if (result->str == NULL) { - F_FREE(result); - return NULL; - } - result->str_sz = sz; - - return result; -} - -void destroy_string_buffer(string_buffer_t *buffer) -{ - if (buffer == NULL) - return; - F_FREE(buffer->str); - buffer->str = 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->str_sz * 2); - if (new_str == NULL) { - return F_MEMORY_ERROR; - } - F_FREE(buffer->str); - buffer->str = new_str; - buffer->str_sz *= 2; - return F_SUCCESS; -} - -static 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 F_MEMORY_ERROR; - - while (sz >= buffer->str_sz) { - int status = realloc_string_buffer_without_copy(buffer); - if (!IS_SUCCESS(status)) { - return status; - } - } - F_FREE(buffer->str); - buffer->str = copy; - - return F_SUCCESS; -} - -/***************************************************************************** - * 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 = realloc(vector->m_data, new_size); - if (vector->m_data == NULL) - return -1; - return 0; -} - -/* ------------ Constructors & Destructors ----------------------------- */ - -static vector_t* create_vector(size_t item_size, size_t capacity) -{ - vector_t *vector = 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 = malloc(init_size); - if (vector->m_data == NULL) { - SYS_LOG_ERROR("Failed to allocate memory for asock vector inern. buffer"); - free(vector); - return NULL; - } - - vector->m_size = 0; - vector->m_capacity = capacity; - vector->m_item_size = item_size; - - return vector; -} - - -static void destroy_vector(vector_t* vector) -{ - assert(vector); - free(vector->m_data); - free(vector); -} - - -/* ----------- Nonmodifying functions --------------------------------- */ - -static size_t vector_size(const vector_t* vector) -{ - assert(vector); - return vector->m_size; -} - - -static size_t vector_capacity(const vector_t* vector) -{ - assert(vector); - return vector->m_capacity; -} - -static size_t vector_index_of(const vector_t* vector, const void *item) -{ - assert(vector); - assert(item); - - for (size_t i = 0; i < vector->m_size; ++i) { - void *data_pos = 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 ------------------------------------- */ - -static 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 F_ERROR; - vector->m_capacity = vector->m_capacity * 2; - } - - ptrdiff_t deviation = vector->m_size * vector->m_item_size; - memcpy(vector->m_data + deviation, item, vector->m_item_size); - - ++(vector->m_size); - - return F_SUCCESS; -} - - -static int vector_erase(vector_t *vector, size_t index) -{ - assert(vector); - - if (vector->m_size == 0 || index >= vector->m_size) - return F_ERROR; - - memmove(vector->m_data + vector->m_item_size * index, - vector->m_data + vector->m_item_size * (index + 1), - (vector->m_size - 1 - index) * vector->m_item_size); - vector->m_size--; - return F_SUCCESS; -} - - -static void vector_clear(vector_t *vector) -{ - vector->m_size = 0; -} - - - -static void *vector_at(vector_t *vector, size_t index) -{ - if (index >= vector->m_size) - return NULL; - - return vector->m_data + index * vector->m_item_size; -} - - -#define FOR_EACH_(type, item, vector, index_name) \ - for (size_t 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)) - -/***************************************************************************** - * ROW - * ***************************************************************************/ - -static fort_row_t* create_row_from_string(const char *str) -{ - fort_row_t * row = create_row(); - if (row == NULL) - return NULL; - - if (str == NULL) - return row; - - char *str_copy = F_STRDUP(str); - if (str_copy == NULL) - goto clear; - - char *pos = str_copy; - char *base_pos = str_copy; - int number_of_separators = 0; - while (*pos) { - pos = strchr(pos, FORT_COL_SEPARATOR); - if (pos != NULL) { - *(pos) = '\0'; - ++pos; - number_of_separators++; - } - - fort_cell_t *cell = create_cell(); - if (cell == NULL) - goto clear; - - int status = fill_buffer_from_string(cell->str_buffer, base_pos); - if (IS_ERROR(status)) { - destroy_cell(cell); - goto clear; - } - - status = vector_push(row->cells, &cell); - if (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_buffer_from_string(cell->str_buffer, ""); - if (IS_ERROR(status)) { - destroy_cell(cell); - goto clear; - } - - status = vector_push(row->cells, &cell); - if (IS_ERROR(status)) { - destroy_cell(cell); - goto clear; - } - } - - F_FREE(str_copy); - return row; - -clear: - destroy_row(row); - F_FREE(str_copy); - return NULL; -} - - -static fort_row_t* create_row_from_fmt_string(const char* FORT_RESTRICT fmt, va_list *va_args) -{ - string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE); - if (buffer == NULL) - return NULL; - - int cols_origin = number_of_columns_in_format_string(fmt); - - while (1) { - va_list va; - va_copy(va, *va_args); - int virtual_sz = vsnprintf(buffer->str, buffer->str_sz, fmt, va); - va_end(va); - /* If error encountered */ - if (virtual_sz == -1) - goto clear; - - /* Successful write */ - if (virtual_sz < buffer->str_sz) - break; - - /* Otherwise buffer was too small, so incr. buffer size ant try again. */ - if (!IS_SUCCESS(realloc_string_buffer_without_copy(buffer))) - goto clear; - } - - int cols = number_of_columns_in_format_string(buffer->str); - if (cols == cols_origin) { - - fort_row_t *row = create_row_from_string(buffer->str); - 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; -} - -static 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) -{ - assert(context); - if (row == NULL) - return -1; - - int 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 - */ - - const char (*bord_chars)[BorderItemPosSize] = (row->is_header) - ? (&context->header_border_chars) - : (&context->border_chars); - const char *L = &(*bord_chars)[LL_bip]; - const char *IV = &(*bord_chars)[IV_bip]; - const char *R = &(*bord_chars)[RR_bip]; - - - int dev = 0; - for (size_t i = 0; i < row_height; ++i) { - dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, *L); - for (size_t j = 0; j < col_width_arr_sz; ++j) { - if (j < cols_in_row) { - fort_cell_t *cell = *(fort_cell_t**)vector_at(row->cells, j); - dev += cell_printf(cell, i, j, buffer + dev, col_width_arr[j] + 1, context); - } else { - dev += snprint_n_chars(buffer + dev, buf_sz - dev, col_width_arr[j], ' '); - } - if (j == col_width_arr_sz - 1) { - dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, *R); - } else { - dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, *IV); - } - } - dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, '\n'); - } - return dev; -} /***************************************************************************** * TABLE * ***************************************************************************/ -/* - * Returns number of cells (rows * cols) - */ -static fort_status_t get_table_sizes(const FTABLE *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 F_SUCCESS; -} - -static fort_status_t table_rows_and_cols_geometry(const FTABLE *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 F_ERROR; - } - - - - size_t cols = 0; - size_t rows = 0; - int status = get_table_sizes(table, &rows, &cols); - if (IS_ERROR(status)) - return status; - - size_t *col_width_arr = F_CALLOC(sizeof(size_t), cols); - size_t *row_height_arr = 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 F_ERROR; - } - - context_t *context = (table->options ? table->options : &g_table_options); - for (size_t col = 0; col < cols; ++col) { - col_width_arr[col] = 0; - for (size_t 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); - if (cell) { - col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, context)); - row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, context)); - } - } - } - - /* 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 F_SUCCESS; -} - -/* - * Returns geometry in characters - */ -static fort_status_t table_geometry(const FTABLE *table, size_t *height, size_t *width) -{ - if (table == NULL) - return F_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 (IS_ERROR(status)) - return status; - - *width = 1 + (cols == 0 ? 1 : cols) + 1; // for boundaries (that take 1 symbol) + newline - for (size_t 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 (size_t i = 0; i < rows; ++i) { - *height += row_height_arr[i]; - } - F_FREE(col_width_arr); - F_FREE(row_height_arr); - return F_SUCCESS; - -} - const char* ft_to_string(const FTABLE *FORT_RESTRICT table) diff --git a/src/fort_impl.c b/src/fort_impl.c new file mode 100644 index 0000000..e075356 --- /dev/null +++ b/src/fort_impl.c @@ -0,0 +1,51 @@ +#include "fort_impl.h" + + + +/***************************************************************************** + * LIBFORT helpers + *****************************************************************************/ +char *fort_strdup(const char* str) +{ + if (str == NULL) + return NULL; + + size_t sz = strlen(str); + char *str_copy = (char*)F_MALLOC(sz + 1); + if (str_copy == NULL) + return NULL; + + strcpy(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; +} + +int snprint_n_chars(char *buf, size_t length, size_t n, char ch) +{ + if (length <= n) + return -1; + + int status = snprintf(buf, length, "%0*d", (int)n, 0); + if (status < 0) + return status; + + for (size_t i = 0; i < n; ++i) { + *buf = ch; + buf++; + } + return n; +} diff --git a/src/fort_impl.h b/src/fort_impl.h new file mode 100644 index 0000000..8cfcb8d --- /dev/null +++ b/src/fort_impl.h @@ -0,0 +1,104 @@ +#ifndef FORT_IMPL_H +#define FORT_IMPL_H + +#include +#include +#include +#include +#include + +#define FORT_COL_SEPARATOR '|' + +#define FORT_UNUSED __attribute__((unused)) + +#define F_CALLOC calloc +#define F_MALLOC malloc +#define F_REALLOC realloc +#define F_FREE free +#define F_STRDUP fort_strdup + +#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 STR_2_CAT_(arg1, arg2) \ + arg1##arg2 +#define STR_2_CAT(arg1, arg2) \ + STR_2_CAT_(arg1, arg2) + +#define UNIQUE_NAME_(prefix) \ + STR_2_CAT(prefix,__COUNTER__) +#define UNIQUE_NAME(prefix) \ + UNIQUE_NAME_(prefix) + + + +/***************************************************************************** + * LOGGER + *****************************************************************************/ +#define SYS_LOG_ERROR(...) + + +/***************************************************************************** + * RETURN CODES + * ***************************************************************************/ +typedef int fort_status_t; +#define F_SUCCESS 0 +#define F_MEMORY_ERROR 1 +#define F_ERROR 2 +#define IS_SUCCESS(arg) ((arg) == F_SUCCESS) +#define IS_ERROR(arg) (!IS_SUCCESS(arg)) + +/***************************************************************************** + * 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; + + +typedef struct fort_table_options fort_table_options_t; +typedef fort_table_options_t 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 fort_table FTABLE; + + + + + +/***************************************************************************** + * LIBFORT helpers + *****************************************************************************/ +char *fort_strdup(const char* str); +size_t number_of_columns_in_format_string(const char *fmt); +int snprint_n_chars(char *buf, size_t length, size_t n, char ch); + +#endif // FORT_IMPL_H diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..4cf7e7e --- /dev/null +++ b/src/options.c @@ -0,0 +1,132 @@ +#include "options.h" +#include "fort_impl.h" +#include "assert.h" +#include "vector.h" + +/***************************************************************************** + * COLUMN OPTIONS + * ***************************************************************************/ + +fort_column_options_t g_column_options = +{ + 0, /* col_min_width*/ + RightAligned, /* align */ +}; + +fort_column_options_t create_column_options() +{ + fort_column_options_t result; + memset(&result, '\0', sizeof(result)); + memcpy(&result, &g_column_options, sizeof(result)); + return result; +} + +/***************************************************************************** + * OPTIONS + * ***************************************************************************/ + + + +fort_table_options_t g_table_options = { + 1, /* cell_padding_top */ + 1, /* cell_padding_bottom */ + 1, /* cell_padding_left */ + 1, /* cell_padding_right */ + 1, /* cell_empty_string_height */ + + /* border_chars */ + { + '+', '-', '+', '+', + '|', '|', '|', + '+', '-', '+', '+', + '+', '-', '+', '+' + }, + + /* header_border_chars */ + { + '+', '-', '+', '+', + '|', '|', '|', + '+', '-', '+', '+', + '+', '-', '+', '+' + }, + + NULL, /* col_options */ +}; + + +fort_table_options_t* create_table_options() +{ + fort_table_options_t* options = F_CALLOC(sizeof(fort_table_options_t), 1); + if (options == NULL) { + return NULL; + } + memcpy(options, &g_table_options, sizeof(fort_table_options_t)); + return options; +} + +void destroy_table_options(fort_table_options_t* options) +{ + if (options == NULL) + return; + + if (options->col_options != NULL) { + destroy_vector(options->col_options); + } + F_FREE(options); +} + + +#define FORT_OPTIONS_SET_COLUMN_OPTION(options, column, opt_name, opt_value) \ + assert(options);\ +\ + if (options->col_options == NULL) {\ + options->col_options = create_vector(sizeof(fort_column_options_t), DEFAULT_VECTOR_CAPACITY);\ + if (options->col_options == NULL) \ + return F_MEMORY_ERROR; \ + } \ +\ + while (vector_size(options->col_options) <= column) {\ + fort_column_options_t def_option = create_column_options();\ + vector_push(options->col_options, &def_option);\ + }\ +\ + fort_column_options_t *col_option = (fort_column_options_t*)vector_at(options->col_options, column);\ + col_option->opt_name = opt_value;\ +\ + return F_SUCCESS; + +fort_status_t fort_options_set_column_min_width(fort_table_options_t *options, size_t column, size_t width) +{ + FORT_OPTIONS_SET_COLUMN_OPTION(options, column, col_min_width, width); +} + +fort_status_t fort_options_set_column_alignment(fort_table_options_t *options, size_t column, enum TextAlignment al) +{ + FORT_OPTIONS_SET_COLUMN_OPTION(options, column, align, al); +} + +int fort_options_column_width(const fort_table_options_t *options, size_t column) +{ + assert(options); + if (options->col_options == NULL) + return -1; + + if (vector_size(options->col_options) <= column) + return -1; + + return ((fort_column_options_t*)vector_at(options->col_options, column))->col_min_width; +} + +int fort_options_column_alignment(const fort_table_options_t *options, size_t column) +{ + assert(options); + + enum TextAlignment align = g_column_options.align; + if (options->col_options == NULL) + return align; + + if (vector_size(options->col_options) <= column) + return align; + + return ((fort_column_options_t*)vector_at(options->col_options, column))->align; +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..8b8cf61 --- /dev/null +++ b/src/options.h @@ -0,0 +1,105 @@ +#ifndef OPTIONS_H +#define OPTIONS_H + +#include "fort_impl.h" + +enum TextAlignment +{ + LeftAligned, + CenterAligned, + RightAligned +}; + + +struct fort_column_options +{ + int col_min_width; + enum TextAlignment 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(); + + + +struct vector; +typedef struct vector vector_t; + + + + + + +/***************************************************************************** + * 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 +}; + + + +struct fort_table_options +{ + int cell_padding_top; + int cell_padding_bottom; + int cell_padding_left; + int cell_padding_right; + int cell_empty_string_height; + + char border_chars[BorderItemPosSize]; + char header_border_chars[BorderItemPosSize]; + vector_t *col_options; + +}; +typedef struct fort_table_options fort_table_options_t; +typedef fort_table_options_t context_t; +extern fort_table_options_t g_table_options; + + + +fort_table_options_t* create_table_options(); +void destroy_table_options(fort_table_options_t* options); +fort_status_t fort_options_set_column_min_width(fort_table_options_t *options, size_t column, size_t width); +fort_status_t fort_options_set_column_alignment(fort_table_options_t *options, size_t column, enum TextAlignment al); +int fort_options_column_width(const fort_table_options_t *options, size_t column); +int fort_options_column_alignment(const fort_table_options_t *options, size_t column); + +#endif // OPTIONS_H diff --git a/src/row.c b/src/row.c new file mode 100644 index 0000000..1069481 --- /dev/null +++ b/src/row.c @@ -0,0 +1,363 @@ +#include "row.h" +#include "cell.h" +#include "string_buffer.h" +#include "assert.h" +#include "vector.h" +#include "ctype.h" + +fort_row_t * create_row() +{ + fort_row_t * row = 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; + return row; +} + +void destroy_row(fort_row_t *row) +{ + if (row == NULL) + return; + + if (row->cells) { + size_t cells_n = vector_size(row->cells); + for (size_t 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); +} + + + + + +int 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 (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); +} + + + + + +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 context_t *context) +{ +#define CHECK_RESULT_AND_MOVE_DEV(statement) \ + k = statement; \ + if (k < 0) {\ + goto clear; \ + } \ + dev += k; + + assert(buffer); + assert(context); + + int dev = 0; + int k = 0; + + const fort_row_t *main_row = NULL; + if (upper_row != NULL && lower_row != NULL) { + main_row = lower_row->is_header == F_TRUE ? lower_row : upper_row; + } else if (upper_row != NULL && lower_row == NULL) { + main_row = upper_row; + } else if (upper_row == NULL && lower_row != NULL) { + main_row = lower_row; + } else if (upper_row == NULL && lower_row == NULL) { + main_row = NULL; + } + + /* Row separator anatomy + * + * L I I I IV I I I R + */ + const char *L = NULL; + const char *I = NULL; + const char *IV = NULL; + const char *R = NULL; + + const char (*border_chars)[BorderItemPosSize] = NULL; + if (main_row && main_row->is_header == F_TRUE) { + border_chars = &context->header_border_chars; + } else { + border_chars = &context->border_chars; + } + + switch (separatorPos) { + case TopSeparator: + L = &(*border_chars)[TL_bip]; + I = &(*border_chars)[TT_bip]; + IV = &(*border_chars)[TV_bip]; + R = &(*border_chars)[TR_bip]; + break; + case InsideSeparator: + L = &(*border_chars)[LH_bip]; + I = &(*border_chars)[IH_bip]; + IV = &(*border_chars)[II_bip]; + R = &(*border_chars)[RH_bip]; + break; + case BottomSeparator: + L = &(*border_chars)[BL_bip]; + I = &(*border_chars)[BB_bip]; + IV = &(*border_chars)[BV_bip]; + R = &(*border_chars)[BR_bip]; + break; + default: + break; + } + + /* If all chars are not printable, skip line separator */ + if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) + return 0; + + for (size_t i = 0; i < cols; ++i) { + if (i == 0) { + CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, *L)); + } else { + CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, *IV)); + } + CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, col_width_arr[i], *I)); + } + CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, *R)); + + CHECK_RESULT_AND_MOVE_DEV(snprint_n_chars(buffer + dev, buffer_sz - dev, 1, '\n')); + + return dev; + +clear: + return -1; + +#undef CHECK_RESULT_AND_MOVE_DEV +} + + + + +fort_row_t* create_row_from_string(const char *str) +{ + fort_row_t * row = create_row(); + if (row == NULL) + return NULL; + + if (str == NULL) + return row; + + char *str_copy = F_STRDUP(str); + if (str_copy == NULL) + goto clear; + + char *pos = str_copy; + char *base_pos = str_copy; + int number_of_separators = 0; + while (*pos) { + pos = strchr(pos, FORT_COL_SEPARATOR); + if (pos != NULL) { + *(pos) = '\0'; + ++pos; + number_of_separators++; + } + + fort_cell_t *cell = create_cell(); + if (cell == NULL) + goto clear; + +// int status = fill_buffer_from_string(cell->str_buffer, base_pos); + int status = fill_cell_from_string(cell, base_pos); + if (IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + status = vector_push(row->cells, &cell); + if (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_buffer_from_string(cell->str_buffer, ""); + int status = fill_cell_from_string(cell, ""); + if (IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + + status = vector_push(row->cells, &cell); + if (IS_ERROR(status)) { + destroy_cell(cell); + goto clear; + } + } + + F_FREE(str_copy); + return row; + +clear: + destroy_row(row); + F_FREE(str_copy); + return NULL; +} + + + + +fort_row_t* create_row_from_fmt_string(const char* FORT_RESTRICT fmt, va_list *va_args) +{ + string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE); + if (buffer == NULL) + return NULL; + + int cols_origin = number_of_columns_in_format_string(fmt); + + while (1) { + va_list va; + va_copy(va, *va_args); + int virtual_sz = vsnprintf(buffer->str, buffer->str_sz, fmt, va); + va_end(va); + /* If error encountered */ + if (virtual_sz == -1) + goto clear; + + /* Successful write */ + if (virtual_sz < buffer->str_sz) + break; + + /* Otherwise buffer was too small, so incr. buffer size ant try again. */ + if (!IS_SUCCESS(realloc_string_buffer_without_copy(buffer))) + goto clear; + } + + int cols = number_of_columns_in_format_string(buffer->str); + if (cols == cols_origin) { + + fort_row_t *row = create_row_from_string(buffer->str); + 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; +} + + +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) +{ + assert(context); + if (row == NULL) + return -1; + + int 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 + */ + + const char (*bord_chars)[BorderItemPosSize] = (row->is_header) + ? (&context->header_border_chars) + : (&context->border_chars); + const char *L = &(*bord_chars)[LL_bip]; + const char *IV = &(*bord_chars)[IV_bip]; + const char *R = &(*bord_chars)[RR_bip]; + + + int dev = 0; + for (size_t i = 0; i < row_height; ++i) { + dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, *L); + for (size_t j = 0; j < col_width_arr_sz; ++j) { + if (j < cols_in_row) { + fort_cell_t *cell = *(fort_cell_t**)vector_at(row->cells, j); + dev += cell_printf(cell, i, j, buffer + dev, col_width_arr[j] + 1, context); + } else { + dev += snprint_n_chars(buffer + dev, buf_sz - dev, col_width_arr[j], ' '); + } + if (j == col_width_arr_sz - 1) { + dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, *R); + } else { + dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, *IV); + } + } + dev += snprint_n_chars(buffer + dev, buf_sz - dev, 1, '\n'); + } + return dev; +} diff --git a/src/row.h b/src/row.h new file mode 100644 index 0000000..682d749 --- /dev/null +++ b/src/row.h @@ -0,0 +1,48 @@ +#ifndef ROW_H +#define ROW_H + +#include "fort_impl.h" +#include "fort.h" +#include "stdarg.h" + +struct fort_row; +typedef struct fort_row fort_row_t; +struct fort_row +{ + vector_t *cells; + enum F_BOOL is_header; +}; + + + +fort_row_t * create_row(); +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* FORT_RESTRICT fmt, va_list *va_args); + + +int 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); + + + +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 context_t *context); + + + + +fort_row_t* create_row_from_string(const char *str); +fort_row_t* create_row_from_fmt_string(const char* FORT_RESTRICT fmt, va_list *va_args); + +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); + + +#endif // ROW_H diff --git a/src/string_buffer.c b/src/string_buffer.c new file mode 100644 index 0000000..3a9d5ba --- /dev/null +++ b/src/string_buffer.c @@ -0,0 +1,132 @@ +#include "string_buffer.h" +#include "options.h" +#include "assert.h" + +/***************************************************************************** + * STRING BUFFER + * ***************************************************************************/ +string_buffer_t* create_string_buffer(size_t sz) +{ + string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t)); + if (result == NULL) + return NULL; + result->str = F_MALLOC(sz); + if (result->str == NULL) { + F_FREE(result); + return NULL; + } + result->str_sz = sz; + + return result; +} + +void destroy_string_buffer(string_buffer_t *buffer) +{ + if (buffer == NULL) + return; + F_FREE(buffer->str); + buffer->str = 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->str_sz * 2); + if (new_str == NULL) { + return F_MEMORY_ERROR; + } + F_FREE(buffer->str); + buffer->str = new_str; + buffer->str_sz *= 2; + return F_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 F_MEMORY_ERROR; + + while (sz >= buffer->str_sz) { + int status = realloc_string_buffer_without_copy(buffer); + if (!IS_SUCCESS(status)) { + return status; + } + } + F_FREE(buffer->str); + buffer->str = copy; + + return F_SUCCESS; +} + + + +size_t buffer_text_height(string_buffer_t *buffer) +{ + if (buffer == NULL || buffer->str == NULL || strlen(buffer->str) == 0) { + return 0; + } + return 1; +} + +size_t buffer_text_width(string_buffer_t *buffer) +{ + if (buffer == NULL || buffer->str == NULL) { + return 0; + } + return strlen(buffer->str); +} + + +int buffer_printf(string_buffer_t *buffer, size_t buffer_row, size_t table_column, char *buf, size_t buf_len, const context_t *context) +{ + if (buffer == NULL || buffer->str == 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; + + int left = 0; + int right = 0; + + switch (fort_options_column_alignment(context, table_column)) { + case LeftAligned: + left = 0; + right = (buf_len - 1) - content_width; + break; + case CenterAligned: + left = ((buf_len - 1) - content_width) / 2; + right = ((buf_len - 1) - content_width) - left; + break; + case RightAligned: + left = (buf_len - 1) - content_width; + right = 0; + break; + default: + assert(0); + break; + } + if (left < 0 || right < 0) + return -1; + + + int written = 0; + written += snprint_n_chars(buf + written, buf_len - written, left, ' '); + if (written < 0) + return written; + + written += snprintf(buf + written, buf_len - written, "%*s", (int)content_width, buffer->str); + if (written < 0) + return written; + + written += snprint_n_chars(buf + written, buf_len - written, right, ' '); + return written; +} diff --git a/src/string_buffer.h b/src/string_buffer.h new file mode 100644 index 0000000..680674c --- /dev/null +++ b/src/string_buffer.h @@ -0,0 +1,30 @@ +#ifndef STRING_BUFFER_H +#define STRING_BUFFER_H + +#include "fort_impl.h" + + +/***************************************************************************** + * STRING BUFFER + * ***************************************************************************/ +struct string_buffer; +typedef struct string_buffer string_buffer_t; +struct string_buffer +{ + char *str; + size_t str_sz; +}; + +string_buffer_t* create_string_buffer(size_t sz); +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); + +size_t buffer_text_height(string_buffer_t *buffer); + + +size_t buffer_text_width(string_buffer_t *buffer); +int buffer_printf(string_buffer_t *buffer, size_t buffer_row, size_t table_column, char *buf, size_t buf_len, const context_t *context); + + +#endif // STRING_BUFFER_H diff --git a/src/table.c b/src/table.c new file mode 100644 index 0000000..d0b353c --- /dev/null +++ b/src/table.c @@ -0,0 +1,186 @@ +#include "table.h" +#include "string_buffer.h" +#include "cell.h" +#include "vector.h" +#include "row.h" +fort_status_t get_table_sizes(const FTABLE *table, size_t *rows, size_t *cols); + + + +fort_row_t *get_row_implementation(fort_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 (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(fort_table_t *table, size_t row) +{ + return get_row_implementation(table, row, DoNotCreate); +} + +const fort_row_t *get_row_c(const fort_table_t *table, size_t row) +{ + return get_row((fort_table_t *)table, row); +} + +fort_row_t *get_row_and_create_if_not_exists(fort_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(FTABLE *FORT_RESTRICT 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; + +// if (cell->str_buffer == NULL) { +// cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE); +// if (cell->str_buffer == NULL) +// return NULL; +// } +// return cell->str_buffer; + + return cell_get_string_buffer(cell); +} + + +/* + * Returns number of cells (rows * cols) + */ +fort_status_t get_table_sizes(const FTABLE *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 F_SUCCESS; +} + +fort_status_t table_rows_and_cols_geometry(const FTABLE *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 F_ERROR; + } + + + + size_t cols = 0; + size_t rows = 0; + int status = get_table_sizes(table, &rows, &cols); + if (IS_ERROR(status)) + return status; + + size_t *col_width_arr = F_CALLOC(sizeof(size_t), cols); + size_t *row_height_arr = 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 F_ERROR; + } + + context_t *context = (table->options ? table->options : &g_table_options); + for (size_t col = 0; col < cols; ++col) { + col_width_arr[col] = 0; + for (size_t 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); + if (cell) { + col_width_arr[col] = MAX(col_width_arr[col], hint_width_cell(cell, context)); + row_height_arr[row] = MAX(row_height_arr[row], hint_height_cell(cell, context)); + } + } + } + + /* 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 F_SUCCESS; +} + +/* + * Returns geometry in characters + */ +fort_status_t table_geometry(const FTABLE *table, size_t *height, size_t *width) +{ + if (table == NULL) + return F_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 (IS_ERROR(status)) + return status; + + *width = 1 + (cols == 0 ? 1 : cols) + 1; // for boundaries (that take 1 symbol) + newline + for (size_t 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 (size_t i = 0; i < rows; ++i) { + *height += row_height_arr[i]; + } + F_FREE(col_width_arr); + F_FREE(row_height_arr); + return F_SUCCESS; + +} + diff --git a/src/table.h b/src/table.h new file mode 100644 index 0000000..1af922a --- /dev/null +++ b/src/table.h @@ -0,0 +1,33 @@ +#ifndef TABLE_H +#define TABLE_H + +#include "fort_impl.h" +#include "fort.h" +struct fort_table; +typedef struct fort_table fort_table_t; +struct fort_table +{ + vector_t *rows; + fort_table_options_t *options; + string_buffer_t *conv_buffer; + size_t cur_row; + size_t cur_col; +}; + + +fort_status_t get_table_sizes(const FTABLE *table, size_t *rows, size_t *cols); +fort_row_t *get_row_implementation(fort_table_t *table, size_t row, enum PolicyOnNull policy); +fort_row_t *get_row(fort_table_t *table, size_t row); +const fort_row_t *get_row_c(const fort_table_t *table, size_t row); +fort_row_t *get_row_and_create_if_not_exists(fort_table_t *table, size_t row); + +string_buffer_t * get_cur_str_buffer_and_create_if_not_exists(FTABLE *FORT_RESTRICT table); + + + +fort_status_t table_rows_and_cols_geometry(const FTABLE *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 FTABLE *table, size_t *height, size_t *width); + +#endif // TABLE_H diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..2a86c1b --- /dev/null +++ b/src/vector.c @@ -0,0 +1,144 @@ +#include "vector.h" +#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 = 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 = 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 = malloc(init_size); + if (vector->m_data == NULL) { + SYS_LOG_ERROR("Failed to allocate memory for asock vector inern. buffer"); + 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); + free(vector->m_data); + free(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); + + for (size_t i = 0; i < vector->m_size; ++i) { + void *data_pos = 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 F_ERROR; + vector->m_capacity = vector->m_capacity * 2; + } + + ptrdiff_t deviation = vector->m_size * vector->m_item_size; + memcpy(vector->m_data + deviation, item, vector->m_item_size); + + ++(vector->m_size); + + return F_SUCCESS; +} + + +int vector_erase(vector_t *vector, size_t index) +{ + assert(vector); + + if (vector->m_size == 0 || index >= vector->m_size) + return F_ERROR; + + memmove(vector->m_data + vector->m_item_size * index, + vector->m_data + vector->m_item_size * (index + 1), + (vector->m_size - 1 - index) * vector->m_item_size); + vector->m_size--; + return F_SUCCESS; +} + + +void vector_clear(vector_t *vector) +{ + vector->m_size = 0; +} + + + +void *vector_at(vector_t *vector, size_t index) +{ + if (index >= vector->m_size) + return NULL; + + return vector->m_data + index * vector->m_item_size; +} + + diff --git a/src/vector.h b/src/vector.h new file mode 100644 index 0000000..69fe391 --- /dev/null +++ b/src/vector.h @@ -0,0 +1,35 @@ +#ifndef VECTOR_H +#define VECTOR_H + +#include "fort_impl.h" + + +/***************************************************************************** + * 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 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 void* vector_at(vector_t*, size_t index); + + +#define FOR_EACH_(type, item, vector, index_name) \ + for (size_t 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 diff --git a/tests/test_table.c b/tests/test_table.c index 572b59b..1ee7c03 100644 --- a/tests/test_table.c +++ b/tests/test_table.c @@ -1,10 +1,11 @@ -#define FORT_EXTERN static +//#define FORT_EXTERN static #include "tests.h" #include "fort.h" #include #include #include -#include "fort.c" +//#include "fort.c" +#include "table.h" void test_table_sizes(void **state) { diff --git a/tests/test_vector.c b/tests/test_vector.c index b4936f1..cd2d611 100644 --- a/tests/test_vector.c +++ b/tests/test_vector.c @@ -1,5 +1,7 @@ #include "tests.h" -#include "../src/fort.c" + +#include "vector.h" +//#include "../src/fort.c" void test_vector_basic(void **state)