1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "data/csv-file-writer.h"
29 #include "data/calendar.h"
30 #include "data/case.h"
31 #include "data/casewriter-provider.h"
32 #include "data/casewriter.h"
33 #include "data/data-out.h"
34 #include "data/dictionary.h"
35 #include "data/file-handle-def.h"
36 #include "data/file-name.h"
37 #include "data/format.h"
38 #include "data/make-file.h"
39 #include "data/missing-values.h"
40 #include "data/settings.h"
41 #include "data/value-labels.h"
42 #include "data/variable.h"
43 #include "libpspp/assertion.h"
44 #include "libpspp/i18n.h"
45 #include "libpspp/message.h"
46 #include "libpspp/str.h"
48 #include "gl/unlocked-io.h"
49 #include "gl/xalloc.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) (msgid)
55 /* A variable in a CSV file. */
58 int width; /* Variable width (0 to 32767). */
59 int case_index; /* Index into case. */
60 struct fmt_spec format; /* Print format. */
61 struct missing_values missing; /* User-missing values, if recoding. */
62 struct val_labs *val_labs; /* Value labels, if any and they are in use. */
65 /* Comma-separated value (CSV) file writer. */
68 struct file_handle *fh; /* File handle. */
69 struct fh_lock *lock; /* Mutual exclusion for file. */
70 FILE *file; /* File stream. */
71 struct replace_file *rf; /* Ticket for replacing output file. */
73 struct csv_writer_options opts;
75 char *encoding; /* Encoding used by variables. */
78 struct csv_var *csv_vars; /* Variables. */
79 size_t n_csv_vars; /* Number of variables. */
82 static const struct casewriter_class csv_file_casewriter_class;
84 static void write_var_names (struct csv_writer *, const struct dictionary *);
86 static bool write_error (const struct csv_writer *);
87 static bool close_writer (struct csv_writer *);
89 /* Initializes OPTS with default options for writing a CSV file. */
91 csv_writer_options_init (struct csv_writer_options *opts)
93 opts->recode_user_missing = false;
94 opts->include_var_names = false;
95 opts->use_value_labels = false;
96 opts->use_print_formats = false;
97 opts->decimal = settings_get_decimal_char (FMT_F);
99 opts->qualifier = '"';
102 /* Opens the CSV file designated by file handle FH for writing cases from
103 dictionary DICT according to the given OPTS.
105 No reference to D is retained, so it may be modified or
106 destroyed at will after this function returns. */
108 csv_writer_open (struct file_handle *fh, const struct dictionary *dict,
109 const struct csv_writer_options *opts)
111 struct csv_writer *w;
114 /* Create and initialize writer. */
115 w = xmalloc (sizeof *w);
123 w->encoding = (dict_get_encoding (dict)
124 ? xstrdup (dict_get_encoding (dict))
127 w->n_csv_vars = dict_get_var_cnt (dict);
128 w->csv_vars = xnmalloc (w->n_csv_vars, sizeof *w->csv_vars);
129 for (i = 0; i < w->n_csv_vars; i++)
131 const struct variable *var = dict_get_var (dict, i);
132 struct csv_var *cv = &w->csv_vars[i];
134 cv->width = var_get_width (var);
135 cv->case_index = var_get_case_index (var);
137 cv->format = *var_get_print_format (var);
138 if (opts->recode_user_missing)
139 mv_copy (&cv->missing, var_get_missing_values (var));
141 mv_init (&cv->missing, cv->width);
143 if (opts->use_value_labels)
144 cv->val_labs = val_labs_clone (var_get_value_labels (var));
149 /* Open file handle as an exclusive writer. */
150 /* TRANSLATORS: this fragment will be interpolated into messages in fh_lock()
151 that identify types of files. */
152 w->lock = fh_lock (fh, FH_REF_FILE, N_("CSV file"), FH_ACC_WRITE, true);
156 /* Create the file on disk. */
157 w->rf = replace_file_start (fh_get_file_name (fh), "w", 0666,
161 msg (ME, _("Error opening \"%s\" for writing as a system file: %s."),
162 fh_get_file_name (fh), strerror (errno));
166 if (opts->include_var_names)
167 write_var_names (w, dict);
172 return casewriter_create (dict_get_proto (dict),
173 &csv_file_casewriter_class, w);
181 csv_field_needs_quoting (struct csv_writer *w, const char *s, size_t len)
185 for (p = s; p < &s[len]; p++)
186 if (*p == w->opts.qualifier || *p == w->opts.delimiter
187 || *p == '\n' || *p == '\r')
194 csv_output_buffer (struct csv_writer *w, const char *s, size_t len)
196 if (csv_field_needs_quoting (w, s, len))
200 putc (w->opts.qualifier, w->file);
201 for (p = s; p < &s[len]; p++)
203 if (*p == w->opts.qualifier)
204 putc (w->opts.qualifier, w->file);
207 putc (w->opts.qualifier, w->file);
210 fwrite (s, 1, len, w->file);
214 csv_output_string (struct csv_writer *w, const char *s)
216 csv_output_buffer (w, s, strlen (s));
220 write_var_names (struct csv_writer *w, const struct dictionary *d)
224 for (i = 0; i < w->n_csv_vars; i++)
227 putc (w->opts.delimiter, w->file);
228 csv_output_string (w, var_get_name (dict_get_var (d, i)));
230 putc ('\n', w->file);
234 csv_output_format (struct csv_writer *w, const struct csv_var *cv,
235 const union value *value)
237 char *s = data_out (value, w->encoding, &cv->format);
238 struct substring ss = ss_cstr (s);
239 if (cv->format.type != FMT_A)
240 ss_trim (&ss, ss_cstr (" "));
242 ss_rtrim (&ss, ss_cstr (" "));
243 csv_output_buffer (w, ss.string, ss.length);
248 extract_date (double number, int *y, int *m, int *d)
252 calendar_offset_to_gregorian (number / 60. / 60. / 24., y, m, d, &yd);
253 return fmod (number, 60. * 60. * 24.);
257 extract_time (double number, double *H, int *M, int *S)
259 *H = floor (number / 60. / 60.);
260 number = fmod (number, 60. * 60.);
262 *M = floor (number / 60.);
263 number = fmod (number, 60.);
269 csv_write_var__ (struct csv_writer *w, const struct csv_var *cv,
270 const union value *value)
274 label = val_labs_find (cv->val_labs, value);
276 csv_output_string (w, label);
277 else if (cv->width == 0 && value->f == SYSMIS)
278 csv_output_buffer (w, " ", 1);
279 else if (w->opts.use_print_formats)
280 csv_output_format (w, cv, value);
285 switch (cv->format.type)
309 snprintf (s, sizeof s, "%.*g", DBL_DIG + 1, value->f);
310 if (w->opts.decimal != '.')
312 char *cp = strchr (s, '.');
314 *cp = w->opts.decimal;
332 extract_date (value->f, &y, &m, &d);
333 snprintf (s, sizeof s, "%02d/%02d/%04d", m, d, y);
345 extract_time (extract_date (value->f, &y, &m, &d), &H, &M, &S);
346 snprintf (s, sizeof s, "%02d/%02d/%04d %02.0f:%02d:%02d",
357 extract_time (fabs (value->f), &H, &M, &S);
358 snprintf (s, sizeof s, "%s%02.0f:%02d:%02d",
359 value->f < 0 ? "-" : "", H, M, S);
365 csv_output_format (w, cv, value);
368 case FMT_NUMBER_OF_FORMATS:
371 csv_output_string (w, s);
376 csv_write_var (struct csv_writer *w, const struct csv_var *cv,
377 const union value *value)
379 if (mv_is_value_missing (&cv->missing, value, MV_USER))
383 value_init (&missing, cv->width);
384 value_set_missing (&missing, cv->width);
385 csv_write_var__ (w, cv, &missing);
386 value_destroy (&missing, cv->width);
389 csv_write_var__ (w, cv, value);
393 csv_write_case (struct csv_writer *w, const struct ccase *c)
397 for (i = 0; i < w->n_csv_vars; i++)
399 const struct csv_var *cv = &w->csv_vars[i];
402 putc (w->opts.delimiter, w->file);
403 csv_write_var (w, cv, case_data_idx (c, cv->case_index));
405 putc ('\n', w->file);
408 /* Writes case C to CSV file W. */
410 csv_file_casewriter_write (struct casewriter *writer, void *w_,
413 struct csv_writer *w = w_;
415 if (ferror (w->file))
417 casewriter_force_error (writer);
422 csv_write_case (w, c);
426 /* Destroys CSV file writer W. */
428 csv_file_casewriter_destroy (struct casewriter *writer, void *w_)
430 struct csv_writer *w = w_;
431 if (!close_writer (w))
432 casewriter_force_error (writer);
435 /* Returns true if an I/O error has occurred on WRITER, false otherwise. */
437 write_error (const struct csv_writer *writer)
439 return ferror (writer->file);
442 /* Closes a CSV file after we're done with it.
443 Returns true if successful, false if an I/O error occurred. */
445 close_writer (struct csv_writer *w)
458 if (fclose (w->file) == EOF)
462 msg (ME, _("An I/O error occurred writing CSV file \"%s\"."),
463 fh_get_file_name (w->fh));
465 if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
474 for (i = 0; i < w->n_csv_vars; i++)
476 struct csv_var *cv = &w->csv_vars[i];
477 mv_destroy (&cv->missing);
478 val_labs_destroy (cv->val_labs);
487 /* CSV file writer casewriter class. */
488 static const struct casewriter_class csv_file_casewriter_class =
490 csv_file_casewriter_write,
491 csv_file_casewriter_destroy,