Merge commit 'origin/stable'
[pspp-builds.git] / 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
30 #include "error.h"
31 #include "xalloc.h"
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35
36 struct tmpfile
37   {
38     FILE *file;                 /* Underlying file. */
39
40     /* Current byte offset in file.  We track this manually,
41        instead of using ftello, because in glibc ftello flushes
42        the stream buffer, making the common case of sequential
43        access to cases unreasonably slow. */
44     off_t position;
45   };
46
47 /* Creates and returns a new temporary file.  The temporary file
48    will be automatically deleted when the process exits. */
49 struct tmpfile *
50 tmpfile_create (void)
51 {
52   struct tmpfile *tf = xmalloc (sizeof *tf);
53   tf->file = tmpfile ();
54   if (tf->file == NULL)
55     error (0, errno, _("failed to create temporary file"));
56   tf->position = 0;
57   return tf;
58 }
59
60 /* Closes and destroys temporary file TF.  Returns true if I/O on
61    TF always succeeded, false if an I/O error occurred at some
62    point. */
63 bool
64 tmpfile_destroy (struct tmpfile *tf)
65 {
66   bool ok = true;
67   if (tf != NULL)
68     {
69       ok = !tmpfile_error (tf);
70       if (tf->file != NULL)
71         fclose (tf->file);
72       free (tf);
73     }
74   return ok;
75 }
76
77 /* Seeks TF's underlying file to the start of `union value'
78    VALUE_IDX within case CASE_IDX.
79    Returns true if the seek is successful and TF is not
80    otherwise tainted, false otherwise. */
81 static bool
82 do_seek (const struct tmpfile *tf_, off_t offset)
83 {
84   struct tmpfile *tf = (struct tmpfile *) tf_;
85
86   if (!tmpfile_error (tf))
87     {
88       if (tf->position == offset)
89         return true;
90       else if (fseeko (tf->file, offset, SEEK_SET) == 0)
91         {
92           tf->position = offset;
93           return true;
94         }
95       else
96         error (0, errno, _("seeking in temporary file"));
97     }
98
99   return false;
100 }
101
102 /* Reads BYTES bytes from TF's underlying file into BUFFER.
103    TF must not be tainted upon entry into this function.
104    Returns true if successful, false upon an I/O error (in which
105    case TF is marked tainted). */
106 static bool
107 do_read (const struct tmpfile *tf_, void *buffer, size_t bytes)
108 {
109   struct tmpfile *tf = (struct tmpfile *) tf_;
110
111   assert (!tmpfile_error (tf));
112   if (bytes > 0 && fread (buffer, bytes, 1, tf->file) != 1)
113     {
114       if (ferror (tf->file))
115         error (0, errno, _("reading temporary file"));
116       else if (feof (tf->file))
117         error (0, 0, _("unexpected end of file reading temporary file"));
118       else
119         NOT_REACHED ();
120       return false;
121     }
122   tf->position += bytes;
123   return true;
124 }
125
126 /* Writes BYTES bytes from BUFFER into TF's underlying file.
127    TF must not be tainted upon entry into this function.
128    Returns true if successful, false upon an I/O error (in which
129    case TF is marked tainted). */
130 static bool
131 do_write (struct tmpfile *tf, const void *buffer, size_t bytes)
132 {
133   assert (!tmpfile_error (tf));
134   if (bytes > 0 && fwrite (buffer, bytes, 1, tf->file) != 1)
135     {
136       error (0, errno, _("writing to temporary file"));
137       return false;
138     }
139   tf->position += bytes;
140   return true;
141 }
142
143 /* Reads N bytes from TF at byte offset OFFSET into DATA.
144    Returns true if successful, false on failure.  */
145 bool
146 tmpfile_read (const struct tmpfile *tf, off_t offset, size_t n, void *data)
147 {
148   return do_seek (tf, offset) && do_read (tf, data, n);
149 }
150
151 /* Writes the N bytes in DATA to TF at byte offset OFFSET.
152    Returns true if successful, false on failure.  */
153 bool
154 tmpfile_write (struct tmpfile *tf, off_t offset, size_t n, const void *data)
155 {
156   return do_seek (tf, offset) && do_write (tf, data, n);
157 }
158
159 /* Returns true if an error has occurred in I/O on TF,
160    false if no error has been detected. */
161 bool
162 tmpfile_error (const struct tmpfile *tf)
163 {
164   return tf->file == NULL || ferror (tf->file) || feof (tf->file);
165 }