Update all #include directives to the currently preferred style.
[pspp-builds.git] / src / language / data-io / data-writer.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-2004, 2006, 2010, 2011 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/integer-format.h"
32 #include "libpspp/message.h"
33 #include "libpspp/str.h"
34
35 #include "gl/minmax.h"
36 #include "gl/xalloc.h"
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40 #define N_(msgid) (msgid)
41
42 /* Data file writer. */
43 struct dfm_writer
44   {
45     struct file_handle *fh;     /* File handle. */
46     struct fh_lock *lock;       /* Exclusive access to file. */
47     FILE *file;                 /* Associated file. */
48     struct replace_file *rf;    /* Atomic file replacement support. */
49   };
50
51 /* Opens a file handle for writing as a data file. */
52 struct dfm_writer *
53 dfm_open_writer (struct file_handle *fh)
54 {
55   struct dfm_writer *w;
56   struct fh_lock *lock;
57
58   lock = fh_lock (fh, FH_REF_FILE, N_("data file"), FH_ACC_WRITE, false);
59   if (lock == NULL)
60     return NULL;
61
62   w = fh_lock_get_aux (lock);
63   if (w != NULL)
64     return w;
65
66   w = xmalloc (sizeof *w);
67   w->fh = fh_ref (fh);
68   w->lock = lock;
69   w->rf = replace_file_start (fh_get_file_name (w->fh), "wb", 0666,
70                               &w->file, NULL);
71   if (w->rf == NULL)
72     {
73       msg (ME, _("An error occurred while opening `%s' for writing "
74                  "as a data file: %s."),
75            fh_get_file_name (w->fh), strerror (errno));
76       dfm_close_writer (w);
77       return NULL;
78     }
79   fh_lock_set_aux (lock, w);
80
81   return w;
82 }
83
84 /* Returns false if an I/O error occurred on WRITER, true otherwise. */
85 bool
86 dfm_write_error (const struct dfm_writer *writer)
87 {
88   return ferror (writer->file);
89 }
90
91 /* Writes record REC (which need not be null-terminated) having
92    length LEN to the file corresponding to HANDLE.  Adds any
93    needed formatting, such as a trailing new-line.  Returns true
94    on success, false on failure. */
95 bool
96 dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
97 {
98   assert (w != NULL);
99
100   if (dfm_write_error (w))
101     return false;
102
103   switch (fh_get_mode (w->fh))
104     {
105     case FH_MODE_TEXT:
106       fwrite (rec, len, 1, w->file);
107       putc ('\n', w->file);
108       break;
109
110     case FH_MODE_FIXED:
111       {
112         size_t record_width = fh_get_record_width (w->fh);
113         size_t write_bytes = MIN (len, record_width);
114         size_t pad_bytes = record_width - write_bytes;
115         fwrite (rec, write_bytes, 1, w->file);
116         while (pad_bytes > 0)
117           {
118             static const char spaces[32] = "                                ";
119             size_t chunk = MIN (pad_bytes, sizeof spaces);
120             fwrite (spaces, chunk, 1, w->file);
121             pad_bytes -= chunk;
122           }
123       }
124       break;
125
126     case FH_MODE_VARIABLE:
127       {
128         uint32_t size = len;
129         integer_convert (INTEGER_NATIVE, &size, INTEGER_LSB_FIRST, &size,
130                          sizeof size);
131         fwrite (&size, sizeof size, 1, w->file);
132         fwrite (rec, len, 1, w->file);
133         fwrite (&size, sizeof size, 1, w->file);
134       }
135       break;
136
137     case FH_MODE_360_VARIABLE:
138     case FH_MODE_360_SPANNED:
139       {
140         size_t ofs = 0;
141         if (fh_get_mode (w->fh) == FH_MODE_360_VARIABLE)
142           len = MIN (65527, len);
143         while (ofs < len)
144           {
145             size_t chunk = MIN (65527, len - ofs);
146             uint32_t bdw = (chunk + 8) << 16;
147             int scc = (ofs == 0 && chunk == len ? 0
148                        : ofs == 0 ? 1
149                        : ofs + chunk == len ? 2
150                        : 3);
151             uint32_t rdw = ((chunk + 4) << 16) | (scc << 8);
152
153             integer_convert (INTEGER_NATIVE, &bdw, INTEGER_MSB_FIRST, &bdw,
154                              sizeof bdw);
155             integer_convert (INTEGER_NATIVE, &rdw, INTEGER_MSB_FIRST, &rdw,
156                              sizeof rdw);
157             fwrite (&bdw, 1, sizeof bdw, w->file);
158             fwrite (&rdw, 1, sizeof rdw, w->file);
159             fwrite (rec + ofs, 1, chunk, w->file);
160             ofs += chunk;
161           }
162       }
163       break;
164
165     default:
166       NOT_REACHED ();
167     }
168
169   return !dfm_write_error (w);
170 }
171
172 /* Closes data file writer W. */
173 bool
174 dfm_close_writer (struct dfm_writer *w)
175 {
176   bool ok;
177
178   if (w == NULL)
179     return true;
180   if (fh_unlock (w->lock))
181     return true;
182
183   ok = true;
184   if (w->file != NULL)
185     {
186       const char *file_name = fh_get_file_name (w->fh);
187       ok = !dfm_write_error (w) && !fn_close (file_name, w->file);
188
189       if (!ok)
190         msg (ME, _("I/O error occurred writing data file `%s'."), file_name);
191
192       if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
193         ok = false;
194     }
195   fh_unref (w->fh);
196   free (w);
197
198   return ok;
199 }
200
201 /* Returns the legacy character encoding of data written to WRITER. */
202 const char *
203 dfm_writer_get_legacy_encoding (const struct dfm_writer *writer)
204 {
205   return fh_get_legacy_encoding (writer->fh);
206 }