X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcsv.c;h=1c6662793fc3fafd5c40f83d3a668f700e32bc8d;hb=5805214e9bb9e2a4705ada8cdf5f5d0f1814db0e;hp=9b51ed79d34de5e959876367bb0ca5bae59d3217;hpb=fce028c380d496e42823fd24774e0159ed7cc110;p=pspp diff --git a/src/output/csv.c b/src/output/csv.c index 9b51ed79d3..1c6662793f 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2009, 2010, 2012 Free Software Foundation, Inc. + Copyright (C) 2009, 2010, 2012, 2013, 2014 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,9 +20,11 @@ #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" @@ -31,6 +33,7 @@ #include "output/table-item.h" #include "output/table-provider.h" +#include "gl/minmax.h" #include "gl/xalloc.h" #include "gl/xvasprintf.h" @@ -45,10 +48,10 @@ struct csv_driver char *separator; /* Field separator (usually comma or tab). */ int quote; /* Quote character (usually ' or ") or 0. */ char *quote_set; /* Characters that force quoting. */ + 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. */ }; @@ -70,7 +73,7 @@ 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; @@ -79,21 +82,22 @@ csv_create (const char *file_name, enum settings_output_devices device_type, csv = xzalloc (sizeof *csv); 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", "\"")); csv->quote = quote[0]; free (quote); 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; } @@ -107,11 +111,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); } @@ -124,51 +128,72 @@ 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 -csv_output_field_format (struct csv_driver *csv, const char *format, ...) - PRINTF_FORMAT (2, 3); +csv_output_field (struct csv_driver *csv, const char *field) +{ + csv_output_field__ (csv, ss_cstr (field)); +} static void -csv_output_field_format (struct csv_driver *csv, const char *format, ...) +csv_put_separator (struct csv_driver *csv) { - va_list args; - char *s; + if (csv->n_items++ > 0) + putc ('\n', csv->file); +} - va_start (args, format); - s = xvasprintf (format, args); - va_end (args); +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)) + { + csv_output_field__ (csv, line); + putc ('\n', csv->file); + } +} - csv_output_field (csv, s); - free (s); +static void +csv_format_footnotes (const struct footnote **f, size_t n, struct string *s) +{ + for (size_t i = 0; i < n; i++) + ds_put_format (s, "[%s]", f[i]->marker); } static void -csv_put_separator (struct csv_driver *csv) +csv_output_table_item_text (struct csv_driver *csv, + const struct table_item_text *text, + const char *leader) { - if (csv->n_items++ > 0) - putc ('\n', csv->file); + if (!text) + return; + + struct string s = DS_EMPTY_INITIALIZER; + ds_put_format (&s, "%s: %s", leader, text->content); + csv_format_footnotes (text->footnotes, text->n_footnotes, &s); + csv_output_field (csv, ds_cstr (&s)); + ds_destroy (&s); + putc ('\n', csv->file); } static void @@ -177,22 +202,17 @@ csv_submit (struct output_driver *driver, { struct csv_driver *csv = csv_driver_cast (driver); - output_driver_track_current_command (output_item, &csv->command_name); - if (is_table_item (output_item)) { struct table_item *table_item = to_table_item (output_item); - const char *caption = table_item_get_caption (table_item); const struct table *t = table_item_get_table (table_item); int x, y; csv_put_separator (csv); - if (csv->captions && caption != NULL) - { - csv_output_field_format (csv, "Table: %s", caption); - putc ('\n', csv->file); - } + if (csv->titles) + csv_output_table_item_text (csv, table_item_get_title (table_item), + "Table"); for (y = 0; y < table_nr (t); y++) { @@ -207,13 +227,56 @@ csv_submit (struct output_driver *driver, if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0]) csv_output_field (csv, ""); + else if (!(cell.options & TAB_MARKUP) && !cell.n_footnotes + && !cell.n_subscripts && !cell.superscript) + csv_output_field (csv, cell.text); else - csv_output_field (csv, cell.contents); - - table_cell_free (&cell); + { + struct string s = DS_EMPTY_INITIALIZER; + + if (cell.options & TAB_MARKUP) + { + char *t = output_get_text_from_markup (cell.text); + ds_put_cstr (&s, t); + free (t); + } + else + ds_put_cstr (&s, cell.text); + + if (cell.n_subscripts) + for (size_t i = 0; i < cell.n_subscripts; i++) + ds_put_format (&s, "%c%s", + i ? ',' : '_', cell.subscripts[i]); + if (cell.superscript) + ds_put_format (&s, "^%s", cell.superscript); + csv_format_footnotes (cell.footnotes, cell.n_footnotes, &s); + csv_output_field (csv, ds_cstr (&s)); + ds_destroy (&s); + } } putc ('\n', csv->file); } + + if (csv->captions) + csv_output_table_item_text (csv, table_item_get_caption (table_item), + "Caption"); + + const struct footnote **f; + size_t n_footnotes = table_collect_footnotes (table_item, &f); + if (n_footnotes) + { + fputs ("\nFootnotes:\n", csv->file); + + for (size_t i = 0; i < n_footnotes; i++) + { + csv_output_field (csv, f[i]->marker); + fputs (csv->separator, csv->file); + csv_output_field (csv, f[i]->content); + putc ('\n', csv->file); + } + + free (f); + } } else if (is_text_item (output_item)) { @@ -221,32 +284,24 @@ csv_submit (struct output_driver *driver, enum text_item_type type = text_item_get_type (text_item); const char *text = text_item_get_text (text_item); - if (type == TEXT_ITEM_COMMAND_OPEN || type == TEXT_ITEM_COMMAND_CLOSE - || type == TEXT_ITEM_SYNTAX) + if (type == TEXT_ITEM_SYNTAX || type == 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; + if (text_item->markup) + { + char *plain_text = output_get_text_from_markup (text); + csv_output_lines (csv, plain_text); + free (plain_text); } - putc ('\n', csv->file); + else + csv_output_lines (csv, text); } 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); + char *s = msg_to_string (message_item_get_msg (message_item)); csv_put_separator (csv); csv_output_field (csv, s); free (s);