/* PSPP - a program for statistical analysis.
- Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+ Copyright (C) 2009, 2010, 2012, 2013 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
#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/table-item.h"
#include "output/table-provider.h"
-#include "gl/error.h"
#include "gl/xalloc.h"
#include "gl/xvasprintf.h"
{
struct output_driver driver;
- char *separator; /* Comma or tab. */
+ char *separator; /* Field separator (usually comma or tab). */
+ int quote; /* Quote character (usually ' or ") or 0. */
+ char *quote_set; /* Characters that force quoting. */
+ bool captions; /* Print table captions? */
+
char *file_name; /* Output file name. */
char *command_name; /* Current command. */
FILE *file; /* Output file. */
{
struct output_driver *d;
struct csv_driver *csv;
+ char *quote;
csv = xzalloc (sizeof *csv);
d = &csv->driver;
output_driver_init (&csv->driver, &csv_driver_class, file_name, 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->captions = parse_boolean (opt (d, o, "captions", "true"));
csv->file_name = xstrdup (file_name);
csv->file = fn_open (csv->file_name, "w");
csv->n_items = 0;
if (csv->file == NULL)
{
- error (0, errno, _("error opening output file `%s'"), csv->file_name);
+ msg_error (errno, _("error opening output file `%s'"), csv->file_name);
output_driver_destroy (d);
return NULL;
}
fn_close (csv->file_name, csv->file);
free (csv->separator);
+ free (csv->quote_set);
free (csv->file_name);
free (csv);
}
}
static void
-csv_output_field (FILE *file, const char *field)
+csv_output_field (struct csv_driver *csv, const char *field)
+{
+ while (*field == ' ')
+ field++;
+
+ if (csv->quote && field[strcspn (field, csv->quote_set)])
+ {
+ const char *p;
+
+ putc (csv->quote, csv->file);
+ for (p = field; *p != '\0'; p++)
+ {
+ if (*p == csv->quote)
+ putc (csv->quote, csv->file);
+ putc (*p, csv->file);
+ }
+ putc (csv->quote, csv->file);
+ }
+ else
+ fputs (field, csv->file);
+}
+
+static void
+csv_put_field (struct csv_driver *csv, struct string *s, const char *field)
{
while (*field == ' ')
field++;
- if (field[strcspn (field, "\"\n\r,\t")])
+ if (csv->quote && field[strcspn (field, csv->quote_set)])
{
const char *p;
- putc ('"', file);
+ ds_put_byte (s, csv->quote);
for (p = field; *p != '\0'; p++)
{
- if (*p == '"')
- putc ('"', file);
- putc (*p, file);
+ if (*p == csv->quote)
+ ds_put_byte (s, csv->quote);
+ ds_put_byte (s, *p);
}
- putc ('"', file);
+ ds_put_byte (s, csv->quote);
}
else
- fputs (field, file);
+ ds_put_cstr (s, field);
+}
+
+static void
+csv_output_subtable (struct csv_driver *csv, struct string *s,
+ const struct table *t)
+{
+ int y, x;
+
+ for (y = 0; y < table_nr (t); y++)
+ {
+ if (y > 0)
+ ds_put_byte (s, '\n');
+
+ for (x = 0; x < table_nc (t); x++)
+ {
+ struct table_cell cell;
+
+ table_get_cell (t, x, y, &cell);
+
+ if (x > 0)
+ ds_put_cstr (s, csv->separator);
+
+ 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);
+ 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);
+ }
+ }
}
static void
-csv_output_field_format (FILE *file, const char *format, ...)
+csv_output_field_format (struct csv_driver *csv, const char *format, ...)
PRINTF_FORMAT (2, 3);
static void
-csv_output_field_format (FILE *file, const char *format, ...)
+csv_output_field_format (struct csv_driver *csv, const char *format, ...)
{
va_list args;
char *s;
s = xvasprintf (format, args);
va_end (args);
- csv_output_field (file, s);
+ csv_output_field (csv, s);
free (s);
}
csv_put_separator (csv);
- if (caption != NULL)
+ if (csv->captions && caption != NULL)
{
- csv_output_field_format (csv->file, "Table: %s", caption);
+ csv_output_field_format (csv, "Table: %s", caption);
putc ('\n', csv->file);
}
fputs (csv->separator, csv->file);
if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0])
- csv_output_field (csv->file, "");
+ csv_output_field (csv, "");
+ else if (cell.n_contents == 1 && cell.contents[0].text != NULL)
+ csv_output_field (csv, cell.contents[0].text);
else
- csv_output_field (csv->file, cell.contents);
+ {
+ struct string s;
+ size_t i;
+
+ ds_init_empty (&s);
+ for (i = 0; i < cell.n_contents; i++)
+ {
+ if (i > 0)
+ ds_put_cstr (&s, "\n\n");
+
+ if (cell.contents[i].text != NULL)
+ ds_put_cstr (&s, cell.contents[i].text);
+ else
+ csv_output_subtable (csv, &s, cell.contents[i].table);
+ }
+ csv_output_field (csv, ds_cstr (&s));
+ ds_destroy (&s);
+ }
table_cell_free (&cell);
}
switch (type)
{
case TEXT_ITEM_TITLE:
- csv_output_field_format (csv->file, "Title: %s", text);
+ csv_output_field_format (csv, "Title: %s", text);
break;
case TEXT_ITEM_SUBTITLE:
- csv_output_field_format (csv->file, "Subtitle: %s", text);
+ csv_output_field_format (csv, "Subtitle: %s", text);
break;
default:
- csv_output_field (csv->file, text);
+ csv_output_field (csv, text);
break;
}
putc ('\n', csv->file);
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->file, s);
+ csv_output_field (csv, s);
free (s);
putc ('\n', csv->file);
}
}
-struct output_driver_factory csv_driver_factory = { "csv", csv_create };
+struct output_driver_factory csv_driver_factory = { "csv", "-", csv_create };
static const struct output_driver_class csv_driver_class =
{