add csv file

This commit is contained in:
lixianjing 2021-01-21 16:13:51 +08:00
parent 1cd6e324db
commit 83a49bbeae
9 changed files with 1468 additions and 0 deletions

View File

@ -2,6 +2,7 @@
2021/01/21
* 将销毁自定义属性代码放到on\_destroy回调函数之后。
* 增加csv file请参考[csv file](../src/csv/README.md)。
* text\_selector 支持同时修改选项的步长和格式 (感谢智明提供补丁)
2021/01/20

View File

@ -15,6 +15,7 @@ BASE_SOURCES=Glob('layouters/*.c') + \
Glob('ui_loader/*.c') + \
Glob('fscript_ext/*.c') + \
Glob('xml/*.c') + \
Glob('csv/*.c') + \
Glob('svg/*.c') + \
Glob('charset/*.c') + \
Glob('clip_board/*.c') + \

87
src/csv/README.md Normal file
View File

@ -0,0 +1,87 @@
# csv 文件
> 本目录的文件从 [awtk-csv-file](https://github.com/zlgopen/awtk-csv-file) 拷贝而来。如果需要修改,请先修改 [awtk-csv-file](https://github.com/zlgopen/awtk-csv-file),再拷贝过来。
csv 为可选组件,需要自己包含头文件
```c
#include "csv/csv_file.h"
#include "csv/csv_file_object.h"
```
## 示例:
* 普通方式访问
```c
const char* str = "11,12,13\n21,22,23";
csv_file_t* csv = csv_file_create_with_buff(str, strlen(str), ',');
ASSERT_EQ(csv_file_get_rows(csv), 2);
ASSERT_EQ(csv_file_get_cols(csv), 3);
ASSERT_STREQ(csv_file_get(csv, 0, 0), "11");
ASSERT_STREQ(csv_file_get(csv, 0, 1), "12");
ASSERT_STREQ(csv_file_get(csv, 0, 2), "13");
ASSERT_STREQ(csv_file_get(csv, 1, 0), "21");
ASSERT_STREQ(csv_file_get(csv, 1, 1), "22");
ASSERT_STREQ(csv_file_get(csv, 1, 2), "23");
csv_file_destroy(csv);
```
```c
TEST(csv_file, title) {
const char* str = "aa,bb,cc\n11,12,13\n21,22,23";
csv_file_t* csv = csv_file_create_with_buff(str, strlen(str), ',');
ASSERT_EQ(csv_file_get_rows(csv), 3);
ASSERT_EQ(csv_file_get_cols(csv), 3);
ASSERT_STREQ(csv_file_get(csv, 0, 0), "aa");
ASSERT_STREQ(csv_file_get(csv, 0, 1), "bb");
ASSERT_STREQ(csv_file_get(csv, 0, 2), "cc");
ASSERT_STREQ(csv_file_get(csv, 1, 0), "11");
ASSERT_STREQ(csv_file_get(csv, 1, 1), "12");
ASSERT_STREQ(csv_file_get(csv, 1, 2), "13");
ASSERT_STREQ(csv_file_get(csv, 2, 0), "21");
ASSERT_STREQ(csv_file_get(csv, 2, 1), "22");
ASSERT_STREQ(csv_file_get(csv, 2, 2), "23");
ASSERT_STREQ(csv_file_get_by_name(csv, 1, "aa"), "11");
ASSERT_STREQ(csv_file_get_by_name(csv, 1, "bb"), "12");
ASSERT_STREQ(csv_file_get_by_name(csv, 1, "cc"), "13");
ASSERT_STREQ(csv_file_get_by_name(csv, 2, "aa"), "21");
ASSERT_STREQ(csv_file_get_by_name(csv, 2, "bb"), "22");
ASSERT_STREQ(csv_file_get_by_name(csv, 2, "cc"), "23");
ASSERT_EQ(csv_file_get_col_of_name(csv, "aa"), 0);
ASSERT_EQ(csv_file_get_col_of_name(csv, "bb"), 1);
ASSERT_EQ(csv_file_get_col_of_name(csv, "cc"), 2);
csv_file_destroy(csv);
}
```
* 以对象的方式访问:
```c
const char* str = "aa,bb,cc\n11,12,13\n21,22,23";
csv_file_t* csv = csv_file_create_with_buff(str, strlen(str), ',');
object_t* obj = csv_file_object_create(csv);
ASSERT_EQ(object_get_prop_int(obj, "#size", 0), 3);
ASSERT_STREQ(object_get_prop_str(obj, "[0].[0]"), "aa");
ASSERT_STREQ(object_get_prop_str(obj, "[0].[1]"), "bb");
ASSERT_STREQ(object_get_prop_str(obj, "[0].[2]"), "cc");
ASSERT_STREQ(object_get_prop_str(obj, "[1].[0]"), "11");
ASSERT_STREQ(object_get_prop_str(obj, "[1].[1]"), "12");
ASSERT_STREQ(object_get_prop_str(obj, "[1].[2]"), "13");
OBJECT_UNREF(obj);
```
## 具体用法请参考:
* https://github.com/zlgopen/awtk-csv-file/blob/master/tests/csv_file_test.cc
* https://github.com/zlgopen/awtk-csv-file/blob/master/tests/csv_file_object_test.cc

703
src/csv/csv_file.c Normal file
View File

@ -0,0 +1,703 @@
/**
* File: csv_file.c
* Author: AWTK Develop Team
* Brief: csv file
*
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2020-06-08 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "csv_file.h"
#include "streams/mem/istream_mem.h"
#include "streams/file/istream_file.h"
static const uint8_t s_utf8_bom[3] = {0xEF, 0xBB, 0xBF};
ret_t csv_file_reset(csv_file_t* csv);
ret_t csv_file_clear(csv_file_t* csv);
static ret_t csv_rows_extend_rows(csv_rows_t* rows, uint32_t delta);
static csv_file_t* csv_file_load_input(csv_file_t* csv, tk_istream_t* input);
const char* csv_row_get(csv_row_t* row, uint32_t col) {
uint32_t i = 0;
uint32_t c = 0;
return_value_if_fail(row != NULL, NULL);
while (i < row->size) {
const char* p = row->buff + i;
if (c == col) {
return p;
}
c++;
i += strlen(p) + 1;
}
return NULL;
}
int32_t csv_row_get_col(csv_row_t* row, const char* value) {
uint32_t i = 0;
uint32_t col = 0;
return_value_if_fail(row != NULL && value != NULL, -1);
while (i < row->size) {
const char* p = row->buff + i;
if (tk_str_eq(p, value)) {
return col;
}
col++;
i += strlen(p) + 1;
}
return -1;
}
static ret_t csv_col_to_str(str_t* str, const char* data, char sep) {
const char* p = data;
bool_t need_escape = strchr(p, sep) != NULL || strchr(p, '\"') != NULL;
if (need_escape) {
str_append_char(str, '\"');
while (*p) {
if (*p == '\\' || *p == '\"') {
str_append_char(str, '\\');
}
str_append_char(str, *p);
p++;
}
str_append_char(str, '\"');
} else {
str_append(str, data);
}
return RET_OK;
}
ret_t csv_row_to_str(csv_row_t* row, str_t* str, char sep) {
uint32_t i = 0;
return_value_if_fail(row != NULL && str != NULL, RET_BAD_PARAMS);
str_set(str, "");
while ((i + 1) < row->size) {
const char* p = row->buff + i;
csv_col_to_str(str, p, sep);
str_append_char(str, sep);
i += strlen(p) + 1;
}
str_append(str, "\r\n");
return RET_OK;
}
uint32_t csv_row_count_cols(csv_row_t* row) {
uint32_t i = 0;
uint32_t cols = 0;
return_value_if_fail(row != NULL, 0);
while (i < row->size) {
const char* p = row->buff + i;
cols++;
i += strlen(p) + 1;
}
return cols;
}
ret_t csv_row_set(csv_row_t* row, uint32_t col, const char* value) {
char* p = NULL;
uint32_t old_len = 0;
uint32_t new_len = 0;
return_value_if_fail(row != NULL && value != NULL, RET_BAD_PARAMS);
return_value_if_fail(row->buff != NULL, RET_BAD_PARAMS);
p = (char*)csv_row_get(row, col);
return_value_if_fail(p != NULL, RET_BAD_PARAMS);
old_len = strlen(p);
new_len = strlen(value);
if (old_len == new_len) {
strcpy(p, value);
return RET_OK;
} else if (old_len > new_len) {
uint32_t len = row->size - (p + old_len - row->buff);
strcpy(p, value);
memmove(p + new_len, p + old_len, len);
row->size = row->size + new_len - old_len;
return RET_OK;
} else {
uint32_t d = 0;
uint32_t s = 0;
uint32_t size = 0;
uint32_t len = row->size + new_len - old_len;
char* buff = TKMEM_ALLOC(len);
return_value_if_fail(buff != NULL, RET_OOM);
memset(buff, 0x00, len);
size = p - row->buff;
memcpy(buff, row->buff, size);
d = size;
s = size;
memcpy(buff + d, value, new_len + 1);
d += new_len + 1;
s += old_len + 1;
size = row->size - s;
memcpy(buff + d, row->buff + s, size);
csv_row_reset(row);
row->buff = buff;
row->size = len;
row->should_free_buff = TRUE;
return RET_OK;
}
}
ret_t csv_row_init(csv_row_t* row, char* buff, uint32_t size, bool_t should_free_buff) {
return_value_if_fail(row != NULL && buff != NULL, RET_BAD_PARAMS);
row->buff = buff;
row->size = size;
row->should_free_buff = should_free_buff;
return RET_OK;
}
static ret_t csv_row_parse(csv_row_t* row, char sep) {
char* s = row->buff;
char* d = row->buff;
bool_t escape = FALSE;
bool_t in_quota = FALSE;
while (*s) {
char c = *s;
s++;
if (in_quota) {
if (escape) {
*d++ = c;
escape = FALSE;
} else {
if (c == '\"') {
in_quota = FALSE;
} else if (c == '\\') {
escape = TRUE;
} else {
*d++ = c;
}
}
} else {
if (c == sep) {
*d++ = '\0';
} else if (c == '\"') {
in_quota = TRUE;
} else {
*d++ = c;
}
}
}
*d = '\0';
row->size = d - row->buff + 1;
return RET_OK;
}
ret_t csv_row_set_data(csv_row_t* row, const char* data, char sep) {
csv_row_reset(row);
row->buff = tk_strdup(data);
return_value_if_fail(row->buff != NULL, RET_OOM);
row->should_free_buff = TRUE;
row->size = strlen(data) + 1;
return csv_row_parse(row, sep);
}
ret_t csv_row_reset(csv_row_t* row) {
return_value_if_fail(row != NULL, RET_BAD_PARAMS);
if (row->should_free_buff) {
TKMEM_FREE(row->buff);
}
memset(row, 0x00, sizeof(*row));
return RET_OK;
}
static ret_t csv_rows_extend_rows(csv_rows_t* rows, uint32_t delta) {
uint32_t capacity = 0;
csv_row_t* first = NULL;
return_value_if_fail(rows != NULL, RET_BAD_PARAMS);
if ((rows->size + delta) < rows->capacity) {
return RET_OK;
}
first = rows->rows;
capacity = rows->capacity * 1.5 + delta;
first = TKMEM_REALLOC(first, capacity * sizeof(csv_row_t));
return_value_if_fail(first != NULL, RET_OOM);
rows->rows = first;
rows->capacity = capacity;
return RET_OK;
}
ret_t csv_rows_remove(csv_rows_t* rows, uint32_t row) {
uint32_t i = 0;
csv_row_t* r = NULL;
return_value_if_fail(rows != NULL && row < rows->size, RET_BAD_PARAMS);
csv_row_reset(rows->rows + row);
r = rows->rows;
for (i = row; i < rows->size; i++) {
r[i] = r[i + 1];
}
rows->size--;
return RET_OK;
}
csv_row_t* csv_rows_append(csv_rows_t* rows) {
csv_row_t* r = NULL;
return_value_if_fail(csv_rows_extend_rows(rows, 1) == RET_OK, NULL);
r = rows->rows + rows->size++;
memset(r, 0x00, sizeof(*r));
return r;
}
csv_row_t* csv_rows_insert(csv_rows_t* rows, uint32_t row) {
uint32_t i = 0;
csv_row_t* r = NULL;
return_value_if_fail(rows != NULL, NULL);
return_value_if_fail(csv_rows_extend_rows(rows, 1) == RET_OK, NULL);
if (row >= rows->size) {
return csv_rows_append(rows);
}
r = rows->rows;
for (i = rows->size; i > row; i--) {
r[i] = r[i - 1];
}
r = r + row;
memset(r, 0x00, sizeof(*r));
rows->size++;
return r;
}
csv_row_t* csv_rows_get(csv_rows_t* rows, uint32_t row) {
return_value_if_fail(rows != NULL && row < rows->size, NULL);
return rows->rows + row;
}
ret_t csv_rows_reset(csv_rows_t* rows) {
return_value_if_fail(rows != NULL && rows->rows != NULL, RET_BAD_PARAMS);
TKMEM_FREE(rows->rows);
memset(rows, 0x00, sizeof(*rows));
return RET_OK;
}
ret_t csv_rows_init(csv_rows_t* rows, uint32_t init_capacity) {
return_value_if_fail(rows != NULL, RET_BAD_PARAMS);
init_capacity = tk_max(10, init_capacity);
rows->size = 0;
rows->capacity = init_capacity;
rows->rows = TKMEM_ZALLOCN(csv_row_t, init_capacity);
return rows->rows != NULL ? RET_OK : RET_OOM;
}
csv_file_t* csv_file_load(csv_file_t* csv) {
tk_istream_t* input = NULL;
return_value_if_fail(csv != NULL && csv->filename != NULL, NULL);
input = tk_istream_file_create(csv->filename);
return_value_if_fail(input != NULL, NULL);
csv_file_load_input(csv, input);
OBJECT_UNREF(input);
return csv;
}
static csv_file_t* csv_file_load_input(csv_file_t* csv, tk_istream_t* input) {
str_t str;
char* p = NULL;
uint32_t index = 0;
csv_row_t* r = NULL;
char sep = csv->sep;
str_init(&str, 2048);
while (tk_istream_read_line_str(input, &str) == RET_OK) {
p = str.str;
if (csv->rows.size == 0) {
if (memcmp(p, s_utf8_bom, sizeof(s_utf8_bom)) == 0) {
p += sizeof(s_utf8_bom);
log_debug("skip utf-8 bom\n");
}
}
/*skip malformed data*/
while ((p - str.str) < str.size) {
if (*p == '\0') {
p++;
} else {
break;
}
}
if (*p == '\0') {
log_debug("skip empty line\n");
continue;
}
r = csv_rows_append(&(csv->rows));
return_value_if_fail(r != NULL, NULL);
return_value_if_fail(csv_row_init(r, tk_strdup(p), str.size + 1, TRUE) == RET_OK, NULL);
csv_row_parse(r, sep);
if (csv->filter != NULL) {
ret_t ret = csv->filter(csv->filter_ctx, csv, index, r);
if (ret == RET_STOP) {
break;
} else if (ret == RET_FAIL) {
csv_row_reset(r);
csv->rows.size--;
}
}
index++;
}
r = csv_file_get_row(csv, 0);
if (r != NULL) {
uint32_t cols1 = 0;
uint32_t cols0 = csv_row_count_cols(r);
if (cols0 == 1) {
r = csv_file_get_row(csv, 1);
cols1 = csv_row_count_cols(r);
if (cols1 > cols0) {
csv->has_title = TRUE;
}
csv->cols = tk_max(cols0, cols1);
} else {
csv->cols = csv_row_count_cols(r);
}
}
return csv;
}
csv_file_t* csv_file_create_empty(char sep, csv_file_filter_t filter, void* ctx) {
csv_file_t* csv = TKMEM_ZALLOC(csv_file_t);
return_value_if_fail(csv != NULL, NULL);
if (csv_rows_init(&(csv->rows), 100) == RET_OK) {
csv->sep = sep;
csv->filter = filter;
csv->filter_ctx = ctx;
} else {
TKMEM_FREE(csv);
}
return csv;
}
ret_t csv_file_set_filter(csv_file_t* csv, csv_file_filter_t filter, void* ctx) {
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
csv->filter = filter;
csv->filter_ctx = ctx;
return RET_OK;
}
csv_file_t* csv_file_create(const char* filename, char sep) {
csv_file_t* csv = NULL;
return_value_if_fail(filename != NULL, NULL);
csv = csv_file_create_empty(sep, NULL, NULL);
return_value_if_fail(csv != NULL, NULL);
if (csv_file_load_file(csv, filename) != RET_OK) {
csv_file_destroy(csv);
csv = NULL;
}
return csv;
}
ret_t csv_file_load_buff(csv_file_t* csv, const char* buff, uint32_t size) {
ret_t ret = RET_OK;
tk_istream_t* input = NULL;
return_value_if_fail(csv != NULL && buff != NULL && size > 0, RET_BAD_PARAMS);
input = tk_istream_mem_create((uint8_t*)buff, size, 0, FALSE);
return_value_if_fail(input != NULL, RET_BAD_PARAMS);
csv_file_clear(csv);
ret = csv_file_load_input(csv, input) != NULL ? RET_OK : RET_FAIL;
OBJECT_UNREF(input);
return RET_OK;
}
csv_file_t* csv_file_create_with_buff(const char* buff, uint32_t size, char sep) {
csv_file_t* csv = NULL;
csv = csv_file_create_empty(sep, NULL, NULL);
return_value_if_fail(csv != NULL, NULL);
if (csv_file_load_buff(csv, buff, size) != RET_OK) {
csv_file_destroy(csv);
csv = NULL;
}
return csv;
}
const char* csv_file_get(csv_file_t* csv, uint32_t row, uint32_t col) {
csv_row_t* r = csv_file_get_row(csv, row);
return_value_if_fail(r != NULL, NULL);
return_value_if_fail(col < csv_file_get_cols(csv), NULL);
return csv_row_get(r, col);
}
int32_t csv_file_get_col_of_name(csv_file_t* csv, const char* name) {
return csv_row_get_col(csv_file_get_row(csv, 0), name);
}
const char* csv_file_get_by_name(csv_file_t* csv, uint32_t row, const char* name) {
int32_t col = csv_file_get_col_of_name(csv, name);
return_value_if_fail(col >= 0, NULL);
return csv_file_get(csv, row, col);
}
uint32_t csv_file_get_rows(csv_file_t* csv) {
return_value_if_fail(csv != NULL, 0);
return csv->rows.size;
}
uint32_t csv_file_get_checked_rows(csv_file_t* csv) {
uint32_t i = 0;
uint32_t nr = 0;
csv_row_t* r = NULL;
csv_rows_t* rows = NULL;
return_value_if_fail(csv != NULL, 0);
rows = &(csv->rows);
for (i = 0; i < rows->size; i++) {
r = rows->rows + i;
if (r->checked) {
nr++;
}
}
return nr;
}
uint32_t csv_file_get_cols(csv_file_t* csv) {
return_value_if_fail(csv != NULL, 0);
return csv->cols;
}
ret_t csv_file_set(csv_file_t* csv, uint32_t row, uint32_t col, const char* value) {
csv_row_t* r = csv_file_get_row(csv, row);
return_value_if_fail(r != NULL, RET_BAD_PARAMS);
return_value_if_fail(col < csv_file_get_cols(csv), RET_BAD_PARAMS);
return csv_row_set(r, col, value);
}
ret_t csv_file_set_row_checked(csv_file_t* csv, uint32_t row, bool_t checked) {
csv_row_t* r = csv_file_get_row(csv, row);
return_value_if_fail(r != NULL, RET_BAD_PARAMS);
r->checked = checked;
return RET_OK;
}
bool_t csv_file_is_row_checked(csv_file_t* csv, uint32_t row) {
csv_row_t* r = csv_file_get_row(csv, row);
return_value_if_fail(r != NULL, FALSE);
return r->checked;
}
ret_t csv_file_remove_checked_rows(csv_file_t* csv) {
uint32_t i = 0;
uint32_t d = 0;
csv_row_t* r = NULL;
csv_rows_t* rows = NULL;
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
rows = &(csv->rows);
for (i = 0; i < rows->size; i++) {
r = rows->rows + i;
if (r->checked) {
csv_row_reset(r);
} else {
rows->rows[d++] = rows->rows[i];
}
}
rows->size = d;
return RET_OK;
}
csv_row_t* csv_file_get_row(csv_file_t* csv, uint32_t row) {
return_value_if_fail(csv != NULL, NULL);
return csv_rows_get(&(csv->rows), row);
}
ret_t csv_file_insert_row(csv_file_t* csv, uint32_t row, const char* data) {
csv_row_t* r = NULL;
return_value_if_fail(csv != NULL && data != NULL, RET_BAD_PARAMS);
r = csv_rows_insert(&(csv->rows), row);
return_value_if_fail(r != NULL, RET_OOM);
return csv_row_set_data(r, data, csv->sep);
}
ret_t csv_file_append_row(csv_file_t* csv, const char* data) {
csv_row_t* r = NULL;
return_value_if_fail(csv != NULL && data != NULL, RET_BAD_PARAMS);
r = csv_rows_append(&(csv->rows));
return_value_if_fail(r != NULL, RET_OOM);
return csv_row_set_data(r, data, csv->sep);
}
ret_t csv_file_remove_row(csv_file_t* csv, uint32_t row) {
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
return csv_rows_remove(&(csv->rows), row);
}
ret_t csv_file_save(csv_file_t* csv, const char* filename) {
str_t str;
uint32_t i = 0;
csv_row_t* r = NULL;
fs_file_t* f = NULL;
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
filename = filename != NULL ? filename : csv->filename;
return_value_if_fail(filename != NULL, RET_BAD_PARAMS);
return_value_if_fail(str_init(&str, 512) != NULL, RET_OOM);
f = fs_open_file(os_fs(), filename, "wb+");
if (f != NULL) {
ENSURE(fs_file_write(f, s_utf8_bom, sizeof(s_utf8_bom)) == sizeof(s_utf8_bom));
for (i = 0; i < csv->rows.size; i++) {
r = csv->rows.rows + i;
csv_row_to_str(r, &str, csv->sep);
ENSURE(fs_file_write(f, str.str, str.size) == str.size);
}
fs_file_close(f);
if (csv->filename != filename) {
TKMEM_FREE(csv->filename);
csv->filename = tk_strdup(filename);
}
}
str_reset(&str);
return RET_NOT_IMPL;
}
const char* csv_file_get_title(csv_file_t* csv) {
csv_row_t* r = NULL;
return_value_if_fail(csv != NULL && csv->has_title, NULL);
r = csv_file_get_row(csv, 0);
return_value_if_fail(r != NULL, NULL);
return r->buff;
}
ret_t csv_file_clear(csv_file_t* csv) {
uint32_t i = 0;
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
for (i = 0; i < csv->rows.size; i++) {
csv_row_t* r = csv->rows.rows + i;
csv_row_reset(r);
}
csv->rows.size = 0;
return RET_OK;
}
ret_t csv_file_reset(csv_file_t* csv) {
return_value_if_fail(csv != NULL, RET_BAD_PARAMS);
csv_file_clear(csv);
TKMEM_FREE(csv->filename);
TKMEM_FREE(csv->rows.rows);
memset(csv, 0x00, sizeof(*csv));
return RET_OK;
}
ret_t csv_file_destroy(csv_file_t* csv) {
if (csv_file_reset(csv) == RET_OK) {
TKMEM_FREE(csv);
}
return RET_OK;
}
ret_t csv_file_reload(csv_file_t* csv) {
return_value_if_fail(csv != NULL && csv->filename != NULL, RET_BAD_PARAMS);
csv_file_clear(csv);
return (csv_file_load(csv) != NULL) ? RET_OK : RET_FAIL;
}
ret_t csv_file_load_file(csv_file_t* csv, const char* filename) {
return_value_if_fail(csv != NULL && filename != NULL, RET_BAD_PARAMS);
csv_file_clear(csv);
csv->filename = tk_str_copy(csv->filename, filename);
return (csv_file_load(csv) != NULL) ? RET_OK : RET_FAIL;
}

