Test both compressed and uncompressed system files with very long
[pspp] / src / data / case-tmpfile.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007 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 into C.
202    Returns true if successful, false if CTF is tainted or an I/O
203    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 bool
208 case_tmpfile_get_case (const struct case_tmpfile *ctf, casenumber case_idx,
209                        struct ccase *c)
210 {
211   case_create (c, ctf->value_cnt);
212   if (case_tmpfile_get_values (ctf, case_idx, 0,
213                                case_data_all_rw (c), ctf->value_cnt))
214     return true;
215   else
216     {
217       case_destroy (c);
218       case_nullify (c);
219       return false;
220     }
221 }
222
223 /* Writes VALUE_CNT values from VALUES, into the case numbered
224    CASE_IDX starting START_VALUE values into that case.
225    Returns true if successful, false if CTF is tainted or an I/O
226    error occurs during the operation. */
227 bool
228 case_tmpfile_put_values (struct case_tmpfile *ctf,
229                          casenumber case_idx, size_t start_value,
230                          const union value values[], size_t value_cnt)
231
232 {
233   assert (value_cnt <= ctf->value_cnt);
234   assert (value_cnt + start_value <= ctf->value_cnt);
235
236   return (do_seek (ctf, case_idx, start_value)
237           && do_write (ctf, sizeof *values * value_cnt, values));
238 }
239
240 /* Writes C to CTF as the case numbered CASE_IDX.
241    Returns true if successful, false if CTF is tainted or an I/O
242    error occurs during the operation. */
243 bool
244 case_tmpfile_put_case (struct case_tmpfile *ctf, casenumber case_idx,
245                        struct ccase *c)
246 {
247   bool ok = case_tmpfile_put_values (ctf, case_idx, 0,
248                                      case_data_all (c), ctf->value_cnt);
249   case_destroy (c);
250   return ok;
251 }
252