]> mj.ucw.cz Git - libucw.git/commitdiff
Table: added left alignment flag
authorRobert Kessl <kesslr@centrum.cz>
Wed, 4 Jun 2014 10:55:08 +0000 (12:55 +0200)
committerRobert Kessl <kesslr@centrum.cz>
Wed, 4 Jun 2014 10:55:08 +0000 (12:55 +0200)
 - added tests of alignment flag (table-test-align)
 - modified cmd line switches in table-test

ucw/Makefile
ucw/table-test-align.c [new file with mode: 0644]
ucw/table-test-align.t [new file with mode: 0644]
ucw/table-test.c
ucw/table.c
ucw/table.h

index 527e2beba267cc6f6ea5425932a51a47f8c9e654..e45a339c752f28f69eb600208302ca17578ba851 100644 (file)
@@ -133,6 +133,7 @@ $(o)/ucw/ipaccess-test: $(o)/ucw/ipaccess-test.o $(LIBUCW)
 $(o)/ucw/trie-test: $(o)/ucw/trie-test.o $(LIBUCW)
 $(o)/ucw/opt-test: $(o)/ucw/opt-test.o $(LIBUCW)
 $(o)/ucw/table-test: $(o)/ucw/table-test.o $(LIBUCW)
+$(o)/ucw/table-test-align: $(o)/ucw/table-test-align.o $(LIBUCW)
 
 TESTS+=$(addprefix $(o)/ucw/,regex.test unicode.test hash-test.test mempool.test stkstring.test \
     slists.test bbuf.test kmp-test.test getopt.test ff-unicode.test eltpool.test \
@@ -141,7 +142,7 @@ TESTS+=$(addprefix $(o)/ucw/,regex.test unicode.test hash-test.test mempool.test
     fb-mem.test fb-buffer.test fb-mmap.test fb-multi.test fb-null.test \
     redblack-test.test url.test strtonum-test.test \
     gary.test time.test crc.test signames.test md5.test bitops.test opt.test \
-    table.test table-test.test)
+    table.test table-test.test table-test-align.test)
 
 $(o)/ucw/varint.test: $(o)/ucw/varint-t
 $(o)/ucw/regex.test: $(o)/ucw/regex-t
@@ -175,6 +176,7 @@ $(o)/ucw/md5.test: $(o)/ucw/md5-t
 $(o)/ucw/opt.test: $(o)/ucw/opt-test
 $(o)/ucw/table.test: $(o)/ucw/table-t
 $(o)/ucw/table-test.test: $(o)/ucw/table-test
+$(o)/ucw/table-test-align.test: $(o)/ucw/table-test-align
 
 ifdef CONFIG_UCW_THREADS
 TESTS+=$(addprefix $(o)/ucw/,asio.test)
