df3b589fe0c893dad1828c52e86b2542ce78f5b4
[pspp] / src / libpspp / ext-array.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010 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 /* An interface to an array of octets that is stored on disk as a temporary
18    file. */
19
20 #include <config.h>
21
22 #include <libpspp/ext-array.h>
23
24 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include <libpspp/assertion.h>
29 #include <libpspp/cast.h>
30
31 #include "error.h"
32 #include "xalloc.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36
37 struct ext_array
38   {
39     FILE *file;                 /* Underlying file. */
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 external array. */
49 struct ext_array *
50 ext_array_create (void)
51 {
52   struct ext_array *ea = xmalloc (sizeof *ea);
53   ea->file = tmpfile ();
54   if (ea->file == NULL)
55     error (0, errno, _("failed to create temporary file"));
56   ea->position = 0;
57   return ea;
58 }
59
60 /* Closes and destroys external array EA.  Returns true if I/O on EA always
61    succeeded, false if an I/O error occurred at some point. */
62 bool
63 ext_array_destroy (struct ext_array *ea)
64 {
65   bool ok = true;
66   if (ea != NULL)
67     {
68       ok = !ext_array_error (ea);
69       if (ea->file != NULL)
70         fclose (ea->file);
71       free (ea);
72     }
73   return ok;
74 }
75
76 /* Seeks EA's underlying file to the start of `union value'
77    VALUE_IDX within case CASE_IDX.
78    Returns true if the seek is successful and EA is not
79    otherwise tainted, false otherwise. */
80 static bool
81 do_seek (const struct ext_array *ea_, off_t offset)
82 {
83   struct ext_array *ea = CONST_CAST (struct ext_array *, ea_);
84
85   if (!ext_array_error (ea))
86     {
87       if (ea->position == offset)
88         return true;
89       else if (fseeko (ea->file, offset, SEEK_SET) == 0)
90         {
91           ea->position = offset;
92           return true;
93         }
94       else
95         error (0, errno, _("seeking in temporary file"));
96     }
97
98   return false;
99 }
100
101 /* Reads BYTES bytes from EA's underlying file into BUFFER.
102    EA must not be tainted upon entry into this function.
103    Returns true if successful, false upon an I/O error (in which
104    case EA is marked tainted). */
105 static bool
106 do_read (const struct ext_array *ea_, void *buffer, size_t bytes)
107 {
108   struct ext_array *ea = CONST_CAST (struct ext_array *, ea_);
109
110   assert (!ext_array_error (ea));
111   if (bytes > 0 && fread (buffer, bytes, 1, ea->file) != 1)
112     {
113       if (ferror (ea->file))
114         error (0, errno, _("reading temporary file"));
115       else if (feof (ea->file))
116         error (0, 0, _("unexpected end of file reading temporary file"));
117       else
118         NOT_REACHED ();
119       return false;
120     }
121   ea->position += bytes;
122   return true;
123 }
124
125 /* Writes BYTES bytes from BUFFER into EA's underlying file.
126    EA must not be tainted upon entry into this function.
127    Returns true if successful, false upon an I/O error (in which
128    case EA is marked tainted). */
129 static bool
130 do_write (struct ext_array *ea, const void *buffer, size_t bytes)
131 {
132   assert (!ext_array_error (ea));
133   if (bytes > 0 && fwrite (buffer, bytes, 1, ea->file) != 1)
134     {
135       error (0, errno, _("writing to temporary file"));
136       return false;
137     }
138   ea->position += bytes;
139   return true;
140 }
141
142 /* Reads N bytes from EA at byte offset OFFSET into DATA.
143    Returns true if successful, false on failure.  */
144 bool
145 ext_array_read (const struct ext_array *ea, off_t offset, size_t n, void *data)
146 {
147   return do_seek (ea, offset) && do_read (ea, data, n);
148 }
149
150 /* Writes the N bytes in DATA to EA at byte offset OFFSET.
151    Returns true if successful, false on failure.  */
152 bool
153 ext_array_write (struct ext_array *ea, off_t offset, size_t n,
154                  const void *data)
155 {
156   return do_seek (ea, offset) && do_write (ea, data, n);
157 }
158
159 /* Returns true if an error has occurred in I/O on EA,
160    false if no error has been detected. */
161 bool
162 ext_array_error (const struct ext_array *ea)
163 {
164   return ea->file == NULL || ferror (ea->file) || feof (ea->file);
165 }