1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-2004, 2006, 2010, 2011, 2012, 2013 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 "language/data-io/data-writer.h"
27 #include "data/file-name.h"
28 #include "data/make-file.h"
29 #include "language/data-io/file-handle.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/encoding-guesser.h"
32 #include "libpspp/integer-format.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/message.h"
35 #include "libpspp/str.h"
37 #include "gl/minmax.h"
38 #include "gl/xalloc.h"
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) (msgid)
44 /* Data file writer. */
47 struct file_handle *fh; /* File handle. */
48 struct fh_lock *lock; /* Exclusive access to file. */
49 FILE *file; /* Associated file. */
50 struct replace_file *rf; /* Atomic file replacement support. */
51 char *encoding; /* Encoding. */
52 enum fh_line_ends line_ends; /* Line ends for text files. */
54 int unit; /* Unit width, in bytes. */
55 char cr[MAX_UNIT]; /* \r in encoding, 'unit' bytes long. */
56 char lf[MAX_UNIT]; /* \n in encoding, 'unit' bytes long. */
57 char spaces[32]; /* 32 bytes worth of ' ' in encoding. */
60 /* Opens a file handle for writing as a data file.
62 The encoding of the file written is by default that of FH itself. If
63 ENCODING is nonnull, then it overrides the default encoding.
65 *However*: ENCODING directly affects only text strings written by the data
66 writer code itself, that is, new-lines in FH_MODE_TEXT and space padding in
67 FH_MODE_FIXED mode. The client must do its own encoding translation for the
68 data that it writes. (This is unavoidable because sometimes the data
69 written includes binary data that reencoding would mangle.) The client can
70 obtain the encoding to re-encode into with dfm_writer_get_encoding(). */
72 dfm_open_writer (struct file_handle *fh, const char *encoding)
74 struct encoding_info ei;
79 lock = fh_lock (fh, FH_REF_FILE, N_("data file"), FH_ACC_WRITE, false);
83 w = fh_lock_get_aux (lock);
87 encoding = encoding_guess_parse_encoding (encoding != NULL
89 : fh_get_encoding (fh));
90 get_encoding_info (&ei, encoding);
92 w = xmalloc (sizeof *w);
95 w->rf = replace_file_start (fh_get_file_name (w->fh), "wb", 0666,
97 w->encoding = xstrdup (encoding);
98 w->line_ends = fh_get_line_ends (fh);
100 memcpy (w->cr, ei.cr, sizeof w->cr);
101 memcpy (w->lf, ei.lf, sizeof w->lf);
102 for (ofs = 0; ofs + ei.unit <= sizeof w->spaces; ofs += ei.unit)
103 memcpy (&w->spaces[ofs], ei.space, ei.unit);
107 msg (ME, _("An error occurred while opening `%s' for writing "
108 "as a data file: %s."),
109 fh_get_file_name (w->fh), strerror (errno));
110 dfm_close_writer (w);
113 fh_lock_set_aux (lock, w);
118 /* Returns false if an I/O error occurred on WRITER, true otherwise. */
120 dfm_write_error (const struct dfm_writer *writer)
122 return ferror (writer->file);
125 /* Writes record REC (which need not be null-terminated) having
126 length LEN to the file corresponding to HANDLE. Adds any
127 needed formatting, such as a trailing new-line. Returns true
128 on success, false on failure. */
130 dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
134 if (dfm_write_error (w))
137 switch (fh_get_mode (w->fh))
140 fwrite (rec, len, 1, w->file);
141 if (w->line_ends == FH_END_CRLF)
142 fwrite (w->cr, w->unit, 1, w->file);
143 fwrite (w->lf, w->unit, 1, w->file);
148 size_t record_width = fh_get_record_width (w->fh);
149 size_t write_bytes = MIN (len, record_width);
150 size_t pad_bytes = record_width - write_bytes;
151 fwrite (rec, write_bytes, 1, w->file);
152 while (pad_bytes > 0)
154 size_t chunk = MIN (pad_bytes, sizeof w->spaces);
155 fwrite (w->spaces, chunk, 1, w->file);
161 case FH_MODE_VARIABLE:
164 integer_convert (INTEGER_NATIVE, &size, INTEGER_LSB_FIRST, &size,
166 fwrite (&size, sizeof size, 1, w->file);
167 fwrite (rec, len, 1, w->file);
168 fwrite (&size, sizeof size, 1, w->file);
172 case FH_MODE_360_VARIABLE:
173 case FH_MODE_360_SPANNED:
176 if (fh_get_mode (w->fh) == FH_MODE_360_VARIABLE)
177 len = MIN (65527, len);
180 size_t chunk = MIN (65527, len - ofs);
181 uint32_t bdw = (chunk + 8) << 16;
182 int scc = (ofs == 0 && chunk == len ? 0
184 : ofs + chunk == len ? 2
186 uint32_t rdw = ((chunk + 4) << 16) | (scc << 8);
188 integer_convert (INTEGER_NATIVE, &bdw, INTEGER_MSB_FIRST, &bdw,
190 integer_convert (INTEGER_NATIVE, &rdw, INTEGER_MSB_FIRST, &rdw,
192 fwrite (&bdw, 1, sizeof bdw, w->file);
193 fwrite (&rdw, 1, sizeof rdw, w->file);
194 fwrite (rec + ofs, 1, chunk, w->file);
204 return !dfm_write_error (w);
207 /* Closes data file writer W. */
209 dfm_close_writer (struct dfm_writer *w)
215 if (fh_unlock (w->lock))
221 const char *file_name = fh_get_file_name (w->fh);
222 ok = !dfm_write_error (w) && !fn_close (file_name, w->file);
225 msg (ME, _("I/O error occurred writing data file `%s'."), file_name);
227 if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
237 /* Returns the encoding of data written to WRITER. */
239 dfm_writer_get_encoding (const struct dfm_writer *writer)
241 return writer->encoding;