make-file: Fix #includes.
[pspp-builds.git] / src / data / make-file.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 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 #include <config.h>
18
19 #include "data/make-file.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "data/file-name.h"
31 #include "libpspp/ll.h"
32 #include "libpspp/message.h"
33
34 #include "gl/fatal-signal.h"
35 #include "gl/tempname.h"
36 #include "gl/xalloc.h"
37 #include "gl/xvasprintf.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41
42 /* Non ansi compilers may set this */
43 #ifndef P_tmpdir
44 #define P_tmpdir "/tmp"
45 #endif
46
47 /* Creates a temporary file and stores its name in *FILE_NAME and
48    a file descriptor for it in *FD.  Returns success.  Caller is
49    responsible for freeing *FILE_NAME. */
50 int
51 make_temp_file (int *fd, char **file_name)
52 {
53   const char *parent_dir;
54
55   assert (file_name != NULL);
56   assert (fd != NULL);
57
58   if (getenv ("TMPDIR") != NULL)
59     parent_dir = getenv ("TMPDIR");
60   else
61     parent_dir = P_tmpdir;
62
63   *file_name = xmalloc (strlen (parent_dir) + 32);
64   sprintf (*file_name, "%s/psppXXXXXX", parent_dir);
65   *fd = mkstemp (*file_name);
66   if (*fd < 0)
67     {
68       msg (ME, _("%s: Creating temporary file: %s."),
69            *file_name, strerror (errno));
70       free (*file_name);
71       *file_name = NULL;
72       return 0;
73     }
74   return 1;
75 }
76
77
78 /* Creates a temporary file and stores its name in *FILE_NAME and
79    a file stream for it in *FP.  Returns success.  Caller is
80    responsible for freeing *FILE_NAME and for closing *FP */
81 int
82 make_unique_file_stream (FILE **fp, char **file_name)
83 {
84   static int serial = 0;
85   const char *parent_dir;
86
87
88   /* FIXME:
89      Need to check for pre-existing file name.
90      Need also to pass in the directory instead of using /tmp
91   */
92
93   assert (file_name != NULL);
94   assert (fp != NULL);
95
96   if (getenv ("TMPDIR") != NULL)
97     parent_dir = getenv ("TMPDIR");
98   else
99     parent_dir = P_tmpdir;
100
101   *file_name = xmalloc (strlen (parent_dir) + 32);
102
103
104   sprintf (*file_name, "%s/pspp%d.png", parent_dir, serial++);
105
106   *fp = fopen(*file_name, "w");
107
108   if (! *fp )
109     {
110       msg (ME, _("%s: Creating file: %s."), *file_name, strerror (errno));
111       free (*file_name);
112       *file_name = NULL;
113       return 0;
114     }
115
116   return 1;
117 }
118 \f
119 struct replace_file
120   {
121     struct ll ll;
122     char *file_name;
123     char *tmp_name;
124   };
125
126 static struct ll_list all_files = LL_INITIALIZER (all_files);
127
128 static void free_replace_file (struct replace_file *);
129 static void unlink_replace_files (void);
130
131 struct replace_file *
132 replace_file_start (const char *file_name, const char *mode,
133                     mode_t permissions, FILE **fp, char **tmp_name)
134 {
135   static bool registered;
136   struct stat s;
137   struct replace_file *rf;
138   int fd;
139
140   /* If FILE_NAME represents a special file, write to it directly
141      instead of trying to replace it. */
142   if (stat (file_name, &s) == 0 && !S_ISREG (s.st_mode))
143     {
144       /* Open file descriptor. */
145       fd = open (file_name, O_WRONLY);
146       if (fd < 0)
147         {
148           msg (ME, _("Opening %s for writing: %s."),
149                file_name, strerror (errno));
150           return NULL;
151         }
152
153       /* Open file as stream. */
154       *fp = fdopen (fd, mode);
155       if (*fp == NULL)
156         {
157           msg (ME, _("Opening stream for %s: %s."),
158                file_name, strerror (errno));
159           close (fd);
160           return NULL;
161         }
162
163       rf = xmalloc (sizeof *rf);
164       rf->file_name = NULL;
165       rf->tmp_name = xstrdup (file_name);
166       if (tmp_name != NULL)
167         *tmp_name = rf->tmp_name;
168       return rf;
169     }
170
171   if (!registered)
172     {
173       at_fatal_signal (unlink_replace_files);
174       registered = true;
175     }
176   block_fatal_signals ();
177
178   rf = xmalloc (sizeof *rf);
179   rf->file_name = xstrdup (file_name);
180   for (;;)
181     {
182       /* Generate unique temporary file name. */
183       rf->tmp_name = xasprintf ("%s.tmpXXXXXX", file_name);
184       if (gen_tempname (rf->tmp_name, 0, 0600, GT_NOCREATE) < 0)
185         {
186           msg (ME, _("Creating temporary file to replace %s: %s."),
187                rf->file_name, strerror (errno));
188           goto error;
189         }
190
191       /* Create file by that name. */
192       fd = open (rf->tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, permissions);
193       if (fd >= 0)
194         break;
195       if (errno != EEXIST)
196         {
197           msg (ME, _("Creating temporary file %s: %s."),
198                rf->tmp_name, strerror (errno));
199           goto error;
200         }
201       free (rf->tmp_name);
202     }
203
204
205   /* Open file as stream. */
206   *fp = fdopen (fd, mode);
207   if (*fp == NULL)
208     {
209       msg (ME, _("Opening stream for temporary file %s: %s."),
210            rf->tmp_name, strerror (errno));
211       close (fd);
212       unlink (rf->tmp_name);
213       goto error;
214     }
215
216   /* Register file for deletion. */
217   ll_push_head (&all_files, &rf->ll);
218   unblock_fatal_signals ();
219
220   if (tmp_name != NULL)
221     *tmp_name = rf->tmp_name;
222
223   return rf;
224
225 error:
226   unblock_fatal_signals ();
227   free_replace_file (rf);
228   *fp = NULL;
229   if (tmp_name != NULL)
230     *tmp_name = NULL;
231   return NULL;
232 }
233
234 bool
235 replace_file_commit (struct replace_file *rf)
236 {
237   bool ok = true;
238
239   if (rf->file_name != NULL)
240     {
241       int save_errno;
242
243       block_fatal_signals ();
244       ok = rename (rf->tmp_name, rf->file_name) == 0;
245       save_errno = errno;
246       ll_remove (&rf->ll);
247       unblock_fatal_signals ();
248
249       if (!ok)
250         msg (ME, _("Replacing %s by %s: %s."),
251              rf->tmp_name, rf->file_name, strerror (save_errno));
252     }
253   else
254     {
255       /* Special file: no temporary file to rename. */
256     }
257   free_replace_file (rf);
258
259   return ok;
260 }
261
262 bool
263 replace_file_abort (struct replace_file *rf)
264 {
265   bool ok = true;
266
267   if (rf->file_name != NULL)
268     {
269       int save_errno;
270
271       block_fatal_signals ();
272       ok = unlink (rf->tmp_name) == 0;
273       save_errno = errno;
274       ll_remove (&rf->ll);
275       unblock_fatal_signals ();
276
277       if (!ok)
278         msg (ME, _("Removing %s: %s."), rf->tmp_name, strerror (save_errno));
279     }
280   else
281     {
282       /* Special file: no temporary file to unlink. */
283     }
284   free_replace_file (rf);
285
286   return ok;
287 }
288
289 static void
290 free_replace_file (struct replace_file *rf)
291 {
292   free (rf->file_name);
293   free (rf->tmp_name);
294   free (rf);
295 }
296
297 static void
298 unlink_replace_files (void)
299 {
300   struct replace_file *rf;
301
302   block_fatal_signals ();
303   ll_for_each (rf, struct replace_file, ll, &all_files)
304     {
305       /* We don't free_replace_file(RF) because calling free is unsafe
306          from an asynchronous signal handler. */
307       unlink (rf->tmp_name);
308       fflush (stdout);
309     }
310   unblock_fatal_signals ();
311 }