372
src/csv/csv_file.h Normal file
View File

@ -0,0 +1,372 @@
/**
* File: csv_file.h
* Author: AWTK Develop Team
* Brief: csv file
*
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2020-06-08 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_CSV_FILE_H
#define TK_CSV_FILE_H
#include "tkc/istream.h"
BEGIN_C_DECLS
typedef struct _csv_row_t {
char* buff;
uint32_t size : 30;
uint32_t checked : 1;
uint32_t should_free_buff : 1;
} csv_row_t;
typedef struct _csv_rows_t {
csv_row_t* rows;
uint32_t size;
uint32_t capacity;
} csv_rows_t;
struct _csv_file_t;
typedef struct _csv_file_t csv_file_t;
/**
*
* RET_OK:
* RET_STOP:
* RET_FAIL:
*/
typedef ret_t (*csv_file_filter_t)(void* ctx, csv_file_t* csv, uint32_t index, csv_row_t* row);
/**
* @class csv_file_t
* CSV文件
*/
struct _csv_file_t {
/**
* @property {bool_t} has_title
*
*/
bool_t has_title;
/*private*/
char sep;
uint32_t cols;
char* filename;
csv_rows_t rows;
void* filter_ctx;
csv_file_filter_t filter;
};
/**
* @method csv_file_create_empty
*
* csv对象
*
* @param {char} sep
* @param {csv_file_filter_t} filter
* @param {void*} ctx
*
* @return {csv_file_t*} csv对象
*/
csv_file_t* csv_file_create_empty(char sep, csv_file_filter_t filter, void* ctx);
/**
* @method csv_file_set_filter
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {csv_file_filter_t} filter
* @param {void*} ctx
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_set_filter(csv_file_t* csv, csv_file_filter_t filter, void* ctx);
/**
* @method csv_file_create
*
* csv对象
*
* @param {const char*} filename
* @param {char} sep
*
* @return {csv_file_t*} csv对象
*/
csv_file_t* csv_file_create(const char* filename, char sep);
/**
* @method csv_file_create_with_buff
*
* buff创建csv对象
*
* @param {const char*} buff
* @param {uint32_t} size
* @param {char} sep
*
* @return {csv_file_t} csv对象
*/
csv_file_t* csv_file_create_with_buff(const char* buff, uint32_t size, char sep);
/**
* @method csv_file_load_file
*
* csv
*
* @param {csv_file_t*} csv csv对象
* @param {const char*} filename
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_load_file(csv_file_t* csv, const char* filename);
/**
* @method csv_file_load_buff
*
* csv
*
* @param {csv_file_t*} csv csv对象
* @param {const char*} buff
* @param {uint32_t} size
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_load_buff(csv_file_t* csv, const char* buff, uint32_t size);
/**
* @method csv_file_reload
*
*
*
* @param {csv_file_t*} csv csv对象
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_reload(csv_file_t* csv);
/**
* @method csv_file_get
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {uint32_t} row
* @param {uint32_t} col
*
* @return {const char*}
*/
const char* csv_file_get(csv_file_t* csv, uint32_t row, uint32_t col);
/**
* @method csv_file_set
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {uint32_t} row
* @param {uint32_t} col
* @param {const char*} value
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_set(csv_file_t* csv, uint32_t row, uint32_t col, const char* value);
/**
* @method csv_file_set_row_checked
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {uint32_t} row
* @param {bool_t} checked
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_set_row_checked(csv_file_t* csv, uint32_t row, bool_t checked);
/**
* @method csv_file_is_row_checked
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {uint32_t} row
*
* @return {bool_t} TRUE表示勾选
*/
bool_t csv_file_is_row_checked(csv_file_t* csv, uint32_t row);
/**
* @method csv_file_remove_checked_rows
*
*
*
* @param {csv_file_t*} csv csv对象
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_remove_checked_rows(csv_file_t* csv);
/**
* @method csv_file_get_title
*
* (NULL)
*
* @param {csv_file_t*} csv csv对象
*
* @return {const char*}
*/
const char* csv_file_get_title(csv_file_t* csv);
/**
* @method csv_file_get_rows
*
* ()
*
* @param {csv_file_t*} csv csv对象
*
* @return {uint32_t}
*/
uint32_t csv_file_get_rows(csv_file_t* csv);
/**
* @method csv_file_get_checked_rows
*
* checked行数()
*
* @param {csv_file_t*} csv csv对象
*
* @return {uint32_t} checked行数
*/
uint32_t csv_file_get_checked_rows(csv_file_t* csv);
/**
* @method csv_file_get_cols
*
*
*
* @param {csv_file_t*} csv csv对象
*
* @return {uint32_t}
*/
uint32_t csv_file_get_cols(csv_file_t* csv);
/**
* @method csv_file_remove_row
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {uint32_t} row
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_remove_row(csv_file_t* csv, uint32_t row);
/**
* @method csv_file_append_row
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {const char*} data
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_append_row(csv_file_t* csv, const char* data);
/**
* @method csv_file_insert_row
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {uint32_t} row
* @param {const char*} data
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_insert_row(csv_file_t* csv, uint32_t row, const char* data);
/**
* @method csv_file_save
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {const char*} filename
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_save(csv_file_t* csv, const char* filename);
/**
* @method csv_file_clear
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {const char*} filename
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_clear(csv_file_t* csv);
/**
* @method csv_file_load_file
*
*
*
* @param {csv_file_t*} csv csv对象
* @param {const char*} filename
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_load_file(csv_file_t* csv, const char* filename);
/**
* @method csv_file_destroy
*
* csv对象
*
* @param {csv_file_t*} csv csv对象
*
* @return {ret_t} RET_OK表示成功
*/
ret_t csv_file_destroy(csv_file_t* csv);
/*public for test*/
const char* csv_file_get_by_name(csv_file_t* csv, uint32_t row, const char* name);
int32_t csv_file_get_col_of_name(csv_file_t* csv, const char* name);
csv_row_t* csv_file_get_row(csv_file_t* csv, uint32_t row);
uint32_t csv_row_count_cols(csv_row_t* row);
const char* csv_row_get(csv_row_t* row, uint32_t col);
ret_t csv_row_set(csv_row_t* row, uint32_t col, const char* value);
ret_t csv_row_init(csv_row_t* row, char* buff, uint32_t size, bool_t should_free_buff);
ret_t csv_row_reset(csv_row_t* row);
ret_t csv_rows_init(csv_rows_t* rows, uint32_t init_capacity);
ret_t csv_rows_remove(csv_rows_t* rows, uint32_t row);
csv_row_t* csv_rows_append(csv_rows_t* rows);
csv_row_t* csv_rows_insert(csv_rows_t* rows, uint32_t row);
csv_row_t* csv_rows_get(csv_rows_t* rows, uint32_t row);
ret_t csv_rows_reset(csv_rows_t* rows);
ret_t csv_row_set_data(csv_row_t* row, const char* data, char sep);
END_C_DECLS
#endif /*TK_CSV_FILE_H*/

