X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Ftable.c;h=1324839a7c371fd59e1097e09e7724faae437b1e;hb=09395973670d02710885cce266a8b4da943bb423;hp=0c0294e5e9b92188c94a8cf884863fcd9087c90b;hpb=89679c303a41b2a40012c4ad49d6de2b0306b1af;p=libucw.git diff --git a/ucw/table.c b/ucw/table.c index 0c0294e5..1324839a 100644 --- a/ucw/table.c +++ b/ucw/table.c @@ -20,7 +20,7 @@ static void table_update_ll(struct table *tbl); /*** Management of tables ***/ -static struct table *table_make_instance(const struct table_template *tbl_template) +struct table *table_init(const struct table_template *tbl_template) { struct mempool *pool = mp_new(4096); struct table *new_inst = mp_alloc_zero(pool, sizeof(struct table)); @@ -49,39 +49,34 @@ static struct table *table_make_instance(const struct table_template *tbl_templa // initialize column_order if(tbl_template->column_order) { - new_inst->column_order = mp_alloc_zero(new_inst->pool, sizeof(struct table_col_instance) * tbl_template->cols_to_output); - memcpy(new_inst->column_order, tbl_template->column_order, sizeof(struct table_col_instance) * tbl_template->cols_to_output); + int cols_to_output = 0; + for(; ; cols_to_output++) { + if(tbl_template->column_order[cols_to_output].idx == ~0U) break; + } + + new_inst->column_order = mp_alloc_zero(new_inst->pool, sizeof(struct table_col_instance) * cols_to_output); + memcpy(new_inst->column_order, tbl_template->column_order, sizeof(struct table_col_instance) * cols_to_output); for(uint i = 0; i < new_inst->cols_to_output; i++) { new_inst->column_order[i].cell_content = NULL; - int col_idx = new_inst->column_order[i].idx; - new_inst->column_order[i].col_def = new_inst->columns + col_idx; - new_inst->column_order[i].fmt = tbl_template->columns[col_idx].fmt; + int col_def_idx = new_inst->column_order[i].idx; + new_inst->column_order[i].col_def = new_inst->columns + col_def_idx; + new_inst->column_order[i].fmt = tbl_template->columns[col_def_idx].fmt; } - new_inst->cols_to_output = tbl_template->cols_to_output; + new_inst->cols_to_output = cols_to_output; } new_inst->col_delimiter = tbl_template->col_delimiter; new_inst->print_header = true; new_inst->out = 0; - new_inst->last_printed_col = -1; new_inst->row_printing_started = false; new_inst->col_out = -1; new_inst->formatter = tbl_template->formatter; - new_inst->data = NULL; - return new_inst; -} - -struct table *table_init(const struct table_template *tbl_template) -{ - struct table *tbl = table_make_instance(tbl_template); - - if(!tbl->formatter) { - tbl->formatter = &table_fmt_human_readable; + if(!new_inst->formatter) { + new_inst->formatter = &table_fmt_human_readable; } - - tbl->print_header = true; // by default, print header - return tbl; + new_inst->formatter_data = NULL; + return new_inst; } void table_cleanup(struct table *tbl) @@ -92,36 +87,39 @@ void table_cleanup(struct table *tbl) // TODO: test default column order static void table_make_default_column_order(struct table *tbl) { - int *col_order_int = mp_alloc_zero(tbl->pool, sizeof(int) * tbl->column_count); // FIXME: use stack instead of memory pool + struct table_col_instance *col_order = alloca(sizeof(struct table_col_instance) * (tbl->column_count + 1)); + bzero(col_order, sizeof(struct table_col_instance) * tbl->column_count); + for(int i = 0; i < tbl->column_count; i++) { - col_order_int[i] = i; + col_order[i].idx = (uint) i; + // currently, XTYPE_FMT_DEFAULT is 0, so bzero actually sets it correctly. This makes it more explicit. + col_order[i].fmt = XTYPE_FMT_DEFAULT; } - table_set_col_order(tbl, col_order_int, tbl->column_count); + struct table_col_instance tbl_col_order_end = TBL_COL_ORDER_END; + col_order[tbl->column_count] = tbl_col_order_end; + + table_set_col_order(tbl, col_order); } void table_start(struct table *tbl, struct fastbuf *out) { - tbl->last_printed_col = -1; tbl->row_printing_started = false; tbl->out = out; ASSERT_MSG(tbl->out, "Output fastbuf not specified."); if(tbl->column_order == NULL) table_make_default_column_order(tbl); - else { - // update linked lists - table_update_ll(tbl); - } + // update linked lists + table_update_ll(tbl); if(tbl->formatter->table_start != NULL) tbl->formatter->table_start(tbl); mp_save(tbl->pool, &tbl->pool_state); - ASSERT_MSG(tbl->col_delimiter, "In-between column delimiter not specified."); + ASSERT_MSG(tbl->col_delimiter, "Column delimiter not specified."); } void table_end(struct table *tbl) { - tbl->last_printed_col = -1; tbl->row_printing_started = false; mp_restore(tbl->pool, &tbl->pool_state); @@ -166,8 +164,8 @@ static void table_update_ll(struct table *tbl) } for(int i = 0; i < cols_to_output; i++) { - int idx = tbl->column_order[i].idx; - tbl->column_order[i].col_def = tbl->columns + idx; + int col_def_idx = tbl->column_order[i].idx; + tbl->column_order[i].col_def = tbl->columns + col_def_idx; } for(int i = 0; i < cols_to_output; i++) { @@ -178,59 +176,89 @@ static void table_update_ll(struct table *tbl) } } -void table_set_col_order(struct table *tbl, int *col_order, int cols_to_output) +void table_set_col_order(struct table *tbl, const struct table_col_instance *col_order) { - for(int i = 0; i < cols_to_output; i++) { - ASSERT_MSG(col_order[i] >= 0 && col_order[i] < tbl->column_count, "Column %d does not exist (column number should be between 0 and %d).", col_order[i], tbl->column_count - 1); + uint cols_to_output = 0; + for(; ; cols_to_output++) { + if(col_order[cols_to_output].idx == ~0U) break; + ASSERT_MSG(col_order[cols_to_output].idx < (uint) tbl->column_count, + "Column %d does not exist; column number should be between 0 and %d(including).", col_order[cols_to_output].idx, tbl->column_count - 1); } tbl->cols_to_output = cols_to_output; - tbl->column_order = mp_alloc_zero(tbl->pool, sizeof(struct table_col_instance) * cols_to_output); - for(int i = 0; i < cols_to_output; i++) { - int col_idx = col_order[i]; - tbl->column_order[i].idx = col_idx; - tbl->column_order[i].col_def = tbl->columns + col_idx; - tbl->column_order[i].cell_content = NULL; - tbl->column_order[i].fmt = tbl->columns[col_idx].fmt; + tbl->column_order = mp_alloc(tbl->pool, sizeof(struct table_col_instance) * cols_to_output); + memcpy(tbl->column_order, col_order, sizeof(struct table_col_instance) * cols_to_output); + for(uint i = 0; i < cols_to_output; i++) { + int col_def_idx = tbl->column_order[i].idx; // this is given in arg @col_order + tbl->column_order[i].col_def = tbl->columns + col_def_idx; + tbl->column_order[i].cell_content = NULL; // cell_content is copied from arg @col_order, so make sure that it is NULL + tbl->column_order[i].next_column = -1; + // tbl->column_order[i].fmt should be untouched (copied from arg @col_order) } - table_update_ll(tbl); } -bool table_col_is_printed(struct table *tbl, uint col_idx) +bool table_col_is_printed(struct table *tbl, uint col_def_idx) { - if(tbl->ll_headers[col_idx] == -1) return 0; + if(tbl->ll_headers[col_def_idx] == -1) return false; - return 1; + return true; } -static char * table_parse_col_arg(char *col_def) +const char *table_set_col_opt(struct table *tbl, uint col_inst_idx, const char *col_opt) +{ + const struct table_column *col_def = tbl->column_order[col_inst_idx].col_def; + + // Make sure that we do not call table_set_col_opt, which would + // result in an infinite recursion. + if(col_def && col_def->set_col_opt) { + ASSERT_MSG(col_def->set_col_opt != table_set_col_opt,"table_set_col_opt should not be used as a struct table_column::set_col_opt hook"); + return col_def->set_col_opt(tbl, col_inst_idx, col_opt); + } + + if(col_def && col_def->type_def) { + u32 fmt = 0; + const char *tmp_err = xtype_parse_fmt(col_def->type_def, col_opt, &fmt, tbl->pool); + if(tmp_err) return mp_printf(tbl->pool, "Invalid column format; xtypes error: '%s'.", tmp_err); + tbl->column_order[col_inst_idx].fmt = fmt; + return NULL; + } + + return mp_printf(tbl->pool, "Invalid column format option: '%s' for column %d.", col_opt, col_inst_idx); +} + +/** + * the input is a null-terminated string that contains: "'['','\0 + * i.e., the ']' is missing and is replaced by \0. + * the function replace the '[' by \0 and then parses the rest of the string. + **/ +static char **table_parse_col_arg2(char *col_def) { - // FIXME: should be switched to str_sepsplit char * left_br = strchr(col_def, '['); if(left_br == NULL) return NULL; + *left_br = 0; left_br++; - char *right_br = strchr(left_br, ']'); - *right_br = 0; - return left_br; -} -const char *table_set_col_opt(struct table *tbl, uint col_idx, const char *col_opt) -{ - const struct table_column *col_def = tbl->column_order[col_idx].col_def; - if(col_def && col_def->set_col_opt && col_def->set_col_opt != table_set_col_opt) { - return col_def->set_col_opt(tbl, col_idx, col_opt); - } + char *col_opt = left_br; - if(col_def && col_def->type_def && col_def->type_def->parse_fmt) { - uint fmt = 0; - const char *tmp_err = col_def->type_def->parse_fmt(col_opt, &fmt, tbl->pool); - if(tmp_err) return tmp_err; - tbl->column_order[col_idx].fmt = fmt; - return NULL; + char *next = NULL; + char **result = NULL; + GARY_INIT(result, 0); + for(;;) { + next = strchr(col_opt, ','); + if(!next) break; + if(*next == 0) break; + *next = 0; + next++; + if(*col_opt) + *GARY_PUSH(result) = col_opt; + + col_opt = next; } + if(*col_opt) + *GARY_PUSH(result) = col_opt; - return mp_printf(tbl->pool, "Invalid column format option: '%s' for column %d.", col_opt, col_idx); + return result; } /** @@ -253,8 +281,11 @@ const char * table_set_col_order_by_name(struct table *tbl, const char *col_orde char *tmp_col_order = stk_strdup(col_order_str); int col_count = 1; + bool inside_brackets = false; for(int i = 0; col_order_str[i] != 0; i++) { - if(col_order_str[i] == ',') { + if(col_order_str[i] == '[') inside_brackets = true; + if(col_order_str[i] == ']') inside_brackets = false; + if(!inside_brackets && col_order_str[i] == ',') { col_count++; } } @@ -262,41 +293,63 @@ const char * table_set_col_order_by_name(struct table *tbl, const char *col_orde tbl->cols_to_output = col_count; tbl->column_order = mp_alloc_zero(tbl->pool, sizeof(struct table_col_instance) * col_count); - int curr_col_idx = 0; + int curr_col_inst_idx = 0; char *name_start = tmp_col_order; while(name_start) { - char *next = strchr(name_start, ','); - if(next) { + char *next = strpbrk(name_start, "[,"); + if(next && *next == '[') { + next = strchr(next, ']'); + if(!next) return mp_printf(tbl->pool, "Invalid column definition, missing ']'."); + *next++ = 0; + next = *next == 0 ? NULL : next + 1; // if next points to the last \0 => end the computation + } else if(next) { *next++ = 0; } - char *arg = table_parse_col_arg(name_start); // this sets 0 on the '[' - int col_idx = table_get_col_idx(tbl, name_start); + char **args = table_parse_col_arg2(name_start); // this sets 0 on the '[' + int col_def_idx = table_get_col_idx(tbl, name_start); - if(col_idx == -1) { + if(col_def_idx == -1) { return mp_printf(tbl->pool, "Unknown table column '%s', possible column names are: %s.", name_start, table_get_col_list(tbl)); } - tbl->column_order[curr_col_idx].col_def = tbl->columns + col_idx; - tbl->column_order[curr_col_idx].idx = col_idx; - tbl->column_order[curr_col_idx].fmt = tbl->columns[col_idx].fmt; - if(tbl->columns[col_idx].type_def && tbl->columns[col_idx].set_col_opt) { - const char *err = NULL; - err = tbl->columns[col_idx].set_col_opt(tbl, curr_col_idx, arg); - if(err) return mp_printf(tbl->pool, "Error occured while setting column option: %s.", err); - } + tbl->column_order[curr_col_inst_idx].col_def = tbl->columns + col_def_idx; + tbl->column_order[curr_col_inst_idx].idx = col_def_idx; + tbl->column_order[curr_col_inst_idx].fmt = tbl->columns[col_def_idx].fmt; + if(args) { + for(uint i = 0; i < GARY_SIZE(args); i++) { + const char *err = NULL; + err = table_set_col_opt(tbl, curr_col_inst_idx, args[i]); + if(err) return mp_printf(tbl->pool, "Error occured while setting column option: %s.", err); + } + GARY_FREE(args); + } name_start = next; - curr_col_idx++; + curr_col_inst_idx++; } - table_update_ll(tbl); - return NULL; } /*** Table cells ***/ -static void table_set_all_inst_content(struct table *tbl, int col_templ, const char *col_content) +/** + * The TBL_COL_ITER_START macro are used for iterating over all instances of a particular column in + * table _tbl. _colidx is the column index in _tbl, _instptr is the pointer to the column instance + * (struct table_col_instance *), _idxval is the index of current column index. The variables are + * enclosed in a block, so they do not introduce variable name collisions. + * + * The TBL_COL_ITER_END macro must close the block started with TBL_COL_ITER_START. + * + * These macros are usually used to hide the implementation details of the column instances linked + * list. This is usefull for definition of new types. + **/ +#define TBL_COL_ITER_START(_tbl, _colidx, _instptr, _idxval) { struct table_col_instance *_instptr = NULL; int _idxval = _tbl->ll_headers[_colidx]; \ + for(_idxval = _tbl->ll_headers[_colidx], _instptr = _tbl->column_order + _idxval; _idxval != -1; _idxval = _tbl->column_order[_idxval].next_column, _instptr = _tbl->column_order + _idxval) + +#define TBL_COL_ITER_END } + +static void table_col_raw(struct table *tbl, int col_templ, const char *col_content) { TBL_COL_ITER_START(tbl, col_templ, curr_col_ptr, curr_col) { curr_col_ptr->cell_content = col_content; @@ -307,23 +360,24 @@ void table_col_generic_format(struct table *tbl, int col, void *value, const str { ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col); ASSERT(tbl->columns[col].type_def == COL_TYPE_ANY || expected_type == tbl->columns[col].type_def); - tbl->last_printed_col = col; tbl->row_printing_started = true; - TBL_COL_ITER_START(tbl, col, curr_col, curr_col_idx) { + TBL_COL_ITER_START(tbl, col, curr_col, curr_col_inst_idx) { enum xtype_fmt fmt = curr_col->fmt; curr_col->cell_content = expected_type->format(value, fmt, tbl->pool); } TBL_COL_ITER_END } +#undef TBL_COL_ITER_START +#undef TBL_COL_ITER_END + void table_col_printf(struct table *tbl, int col, const char *fmt, ...) { ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col); - tbl->last_printed_col = col; tbl->row_printing_started = true; va_list args; va_start(args, fmt); char *cell_content = mp_vprintf(tbl->pool, fmt, args); - table_set_all_inst_content(tbl, col, cell_content); + table_col_raw(tbl, col, cell_content); va_end(args); } @@ -343,7 +397,6 @@ void table_reset_row(struct table *tbl) tbl->column_order[i].cell_content = NULL; } mp_restore(tbl->pool, &tbl->pool_state); - tbl->last_printed_col = -1; tbl->row_printing_started = false; } @@ -368,7 +421,7 @@ struct fastbuf *table_col_fbstart(struct table *tbl, int col) void table_col_fbend(struct table *tbl) { char *cell_content = fbpool_end(&tbl->fb_col_out); - table_set_all_inst_content(tbl, tbl->col_out, cell_content); + table_col_raw(tbl, tbl->col_out, cell_content); tbl->col_out = -1; } @@ -452,7 +505,7 @@ const char *table_set_option(struct table *tbl, const char *opt) const char *table_set_gary_options(struct table *tbl, char **gary_table_opts) { - for (uint i = 0; i < GARY_SIZE(gary_table_opts); i++) { + for(uint i = 0; i < GARY_SIZE(gary_table_opts); i++) { const char *rv = table_set_option(tbl, gary_table_opts[i]); if(rv != NULL) { return rv; @@ -575,7 +628,8 @@ enum test_table_cols { TEST_COL0_STR, TEST_COL1_INT, TEST_COL2_UINT, TEST_COL3_BOOL, TEST_COL4_DOUBLE }; -static struct table_col_instance test_column_order[] = { TBL_COL(TEST_COL3_BOOL), TBL_COL(TEST_COL4_DOUBLE), TBL_COL(TEST_COL2_UINT), TBL_COL(TEST_COL1_INT), TBL_COL(TEST_COL0_STR) }; +static struct table_col_instance test_column_order[] = { TBL_COL(TEST_COL3_BOOL), TBL_COL(TEST_COL4_DOUBLE), + TBL_COL(TEST_COL2_UINT), TBL_COL(TEST_COL1_INT), TBL_COL(TEST_COL0_STR), TBL_COL_ORDER_END }; static struct table_template test_tbl = { TBL_COLUMNS { @@ -662,6 +716,22 @@ static void test_simple1(struct fastbuf *out) do_print1(tbl); table_end(tbl); + + // test table_col_order_fmt + struct table_col_instance col_order[] = { TBL_COL(TEST_COL0_STR), TBL_COL_FMT(TEST_COL4_DOUBLE, XTYPE_FMT_PRETTY), TBL_COL_FMT(TEST_COL4_DOUBLE, XTYPE_FMT_RAW), TBL_COL_ORDER_END }; + table_set_col_order(tbl, col_order); + table_start(tbl, out); + + table_col_str(tbl, TEST_COL0_STR, "test"); + table_col_double(tbl, TEST_COL4_DOUBLE, 1.23456789); + table_end_row(tbl); + + table_col_str(tbl, TEST_COL0_STR, "test"); + table_col_double(tbl, TEST_COL4_DOUBLE, 1.23456789); + table_end_row(tbl); + + table_end(tbl); + table_cleanup(tbl); } @@ -669,7 +739,7 @@ enum test_any_table_cols { TEST_ANY_COL0_INT, TEST_ANY_COL1_ANY }; -static struct table_col_instance test_any_column_order[] = { TBL_COL(TEST_ANY_COL0_INT), TBL_COL_FMT(TEST_ANY_COL1_ANY, XTYPE_FMT_PRETTY) }; +static struct table_col_instance test_any_column_order[] = { TBL_COL(TEST_ANY_COL0_INT), TBL_COL_FMT(TEST_ANY_COL1_ANY, XTYPE_FMT_PRETTY), TBL_COL_ORDER_END }; static struct table_template test_any_tbl = { TBL_COLUMNS {