540 lines
14 KiB
C
540 lines
14 KiB
C
/*
|
|
libfort
|
|
|
|
MIT License
|
|
|
|
Copyright (c) 2017 - 2018 Seleznev Anton
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
#include "stdlib.h"
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
#include "fort.h"
|
|
#include "assert.h"
|
|
#include "string.h"
|
|
#include "wchar.h"
|
|
#include <ctype.h>
|
|
|
|
#include "vector.h"
|
|
#include "fort_impl.h"
|
|
#include "string_buffer.h"
|
|
#include "table.h"
|
|
#include "row.h"
|
|
#include "options.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* LIBFORT
|
|
* ***************************************************************************/
|
|
|
|
FTABLE * ft_create_table(void)
|
|
{
|
|
FTABLE *result = F_CALLOC(1, sizeof(FTABLE));
|
|
if (result == NULL)
|
|
return NULL;
|
|
|
|
result->rows = create_vector(sizeof(fort_row_t*), DEFAULT_VECTOR_CAPACITY);
|
|
if (result->rows == NULL) {
|
|
F_FREE(result);
|
|
return NULL;
|
|
}
|
|
result->options = NULL;
|
|
result->conv_buffer = NULL;
|
|
result->cur_row = 0;
|
|
result->cur_col = 0;
|
|
return result;
|
|
}
|
|
|
|
|
|
void ft_destroy_table(FTABLE *FORT_RESTRICT table)
|
|
{
|
|
if (table == NULL)
|
|
return;
|
|
|
|
if (table->rows) {
|
|
size_t row_n = vector_size(table->rows);
|
|
for (size_t i = 0; i < row_n; ++i) {
|
|
destroy_row(*(fort_row_t **)vector_at(table->rows, i));
|
|
}
|
|
destroy_vector(table->rows);
|
|
}
|
|
destroy_table_options(table->options);
|
|
destroy_string_buffer(table->conv_buffer);
|
|
F_FREE(table);
|
|
}
|
|
|
|
void ft_ln(FTABLE *FORT_RESTRICT table)
|
|
{
|
|
assert(table);
|
|
table->cur_col = 0;
|
|
table->cur_row++;
|
|
}
|
|
|
|
|
|
static int ft_row_printf_impl(FTABLE *FORT_RESTRICT table, size_t row, const char* FORT_RESTRICT fmt, va_list *va)
|
|
{
|
|
if (table == NULL)
|
|
return -1;
|
|
|
|
fort_row_t *new_row = create_row_from_fmt_string(fmt, va);
|
|
|
|
if (new_row == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
fort_row_t **cur_row_p = NULL;
|
|
size_t sz = vector_size(table->rows);
|
|
if (row >= sz) {
|
|
size_t push_n = row - sz + 1;
|
|
for (size_t i = 0; i < push_n; ++i) {
|
|
fort_row_t *new_row = create_row();
|
|
if (new_row == NULL)
|
|
goto clear;
|
|
|
|
if (IS_ERROR(vector_push(table->rows, &new_row))) {
|
|
destroy_row(new_row);
|
|
goto clear;
|
|
}
|
|
}
|
|
}
|
|
/* 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);
|
|
|
|
destroy_row(*cur_row_p);
|
|
*cur_row_p = new_row;
|
|
size_t new_cols = columns_in_row(new_row);
|
|
table->cur_col += new_cols;
|
|
return new_cols;
|
|
|
|
clear:
|
|
destroy_row(new_row);
|
|
return -1;
|
|
}
|
|
|
|
int ft_hdr_printf(FTABLE *FORT_RESTRICT table, const char* FORT_RESTRICT fmt, ...)
|
|
{
|
|
assert(table);
|
|
assert(fmt);
|
|
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
int result = ft_row_printf_impl(table, table->cur_row, fmt, &va);
|
|
va_end(va);
|
|
if (result >= 0 && table->rows) {
|
|
int sz = vector_size(table->rows);
|
|
if (sz != 0) {
|
|
set_row_type(*(fort_row_t**)vector_at(table->rows, sz - 1), Header);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ft_hdr_printf_ln(FTABLE *FORT_RESTRICT table, const char* FORT_RESTRICT fmt, ...)
|
|
{
|
|
assert(table);
|
|
assert(fmt);
|
|
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
int result = ft_row_printf_impl(table, table->cur_row, fmt, &va);
|
|
va_end(va);
|
|
if (result >= 0 && table->rows) {
|
|
int sz = vector_size(table->rows);
|
|
if (sz != 0) {
|
|
set_row_type(*(fort_row_t**)vector_at(table->rows, sz - 1), Header);
|
|
}
|
|
}
|
|
if (result >= 0) {
|
|
ft_ln(table);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int ft_printf(FTABLE *FORT_RESTRICT table, const char* FORT_RESTRICT fmt, ...)
|
|
{
|
|
assert(table);
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
int result = ft_row_printf_impl(table, table->cur_row, fmt, &va);
|
|
va_end(va);
|
|
return result;
|
|
}
|
|
|
|
int ft_printf_ln(FTABLE *FORT_RESTRICT table, const char* FORT_RESTRICT fmt, ...)
|
|
{
|
|
assert(table);
|
|
va_list va;
|
|
va_start(va, fmt);
|
|
int result = ft_row_printf_impl(table, table->cur_row, fmt, &va);
|
|
if (result >= 0) {
|
|
ft_ln(table);
|
|
}
|
|
va_end(va);
|
|
return result;
|
|
}
|
|
|
|
|
|
int ft_write(FTABLE *FORT_RESTRICT table, const char* FORT_RESTRICT cell_content)
|
|
{
|
|
assert(table);
|
|
string_buffer_t *str_buffer = get_cur_str_buffer_and_create_if_not_exists(table);
|
|
if (str_buffer == NULL)
|
|
return F_ERROR;
|
|
|
|
int status = fill_buffer_from_string(str_buffer, cell_content);
|
|
if (IS_SUCCESS(status)) {
|
|
table->cur_col++;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int ft_write_ln(FTABLE *FORT_RESTRICT table, const char* FORT_RESTRICT cell_content)
|
|
{
|
|
assert(table);
|
|
int status = ft_write(table, cell_content);
|
|
if (IS_SUCCESS(status)) {
|
|
ft_ln(table);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
FORT_EXTERN int ft_nwrite(FTABLE *FORT_RESTRICT table, size_t n, const char* FORT_RESTRICT cell_content, ...)
|
|
{
|
|
assert(table);
|
|
int status = ft_write(table, cell_content);
|
|
if (IS_ERROR(status))
|
|
return status;
|
|
|
|
va_list va;
|
|
va_start(va, cell_content);
|
|
--n;
|
|
for (size_t i = 0; i < n; ++i) {
|
|
const char *cell = va_arg(va, const char*);
|
|
status = ft_write(table, cell);
|
|
if (IS_ERROR(status))
|
|
return status;
|
|
}
|
|
va_end(va);
|
|
return status;
|
|
}
|
|
|
|
FORT_EXTERN int ft_nwrite_ln(FTABLE *FORT_RESTRICT table, size_t n, const char* FORT_RESTRICT cell_content, ...)
|
|
{
|
|
assert(table);
|
|
int status = ft_write(table, cell_content);
|
|
if (IS_ERROR(status))
|
|
return status;
|
|
|
|
va_list va;
|
|
va_start(va, cell_content);
|
|
--n;
|
|
for (size_t i = 0; i < n; ++i) {
|
|
const char *cell = va_arg(va, const char*);
|
|
status = ft_write(table, cell);
|
|
if (IS_ERROR(status)) {
|
|
va_end(va);
|
|
return status;
|
|
}
|
|
}
|
|
va_end(va);
|
|
|
|
ft_ln(table);
|
|
return status;
|
|
}
|
|
|
|
|
|
FORT_EXTERN int ft_write_status(FTABLE *FORT_RESTRICT table, int status, const char* FORT_RESTRICT cell_content)
|
|
{
|
|
assert(table);
|
|
if (IS_ERROR(status))
|
|
return status;
|
|
|
|
return ft_write(table, cell_content);
|
|
}
|
|
|
|
|
|
|
|
FORT_EXTERN int ft_row_write(FTABLE *FORT_RESTRICT table, size_t cols, const char* FORT_RESTRICT cells[])
|
|
{
|
|
assert(table);
|
|
for (size_t i = 0; i < cols; ++i) {
|
|
int status = ft_write(table, cells[i]);
|
|
if (IS_ERROR(status)) {
|
|
//todo: maybe current pos in case of error should be equal to the one before function call?
|
|
return status;
|
|
}
|
|
}
|
|
return F_SUCCESS;
|
|
}
|
|
|
|
FORT_EXTERN int ft_row_write_ln(FTABLE *FORT_RESTRICT table, size_t cols, const char* FORT_RESTRICT cells[])
|
|
{
|
|
assert(table);
|
|
int status = ft_row_write(table, cols, cells);
|
|
if (IS_SUCCESS(status)) {
|
|
ft_ln(table);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
//FORT_EXTERN int ft_table_write(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char** FORT_RESTRICT table_cells[]);
|
|
//FORT_EXTERN int ft_table_write_ln(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char** FORT_RESTRICT table_cells[]);
|
|
|
|
|
|
//int ft_table_write(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char* FORT_RESTRICT table_cells[rows][cols]);
|
|
//int ft_table_write_ln(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char** FORT_RESTRICT table_cells[rows][cols]);
|
|
|
|
int ft_s_table_write(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char* FORT_RESTRICT table_cells[rows][cols])
|
|
{
|
|
assert(table);
|
|
for (size_t i = 0; i < rows; ++i) {
|
|
int status = ft_row_write(table, cols, table_cells[i]);
|
|
if (IS_ERROR(status)) {
|
|
//todo: maybe current pos in case of error should be equal to the one before function call?
|
|
return status;
|
|
}
|
|
if (i != rows - 1)
|
|
ft_ln(table);
|
|
}
|
|
return F_SUCCESS;
|
|
}
|
|
|
|
int ft_s_table_write_ln(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char* FORT_RESTRICT table_cells[rows][cols])
|
|
{
|
|
assert(table);
|
|
int status = ft_s_table_write(table, rows, cols, table_cells);
|
|
if (IS_SUCCESS(status)) {
|
|
ft_ln(table);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
int ft_table_write(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char* * FORT_RESTRICT table_cells[rows])
|
|
{
|
|
assert(table);
|
|
for (size_t i = 0; i < rows; ++i) {
|
|
int status = ft_row_write(table, cols, table_cells[i]);
|
|
if (IS_ERROR(status)) {
|
|
//todo: maybe current pos in case of error should be equal to the one before function call?
|
|
return status;
|
|
}
|
|
if (i != rows - 1)
|
|
ft_ln(table);
|
|
}
|
|
return F_SUCCESS;
|
|
}
|
|
|
|
int ft_table_write_ln(FTABLE *FORT_RESTRICT table, size_t rows, size_t cols, const char* * FORT_RESTRICT table_cells[rows])
|
|
{
|
|
assert(table);
|
|
int status = ft_table_write(table, rows, cols, table_cells);
|
|
if (IS_SUCCESS(status)) {
|
|
ft_ln(table);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int ft_set_default_options(const fort_table_options_t *options)
|
|
{
|
|
memcpy(&g_table_options, options, sizeof(fort_table_options_t));
|
|
return 0;
|
|
}
|
|
|
|
int ft_get_default_options(fort_table_options_t *options)
|
|
{
|
|
memcpy(options, &g_table_options, sizeof(fort_table_options_t));
|
|
return 0;
|
|
}
|
|
|
|
int ft_set_table_options(FTABLE * FORT_RESTRICT table, const fort_table_options_t * FORT_RESTRICT options)
|
|
{
|
|
assert(table);
|
|
if (options == NULL) {
|
|
destroy_table_options(table->options);
|
|
table->options = NULL;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
fort_table_options_t *new_options = copy_table_options(options);
|
|
if (new_options == NULL) {
|
|
return -1;
|
|
}
|
|
destroy_table_options(table->options);
|
|
table->options = new_options;
|
|
return 0;
|
|
|
|
|
|
// fort_table_options_t *new_options = F_CALLOC(sizeof(fort_table_options_t), 1);
|
|
// if (new_options == NULL) {
|
|
// return -1;
|
|
// }
|
|
// memcpy(new_options, options, sizeof(fort_table_options_t));
|
|
// F_FREE(table->options);
|
|
// table->options = new_options;
|
|
// return 0;
|
|
}
|
|
|
|
|
|
|
|
int ft_set_column_min_width(FTABLE *table, size_t column, size_t width)
|
|
{
|
|
if (table->options == NULL) {
|
|
table->options = create_table_options();
|
|
if (table->options == NULL)
|
|
return F_MEMORY_ERROR;
|
|
}
|
|
|
|
int status = fort_options_set_column_min_width(table->options, column, width);
|
|
return status;
|
|
}
|
|
|
|
int ft_set_column_alignment(FTABLE * FORT_RESTRICT table, size_t column, enum TextAlignment align)
|
|
{
|
|
if (table->options == NULL) {
|
|
table->options = create_table_options();
|
|
if (table->options == NULL)
|
|
return F_MEMORY_ERROR;
|
|
}
|
|
|
|
int status = fort_options_set_column_alignment(table->options, column, align);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* TABLE
|
|
* ***************************************************************************/
|
|
|
|
|
|
|
|
const char* ft_to_string(const FTABLE *FORT_RESTRICT table)
|
|
{
|
|
#define CHECK_RESULT_AND_MOVE_DEV(statement) \
|
|
k = statement; \
|
|
if (k < 0) {\
|
|
goto clear; \
|
|
} \
|
|
dev += k;
|
|
|
|
assert(table);
|
|
|
|
/* Determing size of table string representation */
|
|
size_t height = 0;
|
|
size_t width = 0;
|
|
int status = table_geometry(table, &height, &width);
|
|
if (IS_ERROR(status)) {
|
|
return NULL;
|
|
}
|
|
size_t sz = height * width + 1;
|
|
|
|
/* Allocate string buffer for string representation */
|
|
if (table->conv_buffer == NULL) {
|
|
((FTABLE *)table)->conv_buffer = create_string_buffer(sz);
|
|
if (table->conv_buffer == NULL)
|
|
return NULL;
|
|
}
|
|
while (table->conv_buffer->str_sz < sz) {
|
|
if (IS_ERROR(realloc_string_buffer_without_copy(table->conv_buffer))) {
|
|
return NULL;
|
|
}
|
|
}
|
|
char *buffer = table->conv_buffer->str;
|
|
|
|
|
|
size_t cols = 0;
|
|
size_t rows = 0;
|
|
size_t *col_width_arr = NULL;
|
|
size_t *row_height_arr = NULL;
|
|
status = table_rows_and_cols_geometry(table, &col_width_arr, &cols, &row_height_arr, &rows);
|
|
|
|
if (rows == 0)
|
|
return F_STRDUP("");
|
|
|
|
if (IS_ERROR(status))
|
|
return NULL;
|
|
|
|
int dev = 0;
|
|
int k = 0;
|
|
context_t *context = (table->options ? table->options : &g_table_options);
|
|
fort_row_t *prev_row = NULL;
|
|
fort_row_t *cur_row = NULL;
|
|
for (size_t i = 0; i < rows; ++i) {
|
|
cur_row = *(fort_row_t**)vector_at(table->rows, i);
|
|
enum HorSeparatorPos separatorPos = (i == 0) ? TopSeparator : InsideSeparator;
|
|
CHECK_RESULT_AND_MOVE_DEV(print_row_separator(buffer + dev, sz - dev, col_width_arr, cols, prev_row, cur_row, separatorPos, context));
|
|
CHECK_RESULT_AND_MOVE_DEV(snprintf_row(cur_row, buffer + dev, sz - dev, col_width_arr, cols, row_height_arr[i], context));
|
|
prev_row = cur_row;
|
|
}
|
|
cur_row = NULL;
|
|
CHECK_RESULT_AND_MOVE_DEV(print_row_separator(buffer + dev, sz - dev, col_width_arr, cols, prev_row, cur_row, BottomSeparator, context));
|
|
|
|
|
|
F_FREE(col_width_arr);
|
|
F_FREE(row_height_arr);
|
|
return buffer;
|
|
|
|
clear:
|
|
F_FREE(col_width_arr);
|
|
F_FREE(row_height_arr);
|
|
F_FREE(buffer);
|
|
return NULL;
|
|
|
|
#undef CHECK_RESULT_AND_MOVE_DEV
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* TMP
|
|
*/
|
|
static int dummy_function() __attribute__ ((unused));
|
|
|
|
static int dummy_function()
|
|
{
|
|
if (0) {
|
|
vector_t *v = create_vector(1, DEFAULT_VECTOR_CAPACITY);
|
|
vector_clear(v);
|
|
vector_erase(v, 0);
|
|
vector_index_of(v, NULL);
|
|
vector_capacity(v);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|