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