]> mj.ucw.cz Git - xsv.git/commitdiff
Implemented --grid and fixed bugs in headers in 2-pass mode
authorMartin Mares <mj@ucw.cz>
Tue, 24 Jul 2012 13:56:34 +0000 (15:56 +0200)
committerMartin Mares <mj@ucw.cz>
Tue, 24 Jul 2012 13:56:34 +0000 (15:56 +0200)
xsv.c

diff --git a/xsv.c b/xsv.c
index 31a13abc00482d571e3e30d9b4b9252672f2c81a..872090f02abdb73fa19675cd8e8902895a89fb3a 100644 (file)
--- a/xsv.c
+++ b/xsv.c
 
 #ifdef __GNUC__
 #define NONRET __attribute__((noreturn))
+#define UNUSED __attribute__((unused))
 #else
 #define NONRET
+#define UNUSED
 #endif
 
 /*** General functions ***/
@@ -99,6 +101,7 @@ struct format {
        int quiet;
        int (*read_line)(struct format *fmt);
        void (*write_line)(struct format *fmt);
+       void (*write_grid)(struct format *fmt, int pos);        // -1=above, 1=below, 0=after header
        int needs_stats;
 
        // Field names
@@ -121,6 +124,7 @@ struct format {
 
        // Table backend:
        int table_sep;
+       int table_grid;
 };
 
 static struct format *in_format, *out_format;
@@ -158,6 +162,15 @@ static void write_line(void)
                die("I/O error when writing standard input");
 }
 
+static void write_grid(int pos)
+{
+       if (out_format->write_grid) {
+               out_format->write_grid(out_format, pos);
+               if (ferror_unlocked(stdout))
+                       die("I/O error when writing standard input");
+       }
+}
+
 static void new_field(int pos)
 {
        in_field = fields_push(&in_fields);
@@ -409,24 +422,52 @@ static int regex_read(struct format *fmt)
 
 static void table_write(struct format *fmt)
 {
-       for (int i = 0; i < fields_count(&out_fields); i++) {
-               if (i)
+       for (int i = 0; i < intarray_count(&column_widths); i++) {
+               if (fmt->table_grid) {
+                       putchar_unlocked('|');
+                       printf("%*s", fmt->table_sep / 2, "");
+               } else if (i)
                        printf("%*s", fmt->table_sep, "");
-               struct field *f = fields_nth(&out_fields, i);
-               int fw = field_chars(f);
+
                int cw = *intarray_nth(&column_widths, i);
-               if (fw > cw) {
-                       warn(fmt, "Internal error: Wrongly calculated column width (%d > %d)", fw, cw);
-                       cw = fw;
+               int fw = 0;
+               if (i < fields_count(&out_fields)) {
+                       int len;
+                       unsigned char *p = get_field(&out_fields, i, &len);
+                       fw = field_chars(fields_nth(&out_fields, i));
+                       if (fw > cw) {
+                               warn(fmt, "Internal error: Wrongly calculated column width (%d > %d)", fw, cw);
+                               cw = fw;
+                       }
+                       while (len--)
+                               putchar(*p++);
                }
-               unsigned char *p = line_nth(&in_line, f->start_pos);
-               for (int j = 0; j < f->len; j++)
-                       putchar_unlocked(p[j]);
                while (fw < cw) {
                        putchar_unlocked(' ');
                        fw++;
                }
+
+               if (fmt->table_grid)
+                       printf("%*s", fmt->table_sep - fmt->table_sep / 2, "");
+       }
+
+       if (fmt->table_grid)
+               putchar_unlocked('|');
+       putchar_unlocked('\n');
+}
+
+static void table_write_grid(struct format *fmt, int pos UNUSED)
+{
+       if (!fmt->table_grid)
+               return;
+
+       for (int i = 0; i < intarray_count(&column_widths); i++) {
+               putchar_unlocked('+');
+               int w = fmt->table_sep + *intarray_nth(&column_widths, i);      // FIXME: Avoid the *
+               while (w--)
+                       putchar('-');
        }
+       putchar_unlocked('+');
        putchar_unlocked('\n');
 }
 
@@ -555,8 +596,10 @@ static void read_header(void)
 
 static void write_header(void)
 {
-       if (!out_format->has_header)
+       if (!out_format->has_header) {
+               write_grid(-1);
                return;
+       }
 
        if (out_format->set_field_names) {
                struct field_names *fn = xmalloc_zero(sizeof(*fn));
@@ -580,7 +623,19 @@ static void write_header(void)
                        f->len++;
                }
        }
+
+       // This is tricky: when we are formatting a table, field names are normally
+       // calculated in pass 1, but the header is written in pass 2, so we have to
+       // update column statistics, because field name can be too wide to fit.
+       update_stats();
+       write_grid(-1);
        write_line();
+       write_grid(0);
+}
+
+static void write_footer(void)
+{
+       write_grid(1);
 }
 
 static int find_field_by_name(struct field_names *fn, char *name)
@@ -683,6 +738,9 @@ static void select_all_fields(void)
 
 static void one_pass(int pass)
 {
+       if (pass & 2)
+               write_header();
+
        for (;;) {
                line_number++;
                if (!read_line())
@@ -702,6 +760,9 @@ static void one_pass(int pass)
 
                write_line();
        }
+
+       if (pass & 2)
+               write_footer();
 }
 
 static void two_pass(void)
@@ -718,6 +779,7 @@ static void two_pass(void)
        out_format->write_line = tmp_write;
        out_format->tmp_file = tmpfile();
        out_format->needs_stats = final_format->needs_stats;
+       out_format->field_names = in_format->field_names;
        one_pass(1);
 
        // Pass 2: Set up reader of intermediate format
@@ -752,6 +814,7 @@ Format parameters:\n\
 -q, --quiet            Do not show warnings\n\
     --always-quote     Put quotes around all fields (CSV output only)\n\
     --table-sep=<n>    Separate table columns by <n> spaces (default: 2)\n\
+    --grid             Separate table columns by grid lines\n\
 \n\
 Other options:\n\
     --trim             Trim leading and trailing whitespaces in fields\n\
@@ -781,6 +844,7 @@ enum long_options {
        OPT_ALWAYS_QUOTE,
        OPT_TABLE,
        OPT_TABLE_SEP,
+       OPT_GRID,
 };
 
 static const struct option long_options[] = {
@@ -788,6 +852,7 @@ static const struct option long_options[] = {
        { "csv",                0,      NULL,   'c' },
        { "fields",             1,      NULL,   'f' },
        { "fs",                 1,      NULL,   'd' },
+       { "grid",               0,      NULL,   OPT_GRID },
        { "header",             0,      NULL,   'h' },
        { "quiet",              0,      NULL,   'q' },
        { "regex",              1,      NULL,   'r' },
@@ -830,6 +895,7 @@ static void set_format(int format_id)
                        break;
                case FORM_TABLE:
                        f->write_line = table_write;
+                       f->write_grid = table_write_grid;
                        f->needs_stats = 1;
                        f->table_sep = 2;
                        break;
@@ -910,6 +976,9 @@ int main(int argc, char **argv)
                        case OPT_TABLE_SEP:
                                current_format()->table_sep = atoi(optarg);
                                break;
+                       case OPT_GRID:
+                               current_format()->table_grid = 1;
+                               break;
                        default:
                                bad_args(NULL);
                }
@@ -930,7 +999,6 @@ int main(int argc, char **argv)
        }
        finish_parse_selectors();
 
-       write_header();
        if (out_format->needs_stats)
                two_pass();
        else