5270db0e8119f86236ba5e64e68a510b8cf6840c
[pspp] / src / language / data-io / data-writer.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-2004, 2006, 2010, 2011, 2012 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "language/data-io/data-writer.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26
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"
36
37 #include "gl/minmax.h"
38 #include "gl/xalloc.h"
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42 #define N_(msgid) (msgid)
43
44 /* Data file writer. */
45 struct dfm_writer
46   {
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
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. */
56   };
57
58 /* Opens a file handle for writing as a data file.
59
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.
62
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(). */
69 struct dfm_writer *
70 dfm_open_writer (struct file_handle *fh, const char *encoding)
71 {
72   struct encoding_info ei;
73   struct dfm_writer *w;
74   struct fh_lock *lock;
75   int ofs;
76
77   lock = fh_lock (fh, FH_REF_FILE, N_("data file"), FH_ACC_WRITE, false);
78   if (lock == NULL)
79     return NULL;
80
81   w = fh_lock_get_aux (lock);
82   if (w != NULL)
83     return w;
84
85   encoding = encoding_guess_parse_encoding (encoding != NULL
86                                             ? encoding
87                                             : fh_get_encoding (fh));
88   get_encoding_info (&ei, encoding);
89
90   w = xmalloc (sizeof *w);
91   w->fh = fh_ref (fh);
92   w->lock = lock;
93   w->rf = replace_file_start (fh_get_file_name (w->fh), "wb", 0666,
94                               &w->file, NULL);
95   w->encoding = xstrdup (encoding);
96   w->unit = ei.unit;
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);
100
101   if (w->rf == NULL)
102     {
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);
107       return NULL;
108     }
109   fh_lock_set_aux (lock, w);
110
111   return w;
112 }
113
114 /* Returns false if an I/O error occurred on WRITER, true otherwise. */
115 bool
116 dfm_write_error (const struct dfm_writer *writer)
117 {
118   return ferror (writer->file);
119 }
120
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. */
125 bool
126 dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
127 {
128   assert (w != NULL);
129
130   if (dfm_write_error (w))
131     return false;
132
133   switch (fh_get_mode (w->fh))
134     {
135     case FH_MODE_TEXT:
136       fwrite (rec, len, 1, w->file);
137       fwrite (w->lf, w->unit, 1, w->file);
138       break;
139
140     case FH_MODE_FIXED:
141       {
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)
147           {
148             size_t chunk = MIN (pad_bytes, sizeof w->spaces);
149             fwrite (w->spaces, chunk, 1, w->file);
150             pad_bytes -= chunk;
151           }
152       }
153       break;
154
155     case FH_MODE_VARIABLE:
156       {
157         uint32_t size = len;
158         integer_convert (INTEGER_NATIVE, &size, INTEGER_LSB_FIRST, &size,
159                          sizeof size);
160         fwrite (&size, sizeof size, 1, w->file);
161         fwrite (rec, len, 1, w->file);
162         fwrite (&size, sizeof size, 1, w->file);
163       }
164       break;
165
166     case FH_MODE_360_VARIABLE:
167     case FH_MODE_360_SPANNED:
168       {
169         size_t ofs = 0;
170         if (fh_get_mode (w->fh) == FH_MODE_360_VARIABLE)
171           len = MIN (65527, len);
172         while (ofs < len)
173           {
174             size_t chunk = MIN (65527, len - ofs);
175             uint32_t bdw = (chunk + 8) << 16;
176             int scc = (ofs == 0 && chunk == len ? 0
177                        : ofs == 0 ? 1
178                        : ofs + chunk == len ? 2
179                        : 3);
180             uint32_t rdw = ((chunk + 4) << 16) | (scc << 8);
181
182             integer_convert (INTEGER_NATIVE, &bdw, INTEGER_MSB_FIRST, &bdw,
183                              sizeof bdw);
184             integer_convert (INTEGER_NATIVE, &rdw, INTEGER_MSB_FIRST, &rdw,
185                              sizeof 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);
189             ofs += chunk;
190           }
191       }
192       break;
193
194     default:
195       NOT_REACHED ();
196     }
197
198   return !dfm_write_error (w);
199 }
200
201 /* Closes data file writer W. */
202 bool
203 dfm_close_writer (struct dfm_writer *w)
204 {
205   bool ok;
206
207   if (w == NULL)
208     return true;
209   if (fh_unlock (w->lock))
210     return true;
211
212   ok = true;
213   if (w->file != NULL)
214     {
215       const char *file_name = fh_get_file_name (w->fh);
216       ok = !dfm_write_error (w) && !fn_close (file_name, w->file);
217
218       if (!ok)
219         msg (ME, _("I/O error occurred writing data file `%s'."), file_name);
220
221       if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
222         ok = false;
223     }
224   fh_unref (w->fh);
225   free (w->encoding);
226   free (w);
227
228   return ok;
229 }
230
231 /* Returns the encoding of data written to WRITER. */
232 const char *
233 dfm_writer_get_encoding (const struct dfm_writer *writer)
234 {
235   return writer->encoding;
236 }