240
src/csv/csv_file_object.c Normal file
View File

@ -0,0 +1,240 @@
/**
* File: csv_file_object.h
* Author: AWTK Develop Team
* Brief: csv file object
*
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2020-07-19 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/str.h"
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "tkc/object.h"
#include "csv_file.h"
typedef struct _csv_file_object_t {
object_t object;
/*private*/
csv_file_t* csv;
str_t str;
} csv_file_object_t;
static csv_file_object_t* csv_file_object_cast(object_t* obj);
#define CSV_FILE_OBJECT(obj) csv_file_object_cast((object_t*)obj)
typedef struct _csv_path_t {
int32_t row;
int32_t col;
const char* col_name;
} csv_path_t;
static ret_t csv_path_parse(csv_path_t* path, csv_file_t* csv, const char* name) {
const char* p = name;
memset(path, 0x00, sizeof(*path));
while (*p && *p != '[') p++;
return_value_if_fail(*p == '[', RET_FAIL);
p++;
path->row = tk_atoi(p);
p = strchr(p, '.');
if (p == NULL) {
return RET_OK;
} else {
p++;
}
if (tk_str_eq(p, OBJECT_PROP_CHECKED)) {
path->col_name = p;
return RET_OK;
}
if (*p == '[') {
path->col = tk_atoi(p + 1);
} else {
path->col = csv_file_get_col_of_name(csv, p);
if (path->col < 0) {
path->col = tk_atoi(p);
}
}
return_value_if_fail((path->col >= 0) && (path->col < csv_file_get_cols(csv)), RET_BAD_PARAMS);
return_value_if_fail((path->row >= 0) && (path->row < csv_file_get_rows(csv)), RET_BAD_PARAMS);
return RET_OK;
}
static ret_t csv_file_object_remove_prop(object_t* obj, const char* name) {
csv_path_t p;
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL);
return csv_file_remove_row(o->csv, p.row);
}
static ret_t csv_file_object_set_prop(object_t* obj, const char* name, const value_t* v) {
csv_path_t p;
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL);
if (p.col_name != NULL && tk_str_ieq(p.col_name, OBJECT_PROP_CHECKED)) {
return csv_file_set_row_checked(o->csv, p.row, value_bool(v));
}
str_from_value(&(o->str), v);
return csv_file_set(o->csv, p.row, p.col, o->str.str);
}
static ret_t csv_file_object_get_prop(object_t* obj, const char* name, value_t* v) {
csv_path_t p;
const char* str = NULL;
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
if (tk_str_ieq(name, OBJECT_PROP_SIZE)) {
value_set_int(v, csv_file_get_rows(o->csv));
return RET_OK;
}
return_value_if_fail(csv_path_parse(&p, o->csv, name) == RET_OK, RET_FAIL);
if (p.col_name != NULL && tk_str_ieq(p.col_name, OBJECT_PROP_CHECKED)) {
return_value_if_fail(p.row < csv_file_get_rows(o->csv), RET_FAIL);
value_set_bool(v, csv_file_is_row_checked(o->csv, p.row));
return RET_OK;
}
value_set_str(v, "");
str = csv_file_get(o->csv, p.row, p.col);
return_value_if_fail(str != NULL, RET_FAIL);
value_set_str(v, str);
return RET_OK;
}
static bool_t csv_file_object_can_exec(object_t* obj, const char* name, const char* args) {
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
if (tk_str_ieq(name, OBJECT_CMD_SAVE)) {
return TRUE;
} else if (tk_str_ieq(name, OBJECT_CMD_RELOAD)) {
return TRUE;
} else if (tk_str_ieq(name, OBJECT_CMD_CLEAR)) {
return TRUE;
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE)) {
return TRUE;
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE_CHECKED)) {
return csv_file_get_checked_rows(o->csv) > 0;
} else if (tk_str_ieq(name, OBJECT_CMD_ADD)) {
return TRUE;
}
return FALSE;
}
static ret_t csv_file_object_exec(object_t* obj, const char* name, const char* args) {
ret_t ret = RET_NOT_IMPL;
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
if (tk_str_ieq(name, OBJECT_CMD_SAVE)) {
ret = csv_file_save(o->csv, NULL);
} else if (tk_str_ieq(name, OBJECT_CMD_RELOAD)) {
csv_file_reload(o->csv);
ret = RET_ITEMS_CHANGED;
} else if (tk_str_ieq(name, OBJECT_CMD_CLEAR)) {
csv_file_clear(o->csv);
ret = RET_ITEMS_CHANGED;
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE)) {
const char* index = strrchr(args, '[');
if (index != NULL) {
index++;
} else {
index = args;
}
return_value_if_fail(index != NULL, RET_FAIL);
ret = csv_file_remove_row(o->csv, tk_atoi(index)) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL;
} else if (tk_str_ieq(name, OBJECT_CMD_REMOVE_CHECKED)) {
ret = csv_file_remove_checked_rows(o->csv) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL;
} else if (tk_str_ieq(name, OBJECT_CMD_ADD)) {
return_value_if_fail(args != NULL, RET_FAIL);
ret = csv_file_append_row(o->csv, args) == RET_OK ? RET_ITEMS_CHANGED : RET_FAIL;
} else {
return RET_NOT_IMPL;
}
if (ret == RET_ITEMS_CHANGED) {
emitter_dispatch_simple_event(EMITTER(obj), EVT_PROPS_CHANGED);
emitter_dispatch_simple_event(EMITTER(obj), EVT_ITEMS_CHANGED);
}
return RET_OK;
}
static ret_t csv_file_object_destroy(object_t* obj) {
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, RET_BAD_PARAMS);
csv_file_destroy(o->csv);
o->csv = NULL;
str_reset(&(o->str));
return RET_OK;
}
static const object_vtable_t s_csv_file_object_vtable = {.type = "csv_file_object",
.desc = "csv_file_object",
.size = sizeof(csv_file_object_t),
.is_collection = TRUE,
.exec = csv_file_object_exec,
.can_exec = csv_file_object_can_exec,
.remove_prop = csv_file_object_remove_prop,
.get_prop = csv_file_object_get_prop,
.set_prop = csv_file_object_set_prop,
.on_destroy = csv_file_object_destroy};
static csv_file_object_t* csv_file_object_cast(object_t* obj) {
return_value_if_fail(obj != NULL && obj->vt == &s_csv_file_object_vtable, NULL);
return (csv_file_object_t*)obj;
}
object_t* csv_file_object_create(csv_file_t* csv) {
object_t* obj = NULL;
csv_file_object_t* o = NULL;
return_value_if_fail(csv != NULL, NULL);
obj = object_create(&s_csv_file_object_vtable);
o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, NULL);
o->csv = csv;
str_init(&(o->str), 0);
return obj;
}
csv_file_t* csv_file_object_get_csv(object_t* obj) {
csv_file_object_t* o = CSV_FILE_OBJECT(obj);
return_value_if_fail(o != NULL, NULL);
return o->csv;
}

