1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-2004, 2006, 2010, 2011, 2012 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. */
53 int unit; /* Unit width, in bytes. */
54 char lf[MAX_UNIT]; /* \n in encoding, 'unit' bytes long. */
55 char spaces[32]; /* 32 bytes worth of ' ' in encoding. */
58 /* Opens a file handle for writing as a data file.
60 The encoding of the file written is by default that of FH itself. If
61 ENCODING is nonnull, then it overrides the default encoding.
63 *However*: ENCODING directly affects only text strings written by the data
64 writer code itself, that is, new-lines in FH_MODE_TEXT and space padding in
65 FH_MODE_FIXED mode. The client must do its own encoding translation for the
66 data that it writes. (This is unavoidable because sometimes the data
67 written includes binary data that reencoding would mangle.) The client can
68 obtain the encoding to re-encode into with dfm_writer_get_encoding(). */
70 dfm_open_writer (struct file_handle *fh, const char *encoding)
72 struct encoding_info ei;
77 lock = fh_lock (fh, FH_REF_FILE, N_("data file"), FH_ACC_WRITE, false);
81 w = fh_lock_get_aux (lock);
85 encoding = encoding_guess_parse_encoding (encoding != NULL
87 : fh_get_encoding (fh));
88 get_encoding_info (&ei, encoding);
90 w = xmalloc (sizeof *w);
93 w->rf = replace_file_start (fh_get_file_name (w->fh), "wb", 0666,
95 w->encoding = xstrdup (encoding);
97 memcpy (w->lf, ei.lf, sizeof w->lf);
98 for (ofs = 0; ofs + ei.unit <= sizeof w->spaces; ofs += ei.unit)
99 memcpy (&w->spaces[ofs], ei.space, ei.unit);
103 msg (ME, _("An error occurred while opening `%s' for writing "
104 "as a data file: %s."),
105 fh_get_file_name (w->fh), strerror (errno));
106 dfm_close_writer (w);
109 fh_lock_set_aux (lock, w);
114 /* Returns false if an I/O error occurred on WRITER, true otherwise. */
116 dfm_write_error (const struct dfm_writer *writer)
118 return ferror (writer->file);
121 /* Writes record REC (which need not be null-terminated) having
122 length LEN to the file corresponding to HANDLE. Adds any
123 needed formatting, such as a trailing new-line. Returns true
124 on success, false on failure. */
126 dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
130 if (dfm_write_error (w))
133 switch (fh_get_mode (w->fh))
136 fwrite (rec, len, 1, w->file);
137 fwrite (w->lf, w->unit, 1, w->file);
142 size_t record_width = fh_get_record_width (w->fh);
143 size_t write_bytes = MIN (len, record_width);
144 size_t pad_bytes = record_width - write_bytes;
145 fwrite (rec, write_bytes, 1, w->file);
146 while (pad_bytes > 0)
148 size_t chunk = MIN (pad_bytes, sizeof w->spaces);
149 fwrite (w->spaces, chunk, 1, w->file);
155 case FH_MODE_VARIABLE:
158 integer_convert (INTEGER_NATIVE, &size, INTEGER_LSB_FIRST, &size,
160 fwrite (&size, sizeof size, 1, w->file);
161 fwrite (rec, len, 1, w->file);
162 fwrite (&size, sizeof size, 1, w->file);
166 case FH_MODE_360_VARIABLE:
167 case FH_MODE_360_SPANNED:
170 if (fh_get_mode (w->fh) == FH_MODE_360_VARIABLE)
171 len = MIN (65527, len);
174 size_t chunk = MIN (65527, len - ofs);
175 uint32_t bdw = (chunk + 8) << 16;
176 int scc = (ofs == 0 && chunk == len ? 0
178 : ofs + chunk == len ? 2
180 uint32_t rdw = ((chunk + 4) << 16) | (scc << 8);
182 integer_convert (INTEGER_NATIVE, &bdw, INTEGER_MSB_FIRST, &bdw,
184 integer_convert (INTEGER_NATIVE, &rdw, INTEGER_MSB_FIRST, &rdw,
186 fwrite (&bdw, 1, sizeof bdw, w->file);
187 fwrite (&rdw, 1, sizeof rdw, w->file);
188 fwrite (rec + ofs, 1, chunk, w->file);
198 return !dfm_write_error (w);
201 /* Closes data file writer W. */
203 dfm_close_writer (struct dfm_writer *w)
209 if (fh_unlock (w->lock))
215 const char *file_name = fh_get_file_name (w->fh);
216 ok = !dfm_write_error (w) && !fn_close (file_name, w->file);
219 msg (ME, _("I/O error occurred writing data file `%s'."), file_name);
221 if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
231 /* Returns the encoding of data written to WRITER. */
233 dfm_writer_get_encoding (const struct dfm_writer *writer)
235 return writer->encoding;