From: Martin Mares Date: Tue, 24 Jul 2012 13:56:34 +0000 (+0200) Subject: Implemented --grid and fixed bugs in headers in 2-pass mode X-Git-Tag: v1.0~28 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=4b57a845372de3b915f195c84311b30e13bfdfc3;p=xsv.git Implemented --grid and fixed bugs in headers in 2-pass mode --- diff --git a/xsv.c b/xsv.c index 31a13ab..872090f 100644 --- a/xsv.c +++ b/xsv.c @@ -18,8 +18,10 @@ #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= Separate table columns by 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