]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/table.c
Xtype docs: Fixed a typo
[libucw.git] / ucw / table.c
index cf9941775256a48d0d3ff57e7017f7e782e946b2..1324839a7c371fd59e1097e09e7724faae437b1e 100644 (file)
@@ -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 = 1;
+  new_inst->print_header = true;
   new_inst->out = 0;
-  new_inst->last_printed_col = -1;
-  new_inst->row_printing_started = 0;
+  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 = 1; // by default, print header
-  return tbl;
+  new_inst->formatter_data = NULL;
+  return new_inst;
 }
 
 void table_cleanup(struct table *tbl)
@@ -92,37 +87,40 @@ 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 = 0;
+  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 = 0;
+  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,63 +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)
 {
-  // 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 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);
 }
 
 /**
- * Setting options for basic table types (as defined in table.h)
+ * the input is a null-terminated string that contains: "<col-name>'['<param1>','<param2>\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.
  **/
-int table_set_col_opt_default(struct table *tbl, int col_idx, const char *col_arg, char **err)
+static char **table_parse_col_arg2(char *col_def)
 {
-  const struct table_column *col_def = tbl->column_order[col_idx].col_def;
+  char * left_br = strchr(col_def, '[');
+  if(left_br == NULL) return NULL;
 
-  if(col_def->type_def == &xt_double) {
-    uint precision = 0;
-    const char *tmp_err = str_to_uint(&precision, col_arg, NULL, 0);
-    if(tmp_err) {
-      *err = mp_printf(tbl->pool, "An error occured while parsing precision: %s.", tmp_err);
-      return false;
-    }
-    tbl->column_order[col_idx].fmt = precision; // FIXME: shift the value of precision
-    return true;
+  *left_br = 0;
+  left_br++;
+
+  char *col_opt = left_br;
+
+  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;
 
-  *err = mp_printf(tbl->pool, "Invalid column format option: '%s' for column %d.", col_arg, col_idx);
-  return false;
+  return result;
 }
 
 /**
@@ -257,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++;
     }
   }
@@ -266,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_instance_option) {
-      char *err = NULL;
-      tbl->columns[col_idx].set_col_instance_option(tbl, curr_col_idx, arg, &err);
-      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;
@@ -311,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 = 1;
-  TBL_COL_ITER_START(tbl, col, curr_col, curr_col_idx) {
+  tbl->row_printing_started = true;
+  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 = 1;
+  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);
 }
 
@@ -347,14 +397,13 @@ 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 = 0;
+  tbl->row_printing_started = false;
 }
 
 void table_end_row(struct table *tbl)
 {
   ASSERT(tbl->formatter->row_output);
-  if(tbl->row_printing_started == 0) return;
+  if(tbl->row_printing_started == false) return;
   tbl->formatter->row_output(tbl);
   table_reset_row(tbl);
 }
@@ -372,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;
 }
 
@@ -383,7 +432,7 @@ const char *table_set_option_value(struct table *tbl, const char *key, const cha
   // Options with no value
   if(value == NULL || (value != NULL && strlen(value) == 0)) {
     if(strcmp(key, "noheader") == 0) {
-      tbl->print_header = 0;
+      tbl->print_header = false;
       return NULL;
     }
   }
@@ -391,12 +440,13 @@ const char *table_set_option_value(struct table *tbl, const char *key, const cha
   // Options with a value
   if(value) {
     if(strcmp(key, "header") == 0) {
-      if(value[1] != 0)
-        return mp_printf(tbl->pool, "Invalid header parameter: '%s' has invalid value: '%s'.", key, value);
-      uint tmp = value[0] - '0';
-      if(tmp > 1)
+      bool tmp;
+      const char *err = xt_bool.parse(value, &tmp, tbl->pool);
+      if(err)
         return mp_printf(tbl->pool, "Invalid header parameter: '%s' has invalid value: '%s'.", key, value);
+
       tbl->print_header = tmp;
+
       return NULL;
     } else if(strcmp(key, "cols") == 0) {
       return table_set_col_order_by_name(tbl, value);
@@ -455,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;
@@ -500,7 +550,7 @@ static void table_start_human_readable(struct table *tbl)
     tbl->col_delimiter = " ";
   }
 
-  if(tbl->print_header != 0) {
+  if(tbl->print_header != false) {
     table_write_header(tbl);
   }
 }
@@ -529,7 +579,7 @@ static void table_start_machine_readable(struct table *tbl)
     tbl->col_delimiter = "\t";
   }
 
-  if(tbl->print_header != 0 && tbl->cols_to_output > 0) {
+  if(tbl->print_header != false && tbl->cols_to_output > 0) {
     bputs(tbl->out, tbl->column_order[0].col_def->name);
     for(uint i = 1; i < tbl->cols_to_output; i++) {
       bputs(tbl->out, tbl->col_delimiter);
@@ -578,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 {
@@ -634,11 +685,11 @@ static void test_simple1(struct fastbuf *out)
   table_end(tbl);
 
   // this also tests whether there is need to call table_set_col_order_by_name after table_end was called
-  tbl->print_header = 0;
+  tbl->print_header = false;
   table_start(tbl, out);
   do_print1(tbl);
   table_end(tbl);
-  tbl->print_header = 1;
+  tbl->print_header = true;
 
   table_set_col_order_by_name(tbl, "col3_bool");
   table_start(tbl, out);
@@ -665,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);
 }
 
@@ -672,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 {