gui: Fix behavior of print preview in psppire-output-window.
[pspp] / 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 struct replace_file
43   {
44     struct ll ll;
45     char *file_name;
46     char *tmp_name;
47   };
48
49 static struct ll_list all_files = LL_INITIALIZER (all_files);
50
51 static void free_replace_file (struct replace_file *);
52 static void unlink_replace_files (void);
53
54 struct replace_file *
55 replace_file_start (const char *file_name, const char *mode,
56                     mode_t permissions, FILE **fp, char **tmp_name)
57 {
58   static bool registered;
59   struct stat s;
60   struct replace_file *rf;
61   int fd;
62
63   /* If FILE_NAME represents a special file, write to it directly
64      instead of trying to replace it. */
65   if (stat (file_name, &s) == 0 && !S_ISREG (s.st_mode))
66     {
67       /* Open file descriptor. */
68       fd = open (file_name, O_WRONLY);
69       if (fd < 0)
70         {
71           msg (ME, _("Opening %s for writing: %s."),
72                file_name, strerror (errno));
73           return NULL;
74         }
75
76       /* Open file as stream. */
77       *fp = fdopen (fd, mode);
78       if (*fp == NULL)
79         {
80           msg (ME, _("Opening stream for %s: %s."),
81                file_name, strerror (errno));
82           close (fd);
83           return NULL;
84         }
85
86       rf = xmalloc (sizeof *rf);
87       rf->file_name = NULL;
88       rf->tmp_name = xstrdup (file_name);
89       if (tmp_name != NULL)
90         *tmp_name = rf->tmp_name;
91       return rf;
92     }
93
94   if (!registered)
95     {
96       at_fatal_signal (unlink_replace_files);
97       registered = true;
98     }
99   block_fatal_signals ();
100
101   rf = xmalloc (sizeof *rf);
102   rf->file_name = xstrdup (file_name);
103   for (;;)
104     {
105       /* Generate unique temporary file name. */
106       rf->tmp_name = xasprintf ("%s.tmpXXXXXX", file_name);
107       if (gen_tempname (rf->tmp_name, 0, 0600, GT_NOCREATE) < 0)
108         {
109           msg (ME, _("Creating temporary file to replace %s: %s."),
110                rf->file_name, strerror (errno));
111           goto error;
112         }
113
114       /* Create file by that name. */
115       fd = open (rf->tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, permissions);
116       if (fd >= 0)
117         break;
118       if (errno != EEXIST)
119         {
120           msg (ME, _("Creating temporary file %s: %s."),
121                rf->tmp_name, strerror (errno));
122           goto error;
123         }
124       free (rf->tmp_name);
125     }
126
127
128   /* Open file as stream. */
129   *fp = fdopen (fd, mode);
130   if (*fp == NULL)
131     {
132       msg (ME, _("Opening stream for temporary file %s: %s."),
133            rf->tmp_name, strerror (errno));
134       close (fd);
135       unlink (rf->tmp_name);
136       goto error;
137     }
138
139   /* Register file for deletion. */
140   ll_push_head (&all_files, &rf->ll);
141   unblock_fatal_signals ();
142
143   if (tmp_name != NULL)
144     *tmp_name = rf->tmp_name;
145
146   return rf;
147
148 error:
149   unblock_fatal_signals ();
150   free_replace_file (rf);
151   *fp = NULL;
152   if (tmp_name != NULL)
153     *tmp_name = NULL;
154   return NULL;
155 }
156
157 bool
158 replace_file_commit (struct replace_file *rf)
159 {
160   bool ok = true;
161
162   if (rf->file_name != NULL)
163     {
164       int save_errno;
165
166       block_fatal_signals ();
167       ok = rename (rf->tmp_name, rf->file_name) == 0;
168       save_errno = errno;
169       ll_remove (&rf->ll);
170       unblock_fatal_signals ();
171
172       if (!ok)
173         msg (ME, _("Replacing %s by %s: %s."),
174              rf->tmp_name, rf->file_name, strerror (save_errno));
175     }
176   else
177     {
178       /* Special file: no temporary file to rename. */
179     }
180   free_replace_file (rf);
181
182   return ok;
183 }
184
185 bool
186 replace_file_abort (struct replace_file *rf)
187 {
188   bool ok = true;
189
190   if (rf->file_name != NULL)
191     {
192       int save_errno;
193
194       block_fatal_signals ();
195       ok = unlink (rf->tmp_name) == 0;
196       save_errno = errno;
197       ll_remove (&rf->ll);
198       unblock_fatal_signals ();
199
200       if (!ok)
201         msg (ME, _("Removing %s: %s."), rf->tmp_name, strerror (save_errno));
202     }
203   else
204     {
205       /* Special file: no temporary file to unlink. */
206     }
207   free_replace_file (rf);
208
209   return ok;
210 }
211
212 static void
213 free_replace_file (struct replace_file *rf)
214 {
215   free (rf->file_name);
216   free (rf->tmp_name);
217   free (rf);
218 }
219
220 static void
221 unlink_replace_files (void)
222 {
223   struct replace_file *rf;
224
225   block_fatal_signals ();
226   ll_for_each (rf, struct replace_file, ll, &all_files)
227     {
228       /* We don't free_replace_file(RF) because calling free is unsafe
229          from an asynchronous signal handler. */
230       unlink (rf->tmp_name);
231     }
232   unblock_fatal_signals ();
233 }