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