+static void equalize_fields(void)
+{
+ while (fields_count(&out_fields) < intarray_count(&column_widths)) {
+ struct field *f = fields_push(&out_fields);
+ f->start_pos = f->len = 0;
+ }
+}
+
+/*** Field names and headers ***/
+
+struct field_names {
+ stringarray_t names;
+};
+
+static void add_field(struct field_names *fn, char *name, int namelen)
+{
+ char *n = xmalloc(namelen + 1);
+ memcpy(n, name, namelen);
+ n[namelen] = 0;
+ *stringarray_push(&fn->names) = n;
+}
+
+static void add_field_names(struct field_names *fn, char *names)
+{
+ char *p = names;
+ while (p) {
+ char *q = strchr(p, ',');
+ int len = q ? q-p : (int) strlen(p);
+ add_field(fn, p, len);
+ p = q ? q+1 : NULL;
+ }
+}
+
+static void read_header(void)
+{
+ if (!(in_format->has_header || in_format->set_field_names))
+ return;
+
+ struct field_names *fn = xmalloc_zero(sizeof(*fn));
+ in_format->field_names = fn;
+
+ if (in_format->has_header) {
+ if (!read_line())
+ die("Missing input header");
+ }
+
+ if (in_format->set_field_names) {
+ add_field_names(fn, in_format->set_field_names);
+ } else {
+ for (int i = 0; i < fields_count(&in_fields); i++) {
+ int len;
+ char *s = (char *) get_field(&in_fields, i, &len);
+ add_field(fn, s, len);
+ }
+ }
+}
+
+static void write_header(void)
+{
+ if (!out_format->has_header) {
+ write_grid(-1);
+ return;
+ }
+
+ int want_select_fields = 0;
+ if (out_format->set_field_names) {
+ struct field_names *fn = xmalloc_zero(sizeof(*fn));
+ out_format->field_names = fn;
+ add_field_names(fn, out_format->set_field_names);
+ } else if (in_format->field_names) {
+ out_format->field_names = in_format->field_names;
+ want_select_fields = 1;
+ } else
+ die("Output header requested, but no field names specified");
+
+ line_reset(&in_line);
+ fields_reset(&in_fields);
+ struct field_names *fn = out_format->field_names;
+ for (int i = 0; i < stringarray_count(&fn->names); i++) {
+ struct field *f = fields_push(&in_fields);
+ f->start_pos = line_count(&in_line);
+ f->len = 0;
+ char *s = *stringarray_nth(&fn->names, i);
+ while (*s) {
+ *line_push(&in_line) = *s++;
+ f->len++;
+ }
+ }
+
+ fields_reset(&out_fields);
+ if (want_select_fields)
+ select_fields();
+ else
+ select_all_fields();
+
+ // 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.
+ want_stats++;
+ update_stats();
+ want_stats--;
+ if (want_equalize)
+ equalize_fields();
+ 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)
+{
+ for (int i = 0; i < stringarray_count(&fn->names); i++)
+ if (!strcmp(*stringarray_nth(&fn->names, i), name))
+ return i + 1;
+ return -1;
+}
+