X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcsv.c;h=ba1481c9a0c44caf9ab2de9873b67aa2f49dd604;hb=c1b1583b96cc05a2bf9f3f6d01bbfa063fafb253;hp=cf175458716b29b952f4de58c2546bce0a176870;hpb=8dd405d0900f29f69e54726a79045c119146de89;p=pspp diff --git a/src/output/csv.c b/src/output/csv.c index cf17545871..ba1481c9a0 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -20,18 +20,20 @@ #include #include "data/file-name.h" +#include "data/file-handle-def.h" #include "libpspp/assertion.h" #include "libpspp/compiler.h" #include "libpspp/message.h" #include "libpspp/str.h" #include "libpspp/string-map.h" -#include "output/text-item.h" #include "output/driver-provider.h" #include "output/options.h" -#include "output/message-item.h" -#include "output/table-item.h" +#include "output/output-item.h" +#include "output/pivot-output.h" +#include "output/pivot-table.h" #include "output/table-provider.h" +#include "gl/minmax.h" #include "gl/xalloc.h" #include "gl/xvasprintf.h" @@ -49,8 +51,7 @@ struct csv_driver bool titles; /* Print table titles? */ bool captions; /* Print table captions? */ - char *file_name; /* Output file name. */ - char *command_name; /* Current command. */ + struct file_handle *handle; FILE *file; /* Output file. */ int n_items; /* Number of items output so far. */ }; @@ -72,16 +73,15 @@ opt (struct output_driver *d, struct string_map *options, const char *key, } static struct output_driver * -csv_create (const char *file_name, enum settings_output_devices device_type, +csv_create (struct file_handle *fh, enum settings_output_devices device_type, struct string_map *o) { struct output_driver *d; - struct csv_driver *csv; char *quote; - csv = xzalloc (sizeof *csv); + struct csv_driver *csv = XZALLOC (struct csv_driver); d = &csv->driver; - output_driver_init (&csv->driver, &csv_driver_class, file_name, device_type); + output_driver_init (&csv->driver, &csv_driver_class, fh_get_file_name (fh), device_type); csv->separator = parse_string (opt (d, o, "separator", ",")); quote = parse_string (opt (d, o, "quote", "\"")); @@ -90,13 +90,13 @@ csv_create (const char *file_name, enum settings_output_devices device_type, csv->quote_set = xasprintf ("\n\r\t%s%c", csv->separator, csv->quote); csv->titles = parse_boolean (opt (d, o, "titles", "true")); csv->captions = parse_boolean (opt (d, o, "captions", "true")); - csv->file_name = xstrdup (file_name); - csv->file = fn_open (csv->file_name, "w"); + csv->handle = fh; + csv->file = fn_open (fh, "w"); csv->n_items = 0; if (csv->file == NULL) { - msg_error (errno, _("error opening output file `%s'"), csv->file_name); + msg_error (errno, _("error opening output file `%s'"), fh_get_file_name (fh)); output_driver_destroy (d); return NULL; } @@ -110,11 +110,11 @@ csv_destroy (struct output_driver *driver) struct csv_driver *csv = csv_driver_cast (driver); if (csv->file != NULL) - fn_close (csv->file_name, csv->file); + fn_close (csv->handle, csv->file); free (csv->separator); free (csv->quote_set); - free (csv->file_name); + fh_unref (csv->handle); free (csv); } @@ -127,295 +127,161 @@ csv_flush (struct output_driver *driver) } static void -csv_output_field (struct csv_driver *csv, const char *field) +csv_output_field__ (struct csv_driver *csv, struct substring field) { - while (*field == ' ') - field++; + ss_ltrim (&field, ss_cstr (" ")); - if (csv->quote && field[strcspn (field, csv->quote_set)]) + if (csv->quote && ss_cspan (field, ss_cstr (csv->quote_set)) < field.length) { - const char *p; - putc (csv->quote, csv->file); - for (p = field; *p != '\0'; p++) + for (size_t i = 0; i < field.length; i++) { - if (*p == csv->quote) + if (field.string[i] == csv->quote) putc (csv->quote, csv->file); - putc (*p, csv->file); + putc (field.string[i], csv->file); } putc (csv->quote, csv->file); } else - fputs (field, csv->file); + fwrite (field.string, field.length, 1, csv->file); } -static void PRINTF_FORMAT (2, 3) -csv_output_field_format (struct csv_driver *csv, const char *format, ...) +static void +csv_output_field (struct csv_driver *csv, const char *field) { - va_list args; - char *s; - - va_start (args, format); - s = xvasprintf (format, args); - va_end (args); - - csv_output_field (csv, s); - free (s); + csv_output_field__ (csv, ss_cstr (field)); } static void -csv_put_field (struct csv_driver *csv, struct string *s, const char *field) +csv_put_separator (struct csv_driver *csv) { - while (*field == ' ') - field++; + if (csv->n_items++ > 0) + putc ('\n', csv->file); +} - if (csv->quote && field[strcspn (field, csv->quote_set)]) +static void +csv_output_lines (struct csv_driver *csv, const char *text_) +{ + struct substring text = ss_cstr (text_); + struct substring line; + size_t save_idx = 0; + while (ss_separate (text, ss_cstr ("\n"), &save_idx, &line)) { - const char *p; - - ds_put_byte (s, csv->quote); - for (p = field; *p != '\0'; p++) - { - if (*p == csv->quote) - ds_put_byte (s, csv->quote); - ds_put_byte (s, *p); - } - ds_put_byte (s, csv->quote); + csv_output_field__ (csv, line); + putc ('\n', csv->file); } - else - ds_put_cstr (s, field); } static void -csv_output_subtable (struct csv_driver *csv, struct string *s, - const struct table_item *item) +csv_output_table_cell (struct csv_driver *csv, const struct pivot_table *pt, + const struct table_cell *cell, const char *leader) { - const struct table *t = table_item_get_table (item); - const char *title = table_item_get_title (item); - const char *caption = table_item_get_caption (item); - int y, x; + struct string s = DS_EMPTY_INITIALIZER; + if (leader) + ds_put_format (&s, "%s: ", leader); + pivot_value_format (cell->value, pt, &s); + csv_output_field (csv, ds_cstr (&s)); + ds_destroy (&s); +} - if (csv->titles && title != NULL) - { - csv_output_field_format (csv, "Table: %s", title); - putc ('\n', csv->file); - } +static void +csv_output_table__ (struct csv_driver *csv, const struct pivot_table *pt, + const struct table *t, const char *leader) +{ + if (!t) + return; - for (y = 0; y < table_nr (t); y++) + for (int y = 0; y < t->n[TABLE_VERT]; y++) { - if (y > 0) - ds_put_byte (s, '\n'); - - for (x = 0; x < table_nc (t); x++) + for (int x = 0; x < t->n[TABLE_HORZ]; x++) { struct table_cell cell; table_get_cell (t, x, y, &cell); if (x > 0) - ds_put_cstr (s, csv->separator); + fputs (csv->separator, csv->file); if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0]) - csv_put_field (csv, s, ""); - else if (cell.n_contents == 1 && cell.contents[0].text != NULL) - csv_put_field (csv, s, cell.contents[0].text); + csv_output_field (csv, ""); else - { - struct string s2; - size_t i; - - ds_init_empty (&s2); - for (i = 0; i < cell.n_contents; i++) - { - if (i > 0) - ds_put_cstr (&s2, "\n\n"); - - if (cell.contents[i].text != NULL) - ds_put_cstr (&s2, cell.contents[i].text); - else - csv_output_subtable (csv, &s2, cell.contents[i].table); - } - csv_put_field (csv, s, ds_cstr (&s2)); - ds_destroy (&s2); - } - - table_cell_free (&cell); + csv_output_table_cell (csv, pt, &cell, !x ? leader : NULL); } - } - - if (csv->captions && caption != NULL) - { - csv_output_field_format (csv, "Caption: %s", caption); putc ('\n', csv->file); } } static void -csv_put_separator (struct csv_driver *csv) +csv_output_table_layer (struct csv_driver *csv, const struct pivot_table *pt, + const size_t *layer_indexes) { - if (csv->n_items++ > 0) - putc ('\n', csv->file); + struct table *title, *layers, *body, *caption, *footnotes; + pivot_output (pt, layer_indexes, true, &title, &layers, &body, + &caption, &footnotes, NULL, NULL); + + csv_put_separator (csv); + csv_output_table__ (csv, pt, title, "Table"); + csv_output_table__ (csv, pt, layers, "Layer"); + csv_output_table__ (csv, pt, body, NULL); + csv_output_table__ (csv, pt, caption, "Caption"); + csv_output_table__ (csv, pt, footnotes, "Footnote"); + + table_unref (title); + table_unref (layers); + table_unref (body); + table_unref (caption); + table_unref (footnotes); } static void csv_submit (struct output_driver *driver, - const struct output_item *output_item) + const struct output_item *item) { struct csv_driver *csv = csv_driver_cast (driver); - output_driver_track_current_command (output_item, &csv->command_name); - - if (is_table_item (output_item)) + switch (item->type) { - struct table_item *table_item = to_table_item (output_item); - const char *title = table_item_get_title (table_item); - const char *caption = table_item_get_caption (table_item); - const struct table *t = table_item_get_table (table_item); - int footnote_idx; - int x, y; - - csv_put_separator (csv); + case OUTPUT_ITEM_CHART: + break; - if (csv->titles && title != NULL) - { - csv_output_field_format (csv, "Table: %s", title); - putc ('\n', csv->file); - } + case OUTPUT_ITEM_GROUP: + break; - footnote_idx = 0; - for (y = 0; y < table_nr (t); y++) - { - for (x = 0; x < table_nc (t); x++) - { - struct table_cell cell; - - table_get_cell (t, x, y, &cell); - - if (x > 0) - fputs (csv->separator, csv->file); - - if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0]) - csv_output_field (csv, ""); - else if (cell.n_contents == 1 - && cell.contents[0].text != NULL - && cell.contents[0].n_footnotes == 0) - csv_output_field (csv, cell.contents[0].text); - else - { - struct string s; - size_t i; - - ds_init_empty (&s); - for (i = 0; i < cell.n_contents; i++) - { - const struct cell_contents *c = &cell.contents[i]; - int j; - - if (i > 0) - ds_put_cstr (&s, "\n\n"); - - if (c->text != NULL) - ds_put_cstr (&s, c->text); - else - csv_output_subtable (csv, &s, c->table); - - for (j = 0; j < c->n_footnotes; j++) - { - char marker[16]; - - str_format_26adic (++footnote_idx, false, - marker, sizeof marker); - ds_put_format (&s, "[%s]", marker); - } - } - csv_output_field (csv, ds_cstr (&s)); - ds_destroy (&s); - } - - table_cell_free (&cell); - } - putc ('\n', csv->file); - } + case OUTPUT_ITEM_IMAGE: + break; - if (csv->captions && caption != NULL) - { - csv_output_field_format (csv, "Caption: %s", caption); - putc ('\n', csv->file); - } - - if (footnote_idx) - { - size_t i; - - fputs ("\nFootnotes:\n", csv->file); - - footnote_idx = 0; - for (y = 0; y < table_nr (t); y++) - { - struct table_cell cell; - for (x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1]) - { - table_get_cell (t, x, y, &cell); - - if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0]) - for (i = 0; i < cell.n_contents; i++) - { - const struct cell_contents *c = &cell.contents[i]; - int j; - - for (j = 0; j < c->n_footnotes; j++) - { - char marker[16]; - - str_format_26adic (++footnote_idx, false, - marker, sizeof marker); - csv_output_field (csv, marker); - fputs (csv->separator, csv->file); - csv_output_field (csv, c->footnotes[j]); - putc ('\n', csv->file); - } - } - table_cell_free (&cell); - } - } - } - } - else if (is_text_item (output_item)) - { - const struct text_item *text_item = to_text_item (output_item); - enum text_item_type type = text_item_get_type (text_item); - const char *text = text_item_get_text (text_item); + case OUTPUT_ITEM_MESSAGE: + csv_put_separator (csv); + char *s = msg_to_string (item->message); + csv_output_field (csv, s); + free (s); + putc ('\n', csv->file); + break; - if (type == TEXT_ITEM_COMMAND_OPEN || type == TEXT_ITEM_COMMAND_CLOSE - || type == TEXT_ITEM_SYNTAX) + case OUTPUT_ITEM_PAGE_BREAK: + csv_put_separator (csv); + csv_output_lines (csv, ""); + break; + + case OUTPUT_ITEM_TABLE: + { + size_t *layer_indexes; + PIVOT_OUTPUT_FOR_EACH_LAYER (layer_indexes, item->table, true) + csv_output_table_layer (csv, item->table, layer_indexes); + } + break; + + case OUTPUT_ITEM_TEXT: + if (item->text.subtype == TEXT_ITEM_SYNTAX + || item->text.subtype == TEXT_ITEM_PAGE_TITLE) return; csv_put_separator (csv); - switch (type) - { - case TEXT_ITEM_TITLE: - csv_output_field_format (csv, "Title: %s", text); - break; - - case TEXT_ITEM_SUBTITLE: - csv_output_field_format (csv, "Subtitle: %s", text); - break; - default: - csv_output_field (csv, text); - break; - } - putc ('\n', csv->file); - } - else if (is_message_item (output_item)) - { - const struct message_item *message_item = to_message_item (output_item); - const struct msg *msg = message_item_get_msg (message_item); - char *s = msg_to_string (msg, csv->command_name); - csv_put_separator (csv); - csv_output_field (csv, s); - free (s); - putc ('\n', csv->file); + char *text = text_item_get_plain_text (item); + csv_output_lines (csv, text); + free (text); + break; } } @@ -423,8 +289,8 @@ struct output_driver_factory csv_driver_factory = { "csv", "-", csv_create }; static const struct output_driver_class csv_driver_class = { - "csv", - csv_destroy, - csv_submit, - csv_flush, + .name = "csv", + .destroy = csv_destroy, + .submit = csv_submit, + .flush = csv_flush, };