]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/table-types.c
tableprinter: size units are now static
[libucw.git] / ucw / table-types.c
index ab2e5b696ab9f0542b72924f1d4159379d0f7cff..1894a9a6daddf673b929461bc69ee7b3eeac75a9 100644 (file)
+/*
+ *     UCW Library -- Table printer types
+ *
+ *     (c) 2014 Robert Kessl <robert.kessl@economia.cz>
+ */
+
 #include <ucw/lib.h>
-#include <ucw/config.h>
 #include <ucw/table-types.h>
 #include <ucw/fastbuf.h>
-#include <ucw/config.h>
 #include <ucw/table.h>
 #include <time.h>
 #include <stdio.h>
 #include <stdlib.h>
-
-static const char *unit_suffix[] = {
-  [UNIT_BYTE] = "",
-  [UNIT_KILOBYTE] = "KB",
-  [UNIT_MEGABYTE] = "MB",
-  [UNIT_GIGABYTE] = "GB",
-  [UNIT_TERABYTE] = "TB"
+#include <inttypes.h>
+#include <errno.h>
+
+/** xt_size **/
+
+static struct unit_definition xtype_units_size[] = {
+  [SIZE_UNIT_BYTE] = { "", 1LLU, 1 },
+  [SIZE_UNIT_KILOBYTE] = { "KB", 1024LLU, 1 },
+  [SIZE_UNIT_MEGABYTE] = { "MB", 1024LLU * 1024LLU, 1 },
+  [SIZE_UNIT_GIGABYTE] = { "GB", 1024LLU * 1024LLU * 1024LLU, 1 },
+  [SIZE_UNIT_TERABYTE] = { "TB", 1024LLU * 1024LLU * 1024LLU * 1024LLU, 1 },
+  { 0, 0, 0 }
 };
 
-static bool table_set_col_opt_size(struct table *tbl, uint col_copy_idx, const char *col_arg, char **err)
+static const char *xt_size_format(void *src, u32 fmt, struct mempool *pool)
 {
-  int col_type_idx = tbl->column_order[col_copy_idx].idx;
-  if(tbl->columns[col_type_idx].type != COL_TYPE_SIZE) {
-    *err = NULL;
-    return false;
+  u64 curr_val = *(u64*) src;
+
+  if(fmt == XTYPE_FMT_RAW) {
+    return mp_printf(pool, "%"PRIu64, curr_val);
   }
 
-  if(col_arg == NULL) {
-    *err = NULL;
-    return true;
+  uint out_units = SIZE_UNIT_BYTE;
+
+  if(fmt == XTYPE_FMT_DEFAULT) {
+    curr_val = curr_val;
+    out_units = SIZE_UNIT_BYTE;
+  } else if(fmt == XTYPE_FMT_PRETTY) {
+    curr_val = curr_val;
+    out_units = SIZE_UNIT_BYTE;
+  } else if((fmt & SIZE_UNITS_FIXED) != 0) {
+    curr_val = curr_val / xtype_units_size[fmt & ~SIZE_UNITS_FIXED].num;
+    out_units = fmt & ~SIZE_UNITS_FIXED;
   }
 
-  if(strcasecmp(col_arg, "b") == 0 || strcasecmp(col_arg, "bytes") == 0) {
-    tbl->column_order[col_copy_idx].output_type = UNIT_BYTE;
+  return mp_printf(pool, "%"PRIu64"%s", curr_val, xtype_units_size[out_units].unit);
+}
+
+static const char *xt_size_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
+{
+  if(opt_str == NULL) {
+    return "NULL is not supported as a column argument.";
   }
 
-  tbl->column_order[col_copy_idx].output_type = CELL_OUT_UNINITIALIZED;
-  for(uint i = 0; i < ARRAY_SIZE(unit_suffix); i++) {
-    if(strcasecmp(col_arg, unit_suffix[i]) == 0) {
-      tbl->column_order[col_copy_idx].output_type = i;
-    }
+  if(strlen(opt_str) == 0 || strcmp(opt_str, "B") == 0 || strcmp(opt_str, "Bytes") == 0) {
+    *dest = SIZE_UNIT_BYTE | SIZE_UNITS_FIXED;
+    return NULL;
   }
 
-  if(tbl->column_order[col_copy_idx].output_type == CELL_OUT_UNINITIALIZED) {
-    *err = mp_printf(tbl->pool, "Tableprinter: invalid column format option: '%s' for column %d (counted from 0)", col_arg, col_copy_idx);
-    return true;
+  int unit_idx = xtype_unit_parser(opt_str, xtype_units_size);
+  if(unit_idx == -1) {
+    return mp_printf(pool, "Unknown option '%s'", opt_str);
   }
 
-  *err = NULL;
-  return true;
+  *dest = unit_idx | SIZE_UNITS_FIXED;
+  return NULL;
 }
 
-struct table_user_type table_type_size = {
-  .set_col_instance_option = table_set_col_opt_size,
-  .type = COL_TYPE_SIZE,
-};
-
-static bool table_set_col_opt_timestamp(struct table *tbl, uint col_copy_idx, const char *col_arg, char **err)
+static const char *xt_size_parse(const char *str, void *dest, struct mempool *pool)
 {
-  int col_type_idx = tbl->column_order[col_copy_idx].idx;
-  if(tbl->columns[col_type_idx].type != COL_TYPE_TIMESTAMP) {
-    *err = NULL;
-    return false;
+  errno = 0;
+  char *units_start = NULL;
+  u64 parsed = strtoul(str, &units_start, 10);
+  if(str == units_start) {
+    return mp_printf(pool, "Invalid value of size: '%s'.", str);
+  }
+
+  if(errno == EINVAL) {
+    return "Error occured during parsing of size.";
+  }
+  if(errno == ERANGE) {
+    return "Error: size value either too large or too small.";
   }
 
-  if(col_arg == NULL) {
-    *err = NULL;
-    return true;
+  if(*units_start == 0) {
+    *(u64*) dest = (u64) parsed;
+    return NULL;
   }
 
-  if(strcasecmp(col_arg, "timestamp") == 0 || strcasecmp(col_arg, "epoch") == 0) {
-    tbl->column_order[col_copy_idx].output_type = TIMESTAMP_EPOCH;
-  } else if(strcasecmp(col_arg, "datetime") == 0) {
-    tbl->column_order[col_copy_idx].output_type = TIMESTAMP_DATETIME;
-  } else {
-    *err = mp_printf(tbl->pool, "Tableprinter: invalid column format option: '%s' for column %d.", col_arg, col_copy_idx);
-    return true;
+  int unit_idx = xtype_unit_parser(units_start, xtype_units_size);
+  if(unit_idx == -1) {
+    return mp_printf(pool, "Invalid units: '%s'.", units_start);
   }
 
-  *err = NULL;
-  return true;
+  *(u64*) dest = parsed * xtype_units_size[unit_idx].num;
+  return NULL;
 }
 
-struct table_user_type table_type_timestamp = {
-  .set_col_instance_option = table_set_col_opt_timestamp,
-  .type = COL_TYPE_TIMESTAMP,
+TABLE_COL_BODY(size, u64)
+
+const struct xtype xt_size = {
+  .size = sizeof(u64),
+  .name = "size",
+  .parse = xt_size_parse,
+  .format = xt_size_format,
+  .parse_fmt = xt_size_fmt_parse
 };
 
-void table_col_size_name(struct table *tbl, const char *col_name, u64 val)
+/** xt_timestamp **/
+
+#define FORMAT_TIME_SIZE 20    // Minimum buffer size
+
+static const char *xt_timestamp_format(void *src, u32 fmt, struct mempool *pool)
 {
-  int col = table_get_col_idx(tbl, col_name);
-  table_col_size(tbl, col, val);
+  char formatted_time_buf[FORMAT_TIME_SIZE] = { 0 };
+
+  u64 tmp_time_u64 = *(u64*)src;
+  time_t tmp_time = (time_t) tmp_time_u64;
+  struct tm t = *gmtime(&tmp_time);
+  switch (fmt) {
+  case XTYPE_FMT_DEFAULT:
+  case XTYPE_FMT_RAW:
+    sprintf(formatted_time_buf, "%"PRIu64, tmp_time_u64);
+    break;
+  case XTYPE_FMT_PRETTY:
+    strftime(formatted_time_buf, FORMAT_TIME_SIZE, "%F %T", &t);
+    break;
+  default:
+    ASSERT(0);
+    break;
+  }
+
+  return mp_printf(pool, "%s", formatted_time_buf);
 }
 
-void table_col_size(struct table *tbl, int col, u64 val)
+static const char *xt_timestamp_fmt_parse(const char *opt_str, u32 *dest, struct mempool *pool)
 {
-  ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
-  ASSERT(tbl->columns[col].type == COL_TYPE_ANY || COL_TYPE_SIZE == tbl->columns[col].type);
-
-  tbl->last_printed_col = col;
-  tbl->row_printing_started = 1;
-
-  static u64 unit_div[] = {
-    [UNIT_BYTE] = (u64) 1,
-    [UNIT_KILOBYTE] = (u64) 1024LLU,
-    [UNIT_MEGABYTE] = (u64) (1024LLU * 1024LLU),
-    [UNIT_GIGABYTE] = (u64) (1024LLU * 1024LLU * 1024LLU),
-    [UNIT_TERABYTE] = (u64) (1024LLU * 1024LLU * 1024LLU * 1024LLU)
-  };
-
-  TBL_COL_ITER(tbl, col, curr_col, curr_col_idx) {
-    // FIXME: do some rounding?
-    uint out_type = 0;
-    if(curr_col->output_type == CELL_OUT_UNINITIALIZED) {
-      val = val / unit_div[UNIT_BYTE];
-      out_type = 0;
-    } else {
-      val = val / unit_div[curr_col->output_type];
-      out_type = curr_col->output_type;
-    }
-
-    curr_col->cell_content = mp_printf(tbl->pool, "%lu%s", val, unit_suffix[out_type]);
+  if(opt_str == NULL) {
+    return "NULL is not supported as a column argument.";
   }
-}
 
-#define FORMAT_TIME_SIZE 20    // Minimum buffer size
+  if(strcasecmp(opt_str, "timestamp") == 0 || strcasecmp(opt_str, "epoch") == 0) {
+    *dest = TIMESTAMP_EPOCH;
+    return NULL;
+  } else if(strcasecmp(opt_str, "datetime") == 0) {
+    *dest = TIMESTAMP_DATETIME;
+    return NULL;
+  }
 
-void table_col_timestamp_name(struct table *tbl, const char * col_name, u64 val)
-{
-  int col = table_get_col_idx(tbl, col_name);
-  table_col_size(tbl, col, val);
+  return mp_printf(pool, "Invalid column format option: '%s'.", opt_str);
 }
 
-void table_col_timestamp(struct table *tbl, int col, u64 val)
+static const char *xt_timestamp_parse(const char *str, void *dest, struct mempool *pool)
 {
-  ASSERT_MSG(col < tbl->column_count && col >= 0, "Table column %d does not exist.", col);
-  ASSERT(tbl->columns[col].type == COL_TYPE_ANY || COL_TYPE_TIMESTAMP == tbl->columns[col].type);
+  errno = 0;
+  char *parse_end = NULL;
+  u64 parsed = strtoul(str, &parse_end, 10);
+  if(str == parse_end) {
+    return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
+  }
+  if(errno == EINVAL) {
+    return "Error occured during parsing of size.";
+  }
 
-  char formatted_time_buf[FORMAT_TIME_SIZE] = { 0 };
+  if(errno == ERANGE) {
+    return "Error: size value either too large or too small.";
+  }
 
-  time_t tmp_time = (time_t)val;
-  struct tm t = *gmtime(&tmp_time);
-  TBL_COL_ITER(tbl, col, curr_col, curr_col_idx) {
-    switch (curr_col->output_type) {
-    case TIMESTAMP_EPOCH:
-    case CELL_OUT_UNINITIALIZED:
-      sprintf(formatted_time_buf, "%lu", val);
-      break;
-    case TIMESTAMP_DATETIME:
-      strftime(formatted_time_buf, FORMAT_TIME_SIZE, "%F %T", &t);
-    break;
-    default:
-      abort();
-      break;
-    }
+  if(*parse_end == 0) {
+    *(u64*) dest = (u64) parsed;
+    return NULL;
+  }
 
-    curr_col->cell_content = mp_printf(tbl->pool, "%s", formatted_time_buf);
+  struct tm parsed_time;
+  parse_end = strptime(str, "%F %T", &parsed_time);
+  if(parse_end == NULL) {
+    return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
+  }
+  if(*parse_end != 0) {
+    return mp_printf(pool, "Invalid value of timestamp: '%s'.", str);
   }
+
+  time_t tmp_time = mktime(&parsed_time);
+  *(u64*)dest = (u64) tmp_time;
+
+  return NULL;
 }
+
+TABLE_COL_BODY(timestamp, u64)
+
+const struct xtype xt_timestamp = {
+  .size = sizeof(u64),
+  .name = "timestamp",
+  .parse = xt_timestamp_parse,
+  .format = xt_timestamp_format,
+  .parse_fmt = xt_timestamp_fmt_parse
+};