diff --git a/ucw/table-test-align.c b/ucw/table-test-align.c
new file mode 100644 (file)
index 0000000..4df4292
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ *     Unit tests of table printer
+ *
+ *     (c) 2014 Robert Kessl <robert.kessl@economia.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/table.h>
+#include <ucw/opt.h>
+#include <stdio.h>
+
+
+enum test_table_cols {
+  test_col0_str, test_col1_int, test_col2_uint, test_col3_bool, test_col4_double
+};
+
+static struct table test_tbl = {
+  TBL_COLUMNS {
+    [test_col0_str] = TBL_COL_STR("col0_str", 30 | CELL_ALIGN_LEFT),
+    [test_col1_int] = TBL_COL_INT("col1_int", 8),
+    [test_col2_uint] = TBL_COL_UINT("col2_uint", 9),
+    [test_col3_bool] = TBL_COL_BOOL("col3_bool", 9 | CELL_ALIGN_LEFT),
+    [test_col4_double] = TBL_COL_DOUBLE("col4_double", 11 | CELL_ALIGN_LEFT, 5),
+    TBL_COL_END
+  },
+  TBL_OUTPUT_HUMAN_READABLE,
+  TBL_COL_DELIMITER("\t"),
+};
+
+static int test_to_perform = -1;
+static char **cli_table_opts;
+
+static struct opt_section table_printer_opts = {
+  OPT_ITEMS {
+    OPT_HELP("Options:"),
+    OPT_STRING_MULTIPLE('T', "table", cli_table_opts, OPT_REQUIRED_VALUE, "\tSets options for the table."),
+    OPT_END
+  }
+};
+
+
+static void process_command_line_opts(char *argv[], struct table *tbl)
+{
+  GARY_INIT(cli_table_opts, 0);
+
+  opt_parse(&table_printer_opts, argv+1);
+  table_set_gary_options(tbl, cli_table_opts);
+
+  GARY_FREE(cli_table_opts);
+}
+
+static void print_table(struct table *tbl, struct fastbuf *out)
+{
+  table_start(tbl, out);
+
+  struct fastbuf *colfb = table_col_fbstart(tbl, test_col0_str);
+  bputs(colfb, "HELLO");
+  bprintf(colfb, ",col_idx:%d", test_col0_str);
+  table_col_fbend(tbl);
+
+  table_col_int(tbl, test_col1_int, -10);
+  table_col_uint(tbl, test_col2_uint, 10);
+  table_col_bool(tbl, test_col3_bool, 0);
+  table_col_double(tbl, test_col4_double, 3.1415926535897);
+  table_end_row(tbl);
+
+
+
+  colfb = table_col_fbstart(tbl, test_col0_str);
+  bputs(colfb, "EHLO");
+  bprintf(colfb, ",col_idx:%d", test_col0_str);
+  table_col_fbend(tbl);
+
+  table_col_int(tbl, test_col1_int, -12345);
+  table_col_uint(tbl, test_col2_uint, 0xFF);
+  table_col_bool(tbl, test_col3_bool, 1);
+  table_col_double(tbl, test_col4_double, 1.61803398875);
+  table_end_row(tbl);
+
+
+
+  colfb = table_col_fbstart(tbl, test_col0_str);
+  bputs(colfb, "AHOJ");
+  bprintf(colfb, ",col_idx:%d", test_col0_str);
+  table_col_fbend(tbl);
+
+  table_col_int(tbl, test_col1_int, -54321);
+  table_col_uint(tbl, test_col2_uint, 0xFF00);
+  table_col_bool(tbl, test_col3_bool, 0);
+  table_col_double(tbl, test_col4_double, 2.718281828459045);
+  table_end_row(tbl);
+
+  table_end(tbl);
+}
+
+
+int main(int argc UNUSED, char **argv)
+{
+  struct fastbuf *out;
+  out = bfdopen_shared(1, 4096);
+
+  table_init(&test_tbl);
+  process_command_line_opts(argv, &test_tbl);
+
+  //bprintf(out, "width: %X; masked: %d; mask: %X\n", test_tbl.columns[0].width, test_tbl.columns[0].width & CELL_ALIGN_MASK, CELL_ALIGN_MASK);
+  //bflush(out);
+
+  print_table(&test_tbl, out);
+  table_cleanup(&test_tbl);
+  bclose(out);
+
+  return 0;
+}
+
diff --git a/ucw/table-test-align.t b/ucw/table-test-align.t
new file mode 100644 (file)
index 0000000..ae50de6
--- /dev/null
@@ -0,0 +1,7 @@
+Run: ../obj/ucw/table-test-align
+Out <<EOF
+col0_str                       col1_int        col2_uint       col3_bool       col4_double
+HELLO,col_idx:0                     -10               10       false           3.14159    
+EHLO,col_idx:0                   -12345              255       true            1.61803    
+AHOJ,col_idx:0                   -54321            65280       false           2.71828    
+EOF
index 75981633e261e279edfa8810e3c4e6c2a779c4c5..41e7884942f4145d0f62b33298d39598c052b85a 100644 (file)
@@ -89,17 +89,22 @@ static void do_print1(struct table *test_tbl)
 }
 
 static char **cli_table_opts;
