libfort/src/row.c

1005 lines
32 KiB
C
Raw Normal View History

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-03-31 12:33:37 +02:00
struct fort_row {
2018-01-17 19:34:15 +01:00
vector_t *cells;
2018-04-01 12:27:02 +02:00
/*enum ft_row_type type;*/
2018-01-17 19:34:15 +01:00
};
2018-03-31 12:33:37 +02:00
fort_row_t *create_row(void)
2018-01-17 19:22:57 +01:00
{
2018-03-31 12:33:37 +02:00
fort_row_t *row = (fort_row_t *)F_CALLOC(sizeof(fort_row_t), 1);
2018-01-17 19:22:57 +01:00
if (row == NULL)
return NULL;
2018-03-31 12:33:37 +02:00
row->cells = create_vector(sizeof(fort_cell_t *), DEFAULT_VECTOR_CAPACITY);
2018-01-17 19:22:57 +01:00
if (row->cells == NULL) {
F_FREE(row);
return NULL;
}
2018-03-09 10:44:16 +01:00
/*
row->is_header = F_FALSE;
2018-04-01 12:27:02 +02:00
row->type = FT_ROW_COMMON;
2018-03-09 10:44:16 +01:00
*/
2018-01-17 19:22:57 +01:00
return row;
}
void destroy_row(fort_row_t *row)
{
2018-03-09 10:44:16 +01:00
size_t i = 0;
2018-01-17 19:22:57 +01:00
if (row == NULL)
return;
if (row->cells) {
size_t cells_n = vector_size(row->cells);
2018-03-09 10:44:16 +01:00
for (i = 0; i < cells_n; ++i) {
2018-01-17 19:22:57 +01:00
fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, i);
destroy_cell(cell);
}
destroy_vector(row->cells);
}
F_FREE(row);
}
2018-04-17 19:14:50 +02:00
size_t columns_in_row(const fort_row_t *row)
2018-01-17 19:22:57 +01:00
{
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)) {
2018-03-31 12:33:37 +02:00
return *(fort_cell_t **)vector_at(row->cells, col);
2018-01-17 19:22:57 +01:00
}
return NULL;
break;
case Create:
2018-03-31 12:33:37 +02:00
while (col >= columns_in_row(row)) {
2018-01-17 19:22:57 +01:00
fort_cell_t *new_cell = create_cell();
if (new_cell == NULL)
return NULL;
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(vector_push(row->cells, &new_cell))) {
2018-01-17 19:22:57 +01:00
destroy_cell(new_cell);
return NULL;
}
}
2018-03-31 12:33:37 +02:00
return *(fort_cell_t **)vector_at(row->cells, col);
2018-01-17 19:22:57 +01:00
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);
}
2018-04-22 20:42:22 +02:00
fort_status_t swap_row(fort_row_t *cur_row, fort_row_t *ins_row, size_t pos)
{
assert(cur_row);
assert(ins_row);
size_t cur_sz = vector_size(cur_row->cells);
if (cur_sz == 0 && pos == 0) {
fort_row_t tmp;
memcpy(&tmp, cur_row, sizeof(fort_row_t));
memcpy(cur_row, ins_row, sizeof(fort_row_t));
memcpy(ins_row, &tmp, sizeof(fort_row_t));
return FT_SUCCESS;
}
return vector_swap(cur_row->cells, ins_row->cells, pos);
}
2018-05-02 16:55:29 +02:00
size_t group_cell_number(const fort_row_t *row, size_t master_cell_col)
{
assert(row);
const fort_cell_t *cell = get_cell_c(row, master_cell_col);
if (cell == NULL)
return 0;
size_t total_cols = vector_size(row->cells);
size_t slave_col = master_cell_col + 1;
while (slave_col < total_cols) {
const fort_cell_t *cell = get_cell_c(row, slave_col);
if (cell && get_cell_type(cell) == GroupSlaveCell) {
++slave_col;
} else {
break;
}
}
return slave_col - master_cell_col;
}
int get_row_cell_types(const fort_row_t *row, enum CellType *types, size_t types_sz)
{
assert(row);
assert(types);
size_t i = 0;
for (i = 0; i < types_sz; ++i) {
const fort_cell_t *cell = get_cell_c(row, i);
if (cell) {
types[i] = get_cell_type(cell);
} else {
types[i] = CommonCell;
}
}
return FT_SUCCESS;
}
2018-01-17 19:22:57 +01:00
2018-05-04 20:25:29 +02:00
fort_status_t row_set_cell_span(fort_row_t *row, size_t cell_column, size_t hor_span)
{
assert(row);
if (hor_span < 2)
return FT_EINVAL;
fort_cell_t *main_cell = get_cell_and_create_if_not_exists(row, cell_column);
if (main_cell == NULL) {
return FT_ERROR;
}
set_cell_type(main_cell, GroupMasterCell);
--hor_span;
++cell_column;
while (hor_span) {
fort_cell_t *slave_cell = get_cell_and_create_if_not_exists(row, cell_column);
if (slave_cell == NULL) {
return FT_ERROR;
}
set_cell_type(slave_cell, GroupSlaveCell);
--hor_span;
++cell_column;
}
return FT_SUCCESS;
}
2018-01-17 19:22:57 +01:00
int print_row_separator(char *buffer, size_t buffer_sz,
2018-03-31 12:33:37 +02:00
const size_t *col_width_arr, size_t cols,
const fort_row_t *upper_row, const fort_row_t *lower_row,
enum HorSeparatorPos separatorPos,
const separator_t *sep, const context_t *context)
2018-01-17 19:22:57 +01:00
{
2018-05-02 20:16:41 +02:00
// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars;
int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings;
2018-03-05 19:08:14 +01:00
2018-01-17 19:22:57 +01:00
assert(buffer);
assert(context);
2018-05-02 20:16:41 +02:00
const char *space_char = " ";
2018-05-02 16:55:29 +02:00
int status = -1;
/* Get cell types
*
* Regions above top row and below bottom row areconsidered full of virtual
* GroupSlaveCell cells
*/
enum CellType *top_row_types = F_MALLOC(sizeof(enum CellType) * cols * 2);
if (top_row_types == NULL) {
return FT_MEMORY_ERROR;
}
enum CellType *bottom_row_types = top_row_types + cols;
if (upper_row) {
get_row_cell_types(upper_row, top_row_types, cols);
} else {
size_t i = 0;
for (i = 0; i < cols; ++i)
top_row_types[i] = GroupSlaveCell;
}
if (lower_row) {
get_row_cell_types(lower_row, bottom_row_types, cols);
} else {
size_t i = 0;
for (i = 0; i < cols; ++i)
bottom_row_types[i] = GroupSlaveCell;
}
2018-03-31 16:54:01 +02:00
int written = 0;
int tmp = 0;
2018-01-17 19:22:57 +01:00
2018-04-01 12:27:02 +02:00
enum ft_row_type lower_row_type = FT_ROW_COMMON;
2018-03-05 19:08:14 +01:00
if (lower_row != NULL) {
2018-04-01 12:27:02 +02:00
lower_row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
2018-03-05 19:08:14 +01:00
}
2018-04-01 12:27:02 +02:00
enum ft_row_type upper_row_type = FT_ROW_COMMON;
2018-03-05 19:08:14 +01:00
if (upper_row != NULL) {
2018-04-01 12:27:02 +02:00
upper_row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row - 1, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
2018-01-17 19:22:57 +01:00
}
2018-05-02 16:55:29 +02:00
/* Row separator anatomy
2018-01-17 19:22:57 +01:00
*
2018-05-02 16:55:29 +02:00
* | C11 | C12 C13 | C14 C15 |
* L I I I IV I I IT I I I IB I I II I I R
* | C21 | C22 | C23 C24 C25 |
*/
2018-05-02 20:16:41 +02:00
const char **L = NULL;
const char **I = NULL;
const char **IV = NULL;
const char **R = NULL;
const char **IT = NULL;
const char **IB = NULL;
const char **II = NULL;
2018-05-02 16:55:29 +02:00
2018-01-17 19:22:57 +01:00
2018-05-02 20:16:41 +02:00
typedef const char *(*border_chars_point_t)[BorderItemPosSize];
const char *(*border_chars)[BorderItemPosSize] = NULL;
2018-03-12 21:02:59 +01:00
border_chars = (border_chars_point_t)&context->table_options->border_style.border_chars;
2018-04-01 12:27:02 +02:00
if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) {
2018-03-12 21:02:59 +01:00
border_chars = (border_chars_point_t)&context->table_options->border_style.header_border_chars;
2018-01-17 19:22:57 +01:00
}
2018-02-04 14:21:04 +01:00
if (sep && sep->enabled) {
2018-03-12 21:02:59 +01:00
L = &(context->table_options->border_style.separator_chars[LH_sip]);
I = &(context->table_options->border_style.separator_chars[IH_sip]);
IV = &(context->table_options->border_style.separator_chars[II_sip]);
R = &(context->table_options->border_style.separator_chars[RH_sip]);
2018-05-02 16:55:29 +02:00
IT = &(context->table_options->border_style.separator_chars[II_sip]);
IB = &(context->table_options->border_style.separator_chars[II_sip]);
II = &(context->table_options->border_style.separator_chars[IH_sip]);
2018-02-04 14:21:04 +01:00
} 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];
2018-05-02 16:55:29 +02:00
IT = &(*border_chars)[TV_bip];
IB = &(*border_chars)[TV_bip];
II = &(*border_chars)[TT_bip];
2018-02-04 14:21:04 +01:00
break;
case InsideSeparator:
L = &(*border_chars)[LH_bip];
I = &(*border_chars)[IH_bip];
IV = &(*border_chars)[II_bip];
R = &(*border_chars)[RH_bip];
2018-05-02 16:55:29 +02:00
IT = &(*border_chars)[TV_bip];
IB = &(*border_chars)[BV_bip];
II = &(*border_chars)[IH_bip];
2018-02-04 14:21:04 +01:00
break;
case BottomSeparator:
L = &(*border_chars)[BL_bip];
I = &(*border_chars)[BB_bip];
IV = &(*border_chars)[BV_bip];
R = &(*border_chars)[BR_bip];
2018-05-02 16:55:29 +02:00
IT = &(*border_chars)[BV_bip];
IB = &(*border_chars)[BV_bip];
II = &(*border_chars)[BB_bip];
2018-02-04 14:21:04 +01:00
break;
default:
break;
}
2018-01-17 19:22:57 +01:00
}
2018-03-05 19:08:14 +01:00
/* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */
2018-05-02 20:16:41 +02:00
// if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) {
// status = 0;
// goto clear;
// }
if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L)))
&& (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I)))
&& (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV)))
&& (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) {
2018-05-02 16:55:29 +02:00
status = 0;
goto clear;
}
2018-01-17 19:22:57 +01:00
2018-03-09 10:44:16 +01:00
size_t i = 0;
2018-03-25 10:32:10 +02:00
/* Print left margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.left_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-03-09 10:44:16 +01:00
for (i = 0; i < cols; ++i) {
2018-01-17 19:22:57 +01:00
if (i == 0) {
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L));
2018-01-17 19:22:57 +01:00
} else {
2018-05-02 16:55:29 +02:00
if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell)
2018-05-02 20:16:41 +02:00
&& (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) {
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV));
2018-05-02 16:55:29 +02:00
} else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) {
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II));
2018-05-02 16:55:29 +02:00
} else if (top_row_types[i] == GroupSlaveCell) {
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT));
2018-05-02 16:55:29 +02:00
} else {
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB));
2018-05-02 16:55:29 +02:00
}
2018-01-17 19:22:57 +01:00
}
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I));
2018-01-17 19:22:57 +01:00
}
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R));
2018-01-17 19:22:57 +01:00
2018-03-25 10:32:10 +02:00
/* Print right margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.right_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n"));
2018-01-17 19:22:57 +01:00
2018-05-02 16:55:29 +02:00
status = written;
2018-01-17 19:22:57 +01:00
clear:
2018-05-02 16:55:29 +02:00
F_FREE(top_row_types);
return status;
2018-01-17 19:22:57 +01:00
}
2018-03-05 19:08:14 +01:00
int wprint_row_separator(wchar_t *buffer, size_t buffer_sz,
2018-03-31 12:33:37 +02:00
const size_t *col_width_arr, size_t cols,
const fort_row_t *upper_row, const fort_row_t *lower_row,
enum HorSeparatorPos separatorPos, const separator_t *sep,
const context_t *context)
2018-03-05 19:08:14 +01:00
{
2018-05-02 20:16:41 +02:00
// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars;
int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string;
2018-03-05 19:08:14 +01:00
assert(buffer);
assert(context);
2018-05-02 20:16:41 +02:00
const char *space_char = " ";
int status = -1;
/* Get cell types
*
* Regions above top row and below bottom row areconsidered full of virtual
* GroupSlaveCell cells
*/
enum CellType *top_row_types = F_MALLOC(sizeof(enum CellType) * cols * 2);
if (top_row_types == NULL) {
return FT_MEMORY_ERROR;
}
enum CellType *bottom_row_types = top_row_types + cols;
if (upper_row) {
get_row_cell_types(upper_row, top_row_types, cols);
} else {
size_t i = 0;
for (i = 0; i < cols; ++i)
top_row_types[i] = GroupSlaveCell;
}
if (lower_row) {
get_row_cell_types(lower_row, bottom_row_types, cols);
} else {
size_t i = 0;
for (i = 0; i < cols; ++i)
bottom_row_types[i] = GroupSlaveCell;
}
2018-03-31 16:54:01 +02:00
int written = 0;
int tmp = 0;
2018-03-05 19:08:14 +01:00
2018-04-01 12:27:02 +02:00
enum ft_row_type lower_row_type = FT_ROW_COMMON;
2018-03-05 19:08:14 +01:00
if (lower_row != NULL) {
2018-04-01 12:27:02 +02:00
lower_row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
2018-03-05 19:08:14 +01:00
}
2018-04-01 12:27:02 +02:00
enum ft_row_type upper_row_type = FT_ROW_COMMON;
2018-03-05 19:08:14 +01:00
if (upper_row != NULL) {
2018-04-01 12:27:02 +02:00
upper_row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row - 1, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
2018-03-05 19:08:14 +01:00
}
2018-05-02 20:16:41 +02:00
/* Row separator anatomy
2018-03-05 19:08:14 +01:00
*
2018-05-02 20:16:41 +02:00
* | C11 | C12 C13 | C14 C15 |
* L I I I IV I I IT I I I IB I I II I I R
* | C21 | C22 | C23 C24 C25 |
*/
const char **L = NULL;
const char **I = NULL;
const char **IV = NULL;
const char **R = NULL;
const char **IT = NULL;
const char **IB = NULL;
const char **II = NULL;
2018-03-05 19:08:14 +01:00
2018-05-02 20:16:41 +02:00
typedef const char *(*border_chars_point_t)[BorderItemPosSize];
const char *(*border_chars)[BorderItemPosSize] = NULL;
2018-03-12 21:02:59 +01:00
border_chars = (border_chars_point_t)&context->table_options->border_style.border_chars;
2018-04-01 12:27:02 +02:00
if (upper_row_type == FT_ROW_HEADER || lower_row_type == FT_ROW_HEADER) {
2018-03-12 21:02:59 +01:00
border_chars = (border_chars_point_t)&context->table_options->border_style.header_border_chars;
2018-03-05 19:08:14 +01:00
}
if (sep && sep->enabled) {
2018-03-12 21:02:59 +01:00
L = &(context->table_options->border_style.separator_chars[LH_sip]);
I = &(context->table_options->border_style.separator_chars[IH_sip]);
IV = &(context->table_options->border_style.separator_chars[II_sip]);
R = &(context->table_options->border_style.separator_chars[RH_sip]);
2018-05-02 20:16:41 +02:00
IT = &(context->table_options->border_style.separator_chars[II_sip]);
IB = &(context->table_options->border_style.separator_chars[II_sip]);
II = &(context->table_options->border_style.separator_chars[IH_sip]);
2018-03-05 19:08:14 +01:00
} 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];
2018-05-02 20:16:41 +02:00
IT = &(*border_chars)[TV_bip];
IB = &(*border_chars)[TV_bip];
II = &(*border_chars)[TT_bip];
2018-03-05 19:08:14 +01:00
break;
case InsideSeparator:
L = &(*border_chars)[LH_bip];
I = &(*border_chars)[IH_bip];
IV = &(*border_chars)[II_bip];
R = &(*border_chars)[RH_bip];
2018-05-02 20:16:41 +02:00
IT = &(*border_chars)[TV_bip];
IB = &(*border_chars)[BV_bip];
II = &(*border_chars)[IH_bip];
2018-03-05 19:08:14 +01:00
break;
case BottomSeparator:
L = &(*border_chars)[BL_bip];
I = &(*border_chars)[BB_bip];
IV = &(*border_chars)[BV_bip];
R = &(*border_chars)[BR_bip];
2018-05-02 20:16:41 +02:00
IT = &(*border_chars)[BV_bip];
IB = &(*border_chars)[BV_bip];
II = &(*border_chars)[BB_bip];
2018-03-05 19:08:14 +01:00
break;
default:
break;
}
}
/* If all chars are not printable, skip line separator */ /* todo: add processing for wchar_t */
2018-05-02 20:16:41 +02:00
// if (!isprint(*L) && !isprint(*I) && !isprint(*IV) && !isprint(*R)) {
// status = 0;
// goto clear;
// }
if ((strlen(*L) == 0 || (strlen(*L) == 1 && !isprint(**L)))
&& (strlen(*I) == 0 || (strlen(*I) == 1 && !isprint(**I)))
&& (strlen(*IV) == 0 || (strlen(*IV) == 1 && !isprint(**IV)))
&& (strlen(*R) == 0 || (strlen(*R) == 1 && !isprint(**R)))) {
status = 0;
goto clear;
}
2018-03-05 19:08:14 +01:00
2018-03-09 10:44:16 +01:00
size_t i = 0;
2018-03-25 10:32:10 +02:00
/* Print left margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.left_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-03-09 10:44:16 +01:00
for (i = 0; i < cols; ++i) {
2018-03-05 19:08:14 +01:00
if (i == 0) {
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *L));
2018-03-05 19:08:14 +01:00
} else {
2018-05-02 20:16:41 +02:00
if ((top_row_types[i] == CommonCell || top_row_types[i] == GroupMasterCell)
&& (bottom_row_types[i] == CommonCell || bottom_row_types[i] == GroupMasterCell)) {
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IV));
} else if (top_row_types[i] == GroupSlaveCell && bottom_row_types[i] == GroupSlaveCell) {
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *II));
} else if (top_row_types[i] == GroupSlaveCell) {
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IT));
} else {
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *IB));
}
2018-03-05 19:08:14 +01:00
}
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, col_width_arr[i], *I));
2018-03-05 19:08:14 +01:00
}
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, *R));
2018-03-05 19:08:14 +01:00
2018-03-25 10:32:10 +02:00
/* Print right margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, context->table_options->entire_table_options.right_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buffer_sz - written, 1, "\n"));
2018-03-05 19:08:14 +01:00
2018-05-02 20:16:41 +02:00
status = written;
2018-03-05 19:08:14 +01:00
clear:
2018-05-02 20:16:41 +02:00
F_FREE(top_row_types);
return status;
2018-03-05 19:08:14 +01:00
}
2018-01-17 19:22:57 +01:00
2018-03-31 12:33:37 +02:00
fort_row_t *create_row_from_string(const char *str)
2018-01-17 19:22:57 +01:00
{
2018-04-24 20:36:07 +02:00
typedef char char_type;
char_type *(*strdup_)(const char_type * str) = F_STRDUP;
const char_type zero_char = '\0';
fort_status_t (*fill_cell_from_string_)(fort_cell_t *cell, const char *str) = fill_cell_from_string;
const char_type *const zero_string = "";
#define STRCHR strchr
char_type *pos = NULL;
char_type *base_pos = NULL;
2018-04-16 21:33:05 +02:00
unsigned int number_of_separators = 0;
2018-03-31 12:33:37 +02:00
fort_row_t *row = create_row();
2018-01-17 19:22:57 +01:00
if (row == NULL)
return NULL;
if (str == NULL)
return row;
2018-04-24 20:36:07 +02:00
char_type *str_copy = strdup_(str);
2018-01-17 19:22:57 +01:00
if (str_copy == NULL)
goto clear;
pos = str_copy;
base_pos = str_copy;
number_of_separators = 0;
2018-01-17 19:22:57 +01:00
while (*pos) {
2018-04-24 20:36:07 +02:00
pos = STRCHR(pos, FORT_COL_SEPARATOR);
2018-01-17 19:22:57 +01:00
if (pos != NULL) {
2018-04-24 20:36:07 +02:00
*(pos) = zero_char;
2018-01-17 19:22:57 +01:00
++pos;
number_of_separators++;
}
fort_cell_t *cell = create_cell();
if (cell == NULL)
goto clear;
2018-04-24 20:36:07 +02:00
int status = fill_cell_from_string_(cell, base_pos);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-01-17 19:22:57 +01:00
destroy_cell(cell);
goto clear;
}
status = vector_push(row->cells, &cell);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-01-17 19:22:57 +01:00
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;
2018-04-24 20:36:07 +02:00
int status = fill_cell_from_string_(cell, zero_string);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-01-17 19:22:57 +01:00
destroy_cell(cell);
goto clear;
}
status = vector_push(row->cells, &cell);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-01-17 19:22:57 +01:00
destroy_cell(cell);
goto clear;
}
}
F_FREE(str_copy);
return row;
clear:
destroy_row(row);
F_FREE(str_copy);
return NULL;
2018-04-24 20:36:07 +02:00
#undef STRCHR
2018-01-17 19:22:57 +01:00
}
2018-04-24 20:36:07 +02:00
#ifdef FT_HAVE_WCHAR
fort_row_t *create_row_from_wstring(const wchar_t *str)
{
typedef wchar_t char_type;
char_type *(*strdup_)(const char_type * str) = F_WCSDUP;
const char_type zero_char = L'\0';
fort_status_t (*fill_cell_from_string_)(fort_cell_t *cell, const wchar_t *str) = fill_cell_from_wstring;
const char_type *const zero_string = L"";
#define STRCHR wcschr
char_type *pos = NULL;
char_type *base_pos = NULL;
unsigned int number_of_separators = 0;
fort_row_t *row = create_row();
if (row == NULL)
return NULL;
if (str == NULL)
return row;
char_type *str_copy = strdup_(str);
if (str_copy == NULL)
goto clear;
pos = str_copy;
base_pos = str_copy;
number_of_separators = 0;
while (*pos) {
pos = STRCHR(pos, FORT_COL_SEPARATOR);
if (pos != NULL) {
*(pos) = zero_char;
++pos;
number_of_separators++;
}
fort_cell_t *cell = create_cell();
if (cell == NULL)
goto clear;
int status = fill_cell_from_string_(cell, base_pos);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-04-24 20:36:07 +02:00
destroy_cell(cell);
goto clear;
}
status = vector_push(row->cells, &cell);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-04-24 20:36:07 +02:00
destroy_cell(cell);
goto clear;
}
if (pos == NULL)
break;
base_pos = pos;
}
/* special case if in format string last cell is empty */
while (vector_size(row->cells) < (number_of_separators + 1)) {
fort_cell_t *cell = create_cell();
if (cell == NULL)
goto clear;
int status = fill_cell_from_string_(cell, zero_string);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-04-24 20:36:07 +02:00
destroy_cell(cell);
goto clear;
}
status = vector_push(row->cells, &cell);
2018-05-05 17:38:45 +02:00
if (FT_IS_ERROR(status)) {
2018-04-24 20:36:07 +02:00
destroy_cell(cell);
goto clear;
}
}
F_FREE(str_copy);
return row;
clear:
destroy_row(row);
F_FREE(str_copy);
return NULL;
#undef STRCHR
}
#endif
2018-01-17 19:22:57 +01:00
2018-03-31 12:33:37 +02:00
fort_row_t *create_row_from_fmt_string(const char *fmt, va_list *va_args)
2018-01-17 19:22:57 +01:00
{
2018-04-24 20:36:07 +02:00
#define VSNPRINTF vsnprintf
#define STR_FILED cstr
#define CREATE_ROW_FROM_STRING create_row_from_string
#define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_string
string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf);
if (buffer == NULL)
return NULL;
size_t cols_origin = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(fmt);
size_t cols = 0;
while (1) {
va_list va;
va_copy(va, *va_args);
int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va);
va_end(va);
/* If error encountered */
if (virtual_sz < 0)
goto clear;
/* Successful write */
if ((size_t)virtual_sz < string_buffer_capacity(buffer))
break;
/* Otherwise buffer was too small, so incr. buffer size ant try again. */
2018-05-05 17:38:45 +02:00
if (!FT_IS_SUCCESS(realloc_string_buffer_without_copy(buffer)))
2018-04-24 20:36:07 +02:00
goto clear;
}
cols = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(buffer->str.STR_FILED);
if (cols == cols_origin) {
fort_row_t *row = CREATE_ROW_FROM_STRING(buffer->str.STR_FILED);
if (row == NULL) {
goto clear;
}
destroy_string_buffer(buffer);
return row;
}
/* todo: add processing of cols != cols_origin */
clear:
destroy_string_buffer(buffer);
return NULL;
#undef VSNPRINTF
#undef STR_FILED
#undef CREATE_ROW_FROM_STRING
#undef NUMBER_OF_COLUMNS_IN_FORMAT_STRING
}
#ifdef FT_HAVE_WCHAR
fort_row_t *create_row_from_fmt_wstring(const wchar_t *fmt, va_list *va_args)
{
#define VSNPRINTF vswprintf
#define STR_FILED wstr
#define CREATE_ROW_FROM_STRING create_row_from_wstring
#define NUMBER_OF_COLUMNS_IN_FORMAT_STRING number_of_columns_in_format_wstring
2018-03-05 19:08:14 +01:00
string_buffer_t *buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CharBuf);
2018-01-17 19:22:57 +01:00
if (buffer == NULL)
return NULL;
2018-04-24 20:36:07 +02:00
size_t cols_origin = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(fmt);
2018-03-29 20:25:04 +02:00
size_t cols = 0;
2018-01-17 19:22:57 +01:00
while (1) {
va_list va;
va_copy(va, *va_args);
2018-04-24 20:36:07 +02:00
int virtual_sz = VSNPRINTF(buffer->str.STR_FILED, string_buffer_capacity(buffer), fmt, va);
2018-01-17 19:22:57 +01:00
va_end(va);
/* If error encountered */
2018-04-16 21:33:05 +02:00
if (virtual_sz < 0)
2018-01-17 19:22:57 +01:00
goto clear;
/* Successful write */
2018-04-16 21:33:05 +02:00
if ((size_t)virtual_sz < string_buffer_capacity(buffer))
2018-01-17 19:22:57 +01:00
break;
/* Otherwise buffer was too small, so incr. buffer size ant try again. */
2018-05-05 17:38:45 +02:00
if (!FT_IS_SUCCESS(realloc_string_buffer_without_copy(buffer)))
2018-01-17 19:22:57 +01:00
goto clear;
}
2018-04-24 20:36:07 +02:00
cols = NUMBER_OF_COLUMNS_IN_FORMAT_STRING(buffer->str.STR_FILED);
2018-01-17 19:22:57 +01:00
if (cols == cols_origin) {
2018-04-24 20:36:07 +02:00
fort_row_t *row = CREATE_ROW_FROM_STRING(buffer->str.STR_FILED);
2018-01-17 19:22:57 +01:00
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;
2018-04-24 20:36:07 +02:00
#undef VSNPRINTF
#undef STR_FILED
#undef CREATE_ROW_FROM_STRING
#undef NUMBER_OF_COLUMNS_IN_FORMAT_STRING
2018-01-17 19:22:57 +01:00
}
2018-04-24 20:36:07 +02:00
#endif
2018-01-17 19:22:57 +01:00
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,
2018-03-31 12:33:37 +02:00
size_t row_height, const context_t *context)
2018-01-17 19:22:57 +01:00
{
2018-05-02 20:16:41 +02:00
// int (*snprint_n_chars_)(char *, size_t, size_t, char) = snprint_n_chars;
int (*snprint_n_strings_)(char *, size_t, size_t, const char *) = snprint_n_strings;
2018-03-29 20:25:04 +02:00
int (*cell_printf_)(fort_cell_t *, size_t, char *, size_t, const context_t *) = cell_printf;
2018-03-05 19:08:14 +01:00
2018-01-17 19:22:57 +01:00
assert(context);
2018-05-02 20:16:41 +02:00
const char *space_char = " ";
const char *new_line_char = "\n";
2018-01-17 19:22:57 +01:00
if (row == NULL)
return -1;
2018-04-17 19:14:50 +02:00
size_t cols_in_row = columns_in_row(row);
2018-01-17 19:22:57 +01:00
if (cols_in_row > col_width_arr_sz)
return -1;
/* Row separator anatomy
*
* L data IV data IV data R
*/
2018-05-02 20:16:41 +02:00
typedef const char *(*border_chars_point_t)[BorderItemPosSize];
2018-04-01 12:27:02 +02:00
enum ft_row_type row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
2018-05-02 20:16:41 +02:00
const char *(*bord_chars)[BorderItemPosSize] = (row_type == FT_ROW_HEADER)
2018-03-31 12:33:37 +02:00
? (border_chars_point_t)(&context->table_options->border_style.header_border_chars)
: (border_chars_point_t)(&context->table_options->border_style.border_chars);
2018-05-02 20:16:41 +02:00
const char **L = &(*bord_chars)[LL_bip];
const char **IV = &(*bord_chars)[IV_bip];
const char **R = &(*bord_chars)[RR_bip];
2018-01-17 19:22:57 +01:00
2018-03-31 16:54:01 +02:00
int written = 0;
int tmp = 0;
2018-03-09 10:44:16 +01:00
size_t i = 0;
for (i = 0; i < row_height; ++i) {
2018-03-25 10:32:10 +02:00
/* Print left margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.left_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-05-02 16:55:29 +02:00
/* Print left table boundary */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L));
2018-03-09 10:44:16 +01:00
size_t j = 0;
2018-05-02 16:55:29 +02:00
while (j < col_width_arr_sz) {
2018-01-17 19:22:57 +01:00
if (j < cols_in_row) {
2018-05-02 16:55:29 +02:00
((context_t *)context)->column = j;
2018-03-31 12:33:37 +02:00
fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j);
2018-05-02 16:55:29 +02:00
size_t cell_width = 0;
size_t group_slave_sz = group_cell_number(row, j);
cell_width = col_width_arr[j];
size_t slave_j = 0;
size_t master_j = j;
for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) {
cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH;
++j;
}
CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context));
2018-01-17 19:22:57 +01:00
} else {
2018-05-02 16:55:29 +02:00
/* Print empty cell */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char));
2018-01-17 19:22:57 +01:00
}
2018-05-02 16:55:29 +02:00
/* Print boundary between cells */
if (j < col_width_arr_sz - 1)
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV));
2018-05-02 16:55:29 +02:00
++j;
2018-01-17 19:22:57 +01:00
}
2018-03-25 10:32:10 +02:00
2018-05-02 16:55:29 +02:00
/* Print right table boundary */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R));
2018-05-02 16:55:29 +02:00
2018-03-25 10:32:10 +02:00
/* Print right margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.right_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-05-02 16:55:29 +02:00
/* Print new line character */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char));
2018-01-17 19:22:57 +01:00
}
2018-03-31 16:54:01 +02:00
return written;
2018-03-25 10:32:10 +02:00
clear:
return -1;
2018-01-17 19:22:57 +01:00
}
2018-01-17 19:34:15 +01:00
2018-05-06 12:12:28 +02:00
#ifdef FT_HAVE_WCHAR
2018-01-17 19:34:15 +01:00
2018-03-05 19:08:14 +01:00
int wsnprintf_row(const fort_row_t *row, wchar_t *buffer, size_t buf_sz, size_t *col_width_arr, size_t col_width_arr_sz,
2018-03-31 12:33:37 +02:00
size_t row_height, const context_t *context)
2018-01-17 19:34:15 +01:00
{
2018-05-02 20:16:41 +02:00
// int (*snprint_n_chars_)(wchar_t *, size_t, size_t, wchar_t) = wsnprint_n_chars;
int (*snprint_n_strings_)(wchar_t *, size_t, size_t, const char *) = wsnprint_n_string;
2018-03-29 20:25:04 +02:00
int (*cell_printf_)(fort_cell_t *, size_t, wchar_t *, size_t, const context_t *) = cell_wprintf;
2018-03-05 19:08:14 +01:00
assert(context);
2018-05-02 20:16:41 +02:00
const char *space_char = " ";
const char *new_line_char = "\n";
2018-03-05 19:08:14 +01:00
if (row == NULL)
return -1;
2018-04-17 19:14:50 +02:00
size_t cols_in_row = columns_in_row(row);
2018-03-05 19:08:14 +01:00
if (cols_in_row > col_width_arr_sz)
return -1;
/* Row separator anatomy
*
* L data IV data IV data R
*/
2018-05-02 20:16:41 +02:00
typedef const char *(*border_chars_point_t)[BorderItemPosSize];
2018-04-01 12:27:02 +02:00
enum ft_row_type row_type = (enum ft_row_type)get_cell_opt_value_hierarcial(context->table_options, context->row, FT_ANY_COLUMN, FT_COPT_ROW_TYPE);
2018-05-02 20:16:41 +02:00
const char *(*bord_chars)[BorderItemPosSize] = (row_type == FT_ROW_HEADER)
2018-03-31 12:33:37 +02:00
? (border_chars_point_t)(&context->table_options->border_style.header_border_chars)
: (border_chars_point_t)(&context->table_options->border_style.border_chars);
2018-05-02 20:16:41 +02:00
const char **L = &(*bord_chars)[LL_bip];
const char **IV = &(*bord_chars)[IV_bip];
const char **R = &(*bord_chars)[RR_bip];
2018-03-05 19:08:14 +01:00
2018-03-31 16:54:01 +02:00
int written = 0;
int tmp = 0;
2018-03-09 10:44:16 +01:00
size_t i = 0;
for (i = 0; i < row_height; ++i) {
2018-03-25 10:32:10 +02:00
/* Print left margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.left_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-05-02 20:16:41 +02:00
/* Print left table boundary */
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *L));
2018-03-09 10:44:16 +01:00
size_t j = 0;
2018-05-02 20:16:41 +02:00
while (j < col_width_arr_sz) {
2018-03-05 19:08:14 +01:00
if (j < cols_in_row) {
2018-05-02 20:16:41 +02:00
((context_t *)context)->column = j;
2018-03-31 12:33:37 +02:00
fort_cell_t *cell = *(fort_cell_t **)vector_at(row->cells, j);
2018-05-02 20:16:41 +02:00
size_t cell_width = 0;
size_t group_slave_sz = group_cell_number(row, j);
cell_width = col_width_arr[j];
size_t slave_j = 0;
size_t master_j = j;
for (slave_j = master_j + 1; slave_j < (master_j + group_slave_sz); ++slave_j) {
cell_width += col_width_arr[slave_j] + FORT_COL_SEPARATOR_LENGTH;
++j;
}
CHCK_RSLT_ADD_TO_WRITTEN(cell_printf_(cell, i, buffer + written, cell_width + 1, context));
2018-03-05 19:08:14 +01:00
} else {
2018-05-02 20:16:41 +02:00
/* Print empty cell */
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, col_width_arr[j], space_char));
2018-03-05 19:08:14 +01:00
}
2018-05-02 20:16:41 +02:00
/* Print boundary between cells */
if (j < col_width_arr_sz - 1)
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *IV));
++j;
2018-03-05 19:08:14 +01:00
}
2018-03-25 10:32:10 +02:00
2018-05-02 20:16:41 +02:00
/* Print right table boundary */
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, *R));
2018-03-25 10:32:10 +02:00
/* Print right margin */
2018-05-02 20:16:41 +02:00
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, context->table_options->entire_table_options.right_margin, space_char));
2018-03-25 10:32:10 +02:00
2018-05-02 20:16:41 +02:00
/* Print new line character */
CHCK_RSLT_ADD_TO_WRITTEN(snprint_n_strings_(buffer + written, buf_sz - written, 1, new_line_char));
2018-03-05 19:08:14 +01:00
}
2018-03-31 16:54:01 +02:00
return written;
2018-03-25 10:32:10 +02:00
clear:
return -1;
2018-01-17 19:34:15 +01:00
}
2018-03-05 19:08:14 +01:00
2018-05-06 12:12:28 +02:00
#endif
2018-05-04 20:25:29 +02:00