ca3a6a564421552f4241900caa2fb21708f08226
[pspp-builds.git] / src / data / case-tmpfile.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009 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 <data/case-tmpfile.h>
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <libpspp/assertion.h>
26 #include <libpspp/taint.h>
27
28 #include "error.h"
29 #include "xalloc.h"
30
31 #include "gettext.h"
32 #define _(msgid) gettext (msgid)
33
34 /* A temporary file that stores an array of cases. */
35 struct case_tmpfile
36   {
37     struct taint *taint;        /* Taint. */
38     FILE *file;                 /* Underlying file. */
39     size_t value_cnt;           /* Number of `union value's per case. */
40
41     /* Current byte offset in file.  We track this manually,
42        instead of using ftello, because in glibc ftello flushes
43        the stream buffer, making the common case of sequential
44        access to cases unreasonably slow. */
45     off_t position;
46   };
47
48 /* Creates and returns a new case_tmpfile. */
49 struct case_tmpfile *
50 case_tmpfile_create (size_t value_cnt)
51 {
52   struct case_tmpfile *ctf = xmalloc (sizeof *ctf);
53   ctf->taint = taint_create ();
54   ctf->file = tmpfile ();
55   if (ctf->file == NULL)
56     {
57       error (0, errno, _("failed to create temporary file"));
58       taint_set_taint (ctf->taint);
59     }
60   ctf->value_cnt = value_cnt;
61   ctf->position = 0;
62   return ctf;
63 }
64
65 /* Destroys case_tmpfile CTF.
66    Returns true if CTF was tainted, which is caused by an I/O
67    error on case_tmpfile access or by taint propagation to the
68    case_tmpfile. */
69 bool
70 case_tmpfile_destroy (struct case_tmpfile *ctf)
71 {
72   bool ok = true;
73   if (ctf != NULL)
74     {
75       struct taint *taint = ctf->taint;
76       if (ctf->file != NULL)
77         fclose (ctf->file);
78       free (ctf);
79       ok = taint_destroy (taint);
80     }
81   return ok;
82 }
83
84 /* Returns true if CTF is tainted, which is caused by an I/O
85    error on case_tmpfile access or by taint propagation to the
86    case_tmpfile. */
87 bool
88 case_tmpfile_error (const struct case_tmpfile *ctf)
89 {
90   return taint_is_tainted (ctf->taint);
91 }
92
93 /* Marks CTF as tainted. */
94 void
95 case_tmpfile_force_error (struct case_tmpfile *ctf)
96 {
97   taint_set_taint (ctf->taint);
98 }
99
100 /* Returns CTF's taint object. */
101 const struct taint *
102 case_tmpfile_get_taint (const struct case_tmpfile *ctf)
103 {
104   return ctf->taint;
105 }
106
107 /* Seeks CTF's underlying file to the start of `union value'
108    VALUE_IDX within case CASE_IDX.
109    Returns true if the seek is successful and CTF is not
110    otherwise tainted, false otherwise. */
111 static bool
112 do_seek (const struct case_tmpfile *ctf_,
113          casenumber case_idx, size_t value_idx)
114 {
115   struct case_tmpfile *ctf = (struct case_tmpfile *) ctf_;
116
117   if (!case_tmpfile_error (ctf))
118     {
119       off_t value_ofs = value_idx + (off_t) ctf->value_cnt * case_idx;
120       off_t byte_ofs = sizeof (union value) * value_ofs;
121
122       if (ctf->position == byte_ofs)
123         return true;
124       else if (fseeko (ctf->file, byte_ofs, SEEK_SET) == 0)
125         {
126           ctf->position = byte_ofs;
127           return true;
128         }
129       else
130         {
131           error (0, errno, _("seeking in temporary file"));
132           case_tmpfile_force_error (ctf);
133         }
134     }
135
136   return false;
137 }
138
139 /* Reads BYTES bytes from CTF's underlying file into BUFFER.
140    CTF must not be tainted upon entry into this function.
141    Returns true if successful, false upon an I/O error (in which
142    case CTF is marked tainted). */
143 static bool
144 do_read (const struct case_tmpfile *ctf_, size_t bytes, void *buffer)
145 {
146   struct case_tmpfile *ctf = (struct case_tmpfile *) ctf_;
147
148   assert (!case_tmpfile_error (ctf));
149   if (fread (buffer, bytes, 1, ctf->file) != 1)
150     {
151       case_tmpfile_force_error (ctf);
152       if (ferror (ctf->file))
153         error (0, errno, _("reading temporary file"));
154       else if (feof (ctf->file))
155         error (0, 0, _("unexpected end of file reading temporary file"));
156       else
157         NOT_REACHED ();
158       return false;
159     }
160   ctf->position += bytes;
161   return true;
162 }
163
164 /* Writes BYTES bytes from BUFFER into CTF's underlying file.
165    CTF must not be tainted upon entry into this function.
166    Returns true if successful, false upon an I/O error (in which
167    case CTF is marked tainted). */
168 static bool
169 do_write (struct case_tmpfile *ctf, size_t bytes, const void *buffer)
170 {
171   assert (!case_tmpfile_error (ctf));
172   if (fwrite (buffer, bytes, 1, ctf->file) != 1)
173     {
174       case_tmpfile_force_error (ctf);
175       error (0, errno, _("writing to temporary file"));
176       return false;
177     }
178   ctf->position += bytes;
179   return true;
180 }
181
182 /* Reads VALUE_CNT values into VALUES, from the case numbered
183    CASE_IDX starting START_VALUE values into that case.
184    Returns true if successful, false if CTF is tainted or an I/O
185    error occurs during the operation.
186
187    The results of this function are undefined if any of the
188    values read have not been previously written to CTF. */
189 bool
190 case_tmpfile_get_values (const struct case_tmpfile *ctf,
191                          casenumber case_idx, size_t start_value,
192                          union value values[], size_t value_cnt)
193 {
194   assert (value_cnt <= ctf->value_cnt);
195   assert (value_cnt + start_value <= ctf->value_cnt);
196
197   return (do_seek (ctf, case_idx, start_value)
198           && do_read (ctf, sizeof *values * value_cnt, values));
199 }
200
201 /* Reads the case numbered CASE_IDX from CTF.
202    Returns the case if successful or a null pointer if CTF is
203    tainted or an I/O error occurs during the operation.
204
205    The results of this function are undefined if the case read
206    from CTF had not previously been written. */
207 struct ccase *
208 case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx)
209 {
210   struct ccase *c = case_create (ctf->value_cnt);
211   if (case_tmpfile_get_values (ctf, case_idx, 0,
212                                case_data_all_rw (c), ctf->value_cnt))
213     return c;
214   else
215     {
216       case_unref (c);
217       return NULL;
218     }
219 }
220
221 /* Writes VALUE_CNT values from VALUES, into the case numbered
222    CASE_IDX starting START_VALUE values into that case.
223    Returns true if successful, false if CTF is tainted or an I/O
224    error occurs during the operation. */
225 bool
226 case_tmpfile_put_values (struct case_tmpfile *ctf,
227                          casenumber case_idx, size_t start_value,
228                          const union value values[], size_t value_cnt)
229
230 {
231   assert (value_cnt <= ctf->value_cnt);
232   assert (value_cnt + start_value <= ctf->value_cnt);
233
234   return (do_seek (ctf, case_idx, start_value)
235           && do_write (ctf, sizeof *values * value_cnt, values));
236 }
237
238 /* Writes C to CTF as the case numbered CASE_IDX.
239    Returns true if successful, false if CTF is tainted or an I/O
240    error occurs during the operation. */
241 bool
242 case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx,
243                        struct ccase *c)
244 {
245   bool ok = case_tmpfile_put_values (ctf, case_idx, 0,
246                                      case_data_all (c), ctf->value_cnt);
247   case_unref (c);
248   return ok;
249 }
250