#ifdef __GNUC__
#define NONRET __attribute__((noreturn))
+#define UNUSED __attribute__((unused))
#else
#define NONRET
+#define UNUSED
#endif
/*** General functions ***/
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
// Table backend:
int table_sep;
+ int table_grid;
};
static struct format *in_format, *out_format;
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);
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');
}
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));
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)
static void one_pass(int pass)
{
+ if (pass & 2)
+ write_header();
+
for (;;) {
line_number++;
if (!read_line())
write_line();
}
+
+ if (pass & 2)
+ write_footer();
}
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
-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\
OPT_ALWAYS_QUOTE,
OPT_TABLE,
OPT_TABLE_SEP,
+ OPT_GRID,
};
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' },
break;
case FORM_TABLE:
f->write_line = table_write;
+ f->write_grid = table_write_grid;
f->needs_stats = 1;
f->table_sep = 2;
break;
case OPT_TABLE_SEP:
current_format()->table_sep = atoi(optarg);
break;
+ case OPT_GRID:
+ current_format()->table_grid = 1;
+ break;
default:
bad_args(NULL);
}
}
finish_parse_selectors();
- write_header();
if (out_format->needs_stats)
two_pass();
else