61
src/csv/csv_file_object.h Normal file
View File

@ -0,0 +1,61 @@
/**
* File: object_csv_file.h
* Author: AWTK Develop Team
* Brief: csv file object
*
* Copyright (c) 2020 - 2020 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2020-07-19 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_CSV_FILE_OBJECT_H
#define TK_CSV_FILE_OBJECT_H
#include "tkc/object.h"
#include "csv_file.h"
BEGIN_C_DECLS
/**
* @class csv_file_object_t
* @parent object_t
* @annotation["fake"]
* cvs file包装成object对象
*/
/**
* @method csv_file_object_create
*
* csv_file对象包装成object
*
* @param {csv_file_t*} csv csv对象(object释放)
*
* @return {object_t*}
*/
object_t* csv_file_object_create(csv_file_t* csv);
/**
* @method csv_file_object_get_csv
*
* csv对象
*
* @param {object_t*} obj obj对象
*
* @return {csv_file_t*} csv对象
*/
csv_file_t* csv_file_object_get_csv(object_t* obj);
END_C_DECLS
#endif /*TK_CSV_FILE_OBJECT_H*/

2
src/xml/README.md Normal file
View File

@ -0,0 +1,2 @@
# xml 解析器

View File

@ -11,6 +11,7 @@ if(process.argv.length == 3) {
let sourcesPath = [
path.normalize(path.join(__dirname, '../../src/tkc')),
path.normalize(path.join(__dirname, '../../src/ubjson')),
path.normalize(path.join(__dirname, '../../src/csv')),
path.normalize(path.join(__dirname, '../../src/streams')),
path.normalize(path.join(__dirname, '../../src/conf_io'))].join(';');