-static int test_default_column_order;
-static int test_invalid_option;
-static int test_invalid_order;
+
+enum test_type_t {
+  TEST_DEFAULT_COLUMN_ORDER = 1,
+  TEST_INVALID_OPTION = 2,
+  TEST_INVALID_ORDER = 3
+};
+
+static int test_to_perform = -1;
 
 static struct opt_section table_printer_opts = {
   OPT_ITEMS {
     OPT_HELP("Options:"),
     OPT_STRING_MULTIPLE('T', "table", cli_table_opts, OPT_REQUIRED_VALUE, "\tSets options for the table."),
-    OPT_BOOL('d', 0, test_default_column_order, 0, "\tRun the test that uses the default column order."),
-    OPT_BOOL('i', 0, test_invalid_option, 0, "\tTest the output for invalid option."),
-    OPT_BOOL('n', 0, test_invalid_order, 0, "\tTest the output for invalid names of columns for the 'cols' option."),
+    OPT_SWITCH('d', 0, test_to_perform, TEST_DEFAULT_COLUMN_ORDER, OPT_SINGLE, "\tRun the test that uses the default column order."),
+    OPT_SWITCH('i', 0, test_to_perform, TEST_INVALID_OPTION, OPT_SINGLE, "\tTest the output for invalid option."),
+    OPT_SWITCH('n', 0, test_to_perform, TEST_INVALID_ORDER, OPT_SINGLE, "\tTest the output for invalid names of columns for the 'cols' option."),
     OPT_END
   }
 };
@@ -109,11 +114,7 @@ static void process_command_line_opts(char *argv[], struct table *tbl)
   GARY_INIT(cli_table_opts, 0);
 
   opt_parse(&table_printer_opts, argv+1);
-
-  for(uint i = 0; i < GARY_SIZE(cli_table_opts); i++) {
-    const char *rv = table_set_option(tbl, cli_table_opts[i]);
-    ASSERT_MSG(rv == NULL, "Tableprinter option parser returned error: '%s'.", rv);
-  }
+  table_set_gary_options(tbl, cli_table_opts);
 
   GARY_FREE(cli_table_opts);
 }
@@ -157,19 +158,21 @@ int main(int argc UNUSED, char **argv)
 
   process_command_line_opts(argv, &test_tbl);
 
-  if(test_invalid_order == 1) {
-    const char *rv = table_set_option(&test_tbl, "cols:test_col0_str,test_col1_int,xxx");
+  const char *rv = NULL;
+  switch(test_to_perform) {
+  case TEST_INVALID_ORDER:
+    rv = table_set_option(&test_tbl, "cols:test_col0_str,test_col1_int,xxx");
     if(rv) printf("Tableprinter option parser returned: '%s'.\n", rv);
     return 0;
-  } else if(test_default_column_order == 1) {
+  case TEST_DEFAULT_COLUMN_ORDER:
     do_default_order_test(out);
     bclose(out);
     return 0;
-  } else if(test_invalid_option == 1) {
+  case TEST_INVALID_OPTION:
     test_option_parser(&test_tbl);
     bclose(out);
     return 0;
-  }
+  };
 
   table_start(&test_tbl, out);
   do_print1(&test_tbl);
index 06c182f1e90d95822c9f842992d17ac3b9e28adc..fa3022860b8edeee96f551350a5303c0d09e5b47 100644 (file)
@@ -411,10 +411,11 @@ static void table_row_human_readable(struct table *tbl)
 {
   for(uint i = 0; i < tbl->cols_to_output; i++) {
     int col_idx = tbl->column_order[i];
-    int col_width = tbl->columns[col_idx].width;
     if(i) {
       bputs(tbl->out, tbl->col_delimiter);
     }
+    int col_width = tbl->columns[col_idx].width & CELL_ALIGN_MASK;
+    if(tbl->columns[col_idx].width & CELL_ALIGN_LEFT) col_width = -1 * col_width;
     bprintf(tbl->out, "%*s", col_width, tbl->col_str_ptrs[col_idx]);
   }
   bputc(tbl->out, '\n');
@@ -427,7 +428,9 @@ static void table_write_header(struct table *tbl)
     if(i) {
       bputs(tbl->out, tbl->col_delimiter);
     }
-    bprintf(tbl->out, "%*s", tbl->columns[col_idx].width, tbl->columns[col_idx].name);
+    int col_width = tbl->columns[col_idx].width & CELL_ALIGN_MASK;
+    if(tbl->columns[col_idx].width & CELL_ALIGN_LEFT) col_width = -1 * col_width;
+    bprintf(tbl->out, "%*s", col_width, tbl->columns[col_idx].name);
   }
   bputc(tbl->out, '\n');
 }
index f191c8bbf239296d43a56910687407a6a439f265..2a4d38940433c2475c12f09b0889ee878c162e31 100644 (file)
@@ -22,6 +22,17 @@ enum column_type {
   COL_TYPE_LAST
 };
 
