aff135d0cc0a1c27b0ea9af1d58dec6beb08d19a
[pspp] / src / libpspp / 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 /* A interface to allow a temporary file to be treated as an
18    array of data. */
19
20 #include <config.h>
21
22 #include <libpspp/tmpfile.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 tmpfile
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 temporary file.  The temporary file
49    will be automatically deleted when the process exits. */
50 struct tmpfile *
51 tmpfile_create (void)
52 {
53   struct tmpfile *tf = xmalloc (sizeof *tf);
54   tf->file = tmpfile ();
55   if (tf->file == NULL)
56     error (0, errno, _("failed to create temporary file"));
57   tf->position = 0;
58   return tf;
59 }
60
61 /* Closes and destroys temporary file TF.  Returns true if I/O on
62    TF always succeeded, false if an I/O error occurred at some
63    point. */
64 bool
65 tmpfile_destroy (struct tmpfile *tf)
66 {
67   bool ok = true;
68   if (tf != NULL)
69     {
70       ok = !tmpfile_error (tf);
71       if (tf->file != NULL)
72         fclose (tf->file);
73       free (tf);
74     }
75   return ok;
76 }
77
78 /* Seeks TF's underlying file to the start of `union value'
79    VALUE_IDX within case CASE_IDX.
80    Returns true if the seek is successful and TF is not
81    otherwise tainted, false otherwise. */
82 static bool
83 do_seek (const struct tmpfile *tf_, off_t offset)
84 {
85   struct tmpfile *tf = CONST_CAST (struct tmpfile *, tf_);
86
87   if (!tmpfile_error (tf))
88     {
89       if (tf->position == offset)
90         return true;
91       else if (fseeko (tf->file, offset, SEEK_SET) == 0)
92         {
93           tf->position = offset;
94           return true;
95         }
96       else
97         error (0, errno, _("seeking in temporary file"));
98     }
99
100   return false;
101 }
102
103 /* Reads BYTES bytes from TF's underlying file into BUFFER.
104    TF must not be tainted upon entry into this function.
105    Returns true if successful, false upon an I/O error (in which
106    case TF is marked tainted). */
107 static bool
108 do_read (const struct tmpfile *tf_, void *buffer, size_t bytes)
109 {
110   struct tmpfile *tf = CONST_CAST (struct tmpfile *, tf_);
111
112   assert (!tmpfile_error (tf));
113   if (bytes > 0 && fread (buffer, bytes, 1, tf->file) != 1)
114     {
115       if (ferror (tf->file))
116         error (0, errno, _("reading temporary file"));
117       else if (feof (tf->file))
118         error (0, 0, _("unexpected end of file reading temporary file"));
119       else
120         NOT_REACHED ();
121       return false;
122     }
123   tf->position += bytes;
124   return true;
125 }
126
127 /* Writes BYTES bytes from BUFFER into TF's underlying file.
128    TF must not be tainted upon entry into this function.
129    Returns true if successful, false upon an I/O error (in which
130    case TF is marked tainted). */
131 static bool
132 do_write (struct tmpfile *tf, const void *buffer, size_t bytes)
133 {
134   assert (!tmpfile_error (tf));
135   if (bytes > 0 && fwrite (buffer, bytes, 1, tf->file) != 1)
136     {
137       error (0, errno, _("writing to temporary file"));
138       return false;
139     }
140   tf->position += bytes;
141   return true;
142 }
143
144 /* Reads N bytes from TF at byte offset OFFSET into DATA.
145    Returns true if successful, false on failure.  */
146 bool
147 tmpfile_read (const struct tmpfile *tf, off_t offset, size_t n, void *data)
148 {
149   return do_seek (tf, offset) && do_read (tf, data, n);
150 }
151
152 /* Writes the N bytes in DATA to TF at byte offset OFFSET.
153    Returns true if successful, false on failure.  */
154 bool
155 tmpfile_write (struct tmpfile *tf, off_t offset, size_t n, const void *data)
156 {
157   return do_seek (tf, offset) && do_write (tf, data, n);
158 }
159
160 /* Returns true if an error has occurred in I/O on TF,
161    false if no error has been detected. */
162 bool
163 tmpfile_error (const struct tmpfile *tf)
164 {
165   return tf->file == NULL || ferror (tf->file) || feof (tf->file);
166 }