diff --git a/CMakeLists.txt b/CMakeLists.txt index 005077d..155ac86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,8 @@ add_executable(TEST ${FORT_SOURCES} tests/test.c tests/test_vector.c - tests/test_table.c) + tests/test_table.c + tests/test_string_buffer.c) target_link_libraries(TEST cmocka) diff --git a/example/main.c b/example/main.c index f6151b4..cb9d18c 100644 --- a/example/main.c +++ b/example/main.c @@ -6,6 +6,8 @@ int main() { FTABLE *table = ft_create_table(); + ft_set_column_alignment(table, 2, LeftAligned); + ft_hdr_printf_ln(table, "%d , %c|| %s|%f", 3, 'c', "234", 3.14); const char *row[] = {"AAA", " ADf qwer", "qwerwqer", "11111 23333", "qwe"}; @@ -34,16 +36,10 @@ int main() ft_nwrite(table, 3, "123", "2345", "4567"); ft_ln(table); - FT_NWRITE(table, "123", "345", "Anton", "Petr", "Pavel"); + FT_NWRITE_LN(table, "123", "132445\n123\n123", "Anton", "Petr", "Pavel"); fprintf(stderr, "Table:\n"); fprintf(stderr, "%s\n", ft_to_string(table)); -// printf("Hello, world!\n"); -// char buffer[3] = {'a', 'b', 'c'}; -// int ret = snprintf(buffer, 3, "so"); -// printf("result is %s = lent = %d\n", buffer, ret); -// fprintf(stderr, "asd %d|", 3); -// fprintf(stderr, "%d\n", 3.14); ft_destroy_table(table); } diff --git a/include/fort.h b/include/fort.h index 4edd84d..84460a8 100644 --- a/include/fort.h +++ b/include/fort.h @@ -183,6 +183,9 @@ static inline void fort_check_if_string_helper(const char*str) #define FT_NWRITE(table, ...)\ (CHECK_IF_ARGS_ARE_STRINGS(__VA_ARGS__),ft_nwrite(table, PP_NARG(__VA_ARGS__), __VA_ARGS__)) +#define FT_NWRITE_LN(table, ...)\ + (CHECK_IF_ARGS_ARE_STRINGS(__VA_ARGS__),ft_nwrite_ln(table, PP_NARG(__VA_ARGS__), __VA_ARGS__)) + FORT_EXTERN int ft_nwrite(FTABLE *FORT_RESTRICT table, size_t n, const char* FORT_RESTRICT cell_content, ...); FORT_EXTERN int ft_nwrite_ln(FTABLE *FORT_RESTRICT table, size_t n, const char* FORT_RESTRICT cell_content, ...); diff --git a/src/cell.c b/src/cell.c index 3475bc1..31a20f4 100644 --- a/src/cell.c +++ b/src/cell.c @@ -83,12 +83,13 @@ int hint_height_cell(const fort_cell_t *cell, const context_t *context) 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) + if (cell == NULL || buf_len == 0 || (buf_len <= hint_width_cell(cell, context))) { return -1; } - if (row < context->cell_padding_top + if (row >= hint_height_cell(cell, context) + || 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; diff --git a/src/fort.c b/src/fort.c index d42379e..be47a0e 100644 --- a/src/fort.c +++ b/src/fort.c @@ -117,7 +117,8 @@ static int ft_row_printf_impl(FTABLE *FORT_RESTRICT table, size_t row, const cha } } } - /* todo clearing pushed items in case of error */ + /* todo: clearing pushed items in case of error */ + /* todo: this function always create new row, this is not correct, it should be more complicated */ cur_row_p = (fort_row_t**)vector_at(table->rows, row); diff --git a/src/string_buffer.c b/src/string_buffer.c index 9358046..dfdd33f 100644 --- a/src/string_buffer.c +++ b/src/string_buffer.c @@ -6,6 +6,61 @@ * STRING BUFFER * ***************************************************************************/ +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; +} + +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; +} + +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; +} + + string_buffer_t* create_string_buffer(size_t sz) { string_buffer_t *result = (string_buffer_t *)F_MALLOC(sizeof(string_buffer_t)); @@ -72,15 +127,23 @@ size_t buffer_text_height(string_buffer_t *buffer) if (buffer == NULL || buffer->str == NULL || strlen(buffer->str) == 0) { return 0; } - return 1; + return 1 + strchr_count(buffer->str, '\n'); } size_t buffer_text_width(string_buffer_t *buffer) { - if (buffer == NULL || buffer->str == NULL) { - return 0; + size_t max_length = 0; + int n = 0; + while (1) { + const char *beg = NULL; + const char *end = NULL; + str_n_substring(buffer->str, '\n', n, &beg, &end); + if (beg == NULL || end == NULL) + return max_length; + + max_length = MAX(max_length, (end - beg)); + ++n; } - return strlen(buffer->str); } @@ -124,9 +187,47 @@ int buffer_printf(string_buffer_t *buffer, size_t buffer_row, size_t table_colum if (written < 0) return written; - written += snprintf(buf + written, buf_len - written, "%*s", (int)content_width, buffer->str); + + +// const char *substr = str_n_substring(buffer->str, '\n', buffer_row); +// if (substr == NULL) +// return -1; +// const char *next_substr = str_n_substring(buffer->str, '\n', buffer_row + 1); +// size_t buf_row_len = 0; +// if (next_substr) { +// buf_row_len = next_substr - substr - 1; +// } else { +// buf_row_len = strlen(buffer->str) - (next_substr - buffer->str); +// } todo +// if (buf_row_len > content_width) +// return -1; + +// written += snprintf(buf + written, buf_len - written, "%*s", (int)buf_row_len, substr); +// if (written < 0) +// return written; +// written += snprint_n_chars(buf + written, buf_len - written, content_width - buf_row_len, ' '); +// if (written < 0) +// return written; + + const char *beg = NULL; + const char *end = NULL; + str_n_substring(buffer->str, '\n', buffer_row, &beg, &end); + if (beg == NULL || end == NULL) + return -1; + char old_value = *end; + *(char *)end = '\0'; + + written += snprintf(buf + written, buf_len - written, "%*s", (int)(end - beg), beg); + *(char *)end = old_value; if (written < 0) return written; + written += snprint_n_chars(buf + written, buf_len - written, (int)(content_width - (end - beg)), ' '); + 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/tests/test.c b/tests/test.c index 22f3fa6..2357cac 100644 --- a/tests/test.c +++ b/tests/test.c @@ -7,7 +7,9 @@ int main(void) { cmocka_unit_test(test_table_sizes), cmocka_unit_test(test_table_geometry), cmocka_unit_test(test_table_basic), - cmocka_unit_test(test_table_options) + cmocka_unit_test(test_table_options), + cmocka_unit_test(test_string_buffer), + }; return cmocka_run_group_tests(tests, NULL, NULL); } diff --git a/tests/test_string_buffer.c b/tests/test_string_buffer.c new file mode 100644 index 0000000..c7a0888 --- /dev/null +++ b/tests/test_string_buffer.c @@ -0,0 +1,169 @@ +#include "tests.h" + +#include "string_buffer.h" +//#include "../src/fort.c" + +size_t strchr_count(const char* str, int ch); +const char* str_n_substring_beg(const char* str, int ch, int n); +fort_status_t str_n_substring(const char* str, char ch_separator, size_t n, const char **begin, const char **end); +size_t buffer_text_width(string_buffer_t *buffer); + + +void test_strchr_count(void); +void test_str_n_substring(void); +void test_buffer_text_width(void); +void test_buffer_text_height(void); + + +void test_string_buffer(void **state) +{ + (void)state; + + test_strchr_count(); + test_str_n_substring(); + test_buffer_text_width(); + test_buffer_text_height(); +} + + +void test_strchr_count(void) +{ + assert_true(strchr_count(NULL, '\n') == 0); + assert_true(strchr_count("", '\n') == 0); + assert_true(strchr_count("asbd", '\n') == 0); + + assert_true(strchr_count("asbd\n", '\n') == 1); + assert_true(strchr_count("\nasbd", '\n') == 1); + assert_true(strchr_count("a\nsbd", '\n') == 1); + + assert_true(strchr_count("\n12\n123", '\n') == 2); + assert_true(strchr_count("\n12\n123\n", '\n') == 3); + assert_true(strchr_count("\n\n\n", '\n') == 3); + assert_true(strchr_count("\n123123\n123123\n\n\n123", '\n') == 5); + + assert_true(strchr_count("1a23123a123123aaa123", 'a') == 5); +} + + +void test_str_n_substring(void) +{ + const char *empty_str = ""; + assert_true(str_n_substring_beg(empty_str, '\n', 0) == empty_str); + assert_true(str_n_substring_beg(empty_str, '\n', 1) == NULL); + assert_true(str_n_substring_beg(empty_str, '\n', 2) == NULL); + + const char *str = "123\n5678\n9"; + assert_true(str_n_substring_beg(NULL, '\n', 0) == NULL); + assert_true(str_n_substring_beg(str, '\n', 0) == str); + assert_true(str_n_substring_beg(str, '1', 0) == str); + + assert_true(str_n_substring_beg(str, '\n', 1) == str + 4); + assert_true(str_n_substring_beg(str, '\n', 2) == str + 9); + assert_true(str_n_substring_beg(str, '\n', 3) == NULL); + + const char *str2 = "\n123\n56\n\n9\n"; + assert_true(str_n_substring_beg(str2, '\n', 0) == str2); + assert_true(str_n_substring_beg(str2, '\n', 1) == str2 + 1); + assert_true(str_n_substring_beg(str2, '\n', 2) == str2 + 5); + assert_true(str_n_substring_beg(str2, '\n', 3) == str2 + 8); + assert_true(str_n_substring_beg(str2, '\n', 4) == str2 + 9); + assert_true(str_n_substring_beg(str2, '\n', 5) == str2 + 11); + assert_true(str_n_substring_beg(str2, '\n', 6) == NULL); + + const char *beg = NULL; + const char *end = NULL; + str_n_substring(empty_str, '\n', 0, &beg, &end); + assert_true(beg == empty_str && end == empty_str + strlen(empty_str)); + str_n_substring(empty_str, '\n', 1, &beg, &end); + assert_true(beg == NULL && end == NULL); + str_n_substring(empty_str, '\n', 2, &beg, &end); + assert_true(beg == NULL && end == NULL); + + str_n_substring(NULL, '\n', 0, &beg, &end); + assert_true(beg == NULL && end == NULL); + str_n_substring(str, '\n', 0, &beg, &end); + assert_true(beg == str && end == str + 3); + str_n_substring(str, '2', 0, &beg, &end); + assert_true(beg == str && end == str + 1); + + str_n_substring(str, '\n', 1, &beg, &end); + assert_true(beg == str +4 && end == str + 8); + str_n_substring(str, '\n', 2, &beg, &end); + assert_true(beg == str + 9 && end == str + strlen(str)); + str_n_substring(str, '\n', 3, &beg, &end); + assert_true(beg == NULL && end == NULL); + + + str_n_substring(str2, '\n', 0, &beg, &end); + assert_true(beg == str2 && end == str2); + str_n_substring(str2, '\n', 1, &beg, &end); + assert_true(beg == str2 + 1 && end == str2 + 4); + str_n_substring(str2, '\n', 2, &beg, &end); + assert_true(beg == str2 + 5 && end == str2 + 7); + str_n_substring(str2, '\n', 3, &beg, &end); + assert_true(beg == str2 + 8 && end == str2 + 8); + str_n_substring(str2, '\n', 4, &beg, &end); + assert_true(beg == str2 + 9 && end == str2 + 10); + str_n_substring(str2, '\n', 5, &beg, &end); + assert_true(beg == str2 + 11 && end == str2 + 11); + str_n_substring(str2, '\n', 6, &beg, &end); + assert_true(beg == NULL && end == NULL); +} + +void test_buffer_text_width(void) +{ + string_buffer_t *buffer = create_string_buffer(200); + char *old_value = buffer->str; + + buffer->str = ""; + assert_true(buffer_text_width(buffer) == 0); + + buffer->str = "\n\n\n\n"; + assert_true(buffer_text_width(buffer) == 0); + + buffer->str = "12345"; + assert_true(buffer_text_width(buffer) == 5); + + buffer->str = "12345\n1234567"; + assert_true(buffer_text_width(buffer) == 7); + + buffer->str = "12345\n1234567\n"; + assert_true(buffer_text_width(buffer) == 7); + + buffer->str = "12345\n1234567\n123"; + assert_true(buffer_text_width(buffer) == 7); + + buffer->str = old_value; + destroy_string_buffer(buffer); +} + + +void test_buffer_text_height(void) +{ + string_buffer_t *buffer = create_string_buffer(200); + char *old_value = buffer->str; + + buffer->str = ""; + assert_true(buffer_text_height(buffer) == 0); + + buffer->str = "\n"; + assert_true(buffer_text_height(buffer) == 2); + + buffer->str = "\n\n"; + assert_true(buffer_text_height(buffer) == 3); + + buffer->str = "\n\n\n\n"; + assert_true(buffer_text_height(buffer) == 5); + + buffer->str = "12345"; + assert_true(buffer_text_height(buffer) == 1); + + buffer->str = "\n12345"; + assert_true(buffer_text_height(buffer) == 2); + + buffer->str = "\n12345\n\n2"; + assert_true(buffer_text_height(buffer) == 4); + + buffer->str = old_value; + destroy_string_buffer(buffer); +} diff --git a/tests/test_table.c b/tests/test_table.c index d4e1784..4dc5cb8 100644 --- a/tests/test_table.c +++ b/tests/test_table.c @@ -94,9 +94,12 @@ void test_table_geometry(void **state) void test_table_basic(void **state) { (void)state; - FTABLE *table = ft_create_table(); + + FTABLE *table = NULL; WHEN("All columns are equal and not empty") { + table = ft_create_table(); + int n = FT_HDR_PRINTF_LN(table, "%d|%c|%s|%f", 3, 'c', "234", 3.14); assert_true( n == 4 ); n = FT_PRINTF_LN(table, 1, "%d|%c|%s|%f", 3, 'c', "234", 3.14); @@ -611,4 +614,41 @@ void test_table_options(void **state) ft_destroy_table(table); } + + WHEN("All columns are equal and not empty") { + table = ft_create_table(); + + int n = FT_HDR_PRINTF_LN(table, "%d|%c|%s|%f", 3, 'c', "234", 3.14); + assert_true( n == 4 ); +// n = FT_PRINTF_LN(table, 1, "%d|%c|%s|%f", 3, 'c', "234\n123", 3.14); +// assert_true( n == 4 ); +// FT_NWRITE_LN(table, "3", "c", "234", "3.140000"); + FT_NWRITE_LN(table, "3", "c", "234\n12", "3.140000"); + n = FT_PRINTF_LN(table, 2, "%d|%c|%s|%f", 3, 'c', "234", 3.14); + assert_true( n == 4 ); + + const char *table_str = ft_to_string(table); + assert_true( table_str != NULL ); + const char *table_str_etalon = + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | |\n" + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | 12 | |\n" + "| | | | |\n" + "+---+---+-----+----------+\n" + "| | | | |\n" + "| 3 | c | 234 | 3.140000 |\n" + "| | | | |\n" + "+---+---+-----+----------+\n"; +// fprintf(stderr, "content:\n%s", table_str); + + assert_true( strcmp(table_str, table_str_etalon) == 0); + + ft_destroy_table(table); + } + } diff --git a/tests/tests.h b/tests/tests.h index ab5b36a..95f8961 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -16,6 +16,7 @@ void test_table_sizes(void **state); void test_table_geometry(void **state); void test_table_basic(void **state); void test_table_options(void **state); +void test_string_buffer(void **state); #endif // TESTS_H