+#define CELL_ALIGN_LEFT     (1<<(sizeof(enum column_type)*8 - 1))
+// FIXME: an example of another flag, not implemented now
+#define CELL_ALIGN_CENTER   (1<<(sizeof(enum column_type)*8 - 2))
+#define CELL_ALIGN_FLOAT    (1<<(sizeof(enum column_type)*8 - 3))
+
+// CELL_ALIGN_MASK is a mask which has 1's on positions used by some alignment mask.
+// that is: col_width & CELL_ALIGN_MASK  gives column width (in characters).
+// the top bit is reserved for left alignment and is not demasked by CELL_ALIGN_MASK.
+// the reason is that all printf and friends are using negative number for left alignment.
+#define CELL_ALIGN_MASK     (~(CELL_ALIGN_LEFT | CELL_ALIGN_FLOAT | CELL_ALIGN_CENTER))
+
 #define TBL_COL_STR(_name, _width)            { .name = _name, .width = _width, .fmt = "%s", .type = COL_TYPE_STR }
 #define TBL_COL_INT(_name, _width)            { .name = _name, .width = _width, .fmt = "%d", .type = COL_TYPE_INT }
 #define TBL_COL_UINT(_name, _width)           { .name = _name, .width = _width, .fmt = "%u", .type = COL_TYPE_UINT }
@@ -61,12 +72,12 @@ enum column_type {
  * table_cleanup. After table_cleanup is called it is not possible to further use the struct table.
  * The struct table must be reinitialized.
  *
- * Default behaviour of the table_set_col_* is replacement of already set data. To append, the user
+ * Default behaviour of the table_col_* is replacement of already set data. To append, the user
  * must use table_append_*
  *
  * To summarize:
  * 1) @table_init is called;
- * 2) @table_start is called following by table_set_xxx functions and @table_end.
+ * 2) @table_start is called following by table_col_xxx functions and @table_end.
  *    table_start/table_end forms 1-level parenthesis structure. Some of the table
  *    settings can be changed only between table_init and @table_start or after table_end
  *    is called (but before next table_start.
@@ -88,7 +99,7 @@ enum column_type {
  * that is called in table_end).
  *
  * The table is initialized by defining a table struct using the following macros:
- *  o TBL_START_COLUMNS indicates start of definition of columns
+ *  o TBL_COLUMNS    indicates start of definition of columns
  *  o TBL_COL_XXX    macros specify the column types with some default formatting the column is specified using a column
  *                   name (which should be C identifier) and a prefix.  the column name is the a string with the column
  *                   name. The prefix is used for discriminating between columns from different tables. The column index
@@ -105,21 +116,15 @@ enum column_type {
  * Features:
  * * user supplied callback functions can be used for modifying the output format.
  *
- * Non-tested features:
+ * TODO part/Planned features:
  * * computing statistics of columns via the table_start_callback/table_end_callback.
- *   TODO: is it better to have callback for each cell with the original value supplied by the caller of the table_set_* functions?
- * TODO:
  * * unsupported: (dynamic) alignment of cells which is computed in table_end
  *
  * TODO: table_set_col_fmt: this functin takes the format string and the value. But I'm not able to
  * test whether the format string and the type match !!!
  *
- * TODO: Return value of the parser should be a string allocated on the mempool of the table. But:
- * is the return value really necessary? The error should be show to the user on the terminal
- * (std. out).
- * TODO: all macros prefix TBL_ should be changed to TABLE_ ?
  * TODO: how to print column which is aligned to the left flag for alignment: 1) left; 2) right;
- *       3) decimal point alignment; 4) arbitrary separator alignment
+ *       3) decimal point alignment;
  ***/
 
 struct table;
@@ -177,9 +182,10 @@ void table_init(struct table *tbl);
 void table_cleanup(struct table *tbl);
 
 /**
- * table_start is called before the cells of the table are set. After the table_start is called, the user can
- * call the table_set_* functions. The table_end_row function can be called after the table_start is called
- * (but before the table_end is called)
+ * table_start is called before the cells of the table are set. After the table_start is called, the
+ * user can call the table_col_* or table_append_ functions and cannot call the table_set_*
+ * functions. The table_end_row function can be called after the table_start is called (but before
+ * the table_end is called)
  **/
 void table_start(struct table *tbl, struct fastbuf *out);