2018-01-17 19:22:57 +01:00
|
|
|
#include "row.h"
|
|
|
|
#include "cell.h"
|
|
|
|
#include "string_buffer.h"
|
|
|
|
#include "assert.h"
|
|
|
|
#include "vector.h"
|
|
|
|
#include "ctype.h"
|
|
|
|
|
2018-01-17 19:34:15 +01:00
|
|
|
struct fort_row
|
|
|
|
{
|
|
|
|
vector_t *cells;
|
|
|
|
enum RowType type;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-17 19:22:57 +01:00
|
|
|
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;
|
|
|
|
}
|
2018-01-17 19:34:15 +01:00
|
|
|
// row->is_header = F_FALSE;
|
|
|
|
row->type = Common;
|
2018-01-17 19:22:57 +01:00
|
|
|
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,
|
2018-02-04 14:21:04 +01:00
|
|
|
enum HorSeparatorPos separatorPos,
|
|
|
|
const separator_t *sep, const context_t *context)
|
2018-01-17 19:22:57 +01:00
|
|
|
{
|
|
|
|
#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) {
|
2018-01-17 19:34:15 +01:00
|
|
|
main_row = lower_row->type == Header ? lower_row : upper_row;
|
2018-01-17 19:22:57 +01:00
|
|
|
} 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;
|
2018-01-17 19:34:15 +01:00
|
|
|
if (main_row && main_row->type == Header) {
|
2018-01-17 19:22:57 +01:00
|
|
|
border_chars = &context->header_border_chars;
|
|
|
|
} else {
|
|
|
|
border_chars = &context->border_chars;
|
|
|
|
}
|
|
|
|
|
2018-02-04 14:21:04 +01:00
|
|
|
if (sep && sep->enabled) {
|
|
|
|
L = &(context->separator_chars[LH_sip]);
|
|
|
|
I = &(context->separator_chars[IH_sip]);
|
|
|
|
IV = &(context->separator_chars[II_sip]);
|
|
|
|
R = &(context->separator_chars[RH_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];
|
|
|
|
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;
|
|
|
|
}
|
2018-01-17 19:22:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
|
2018-01-17 19:34:15 +01:00
|
|
|
const char (*bord_chars)[BorderItemPosSize] = (row->type == Header)
|
2018-01-17 19:22:57 +01:00
|
|
|
? (&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;
|
|
|
|
}
|
2018-01-17 19:34:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_row_type(fort_row_t *row, enum RowType type)
|
|
|
|
{
|
|
|
|
assert(row);
|
|
|
|
row->type = type;
|
|
|
|
}
|