Allow output files to overwrite input files (bug #21280). Thanks to
[pspp-builds.git] / src / language / data-io / data-writer.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-2004, 2006 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 <language/data-io/data-writer.h>
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <sys/stat.h>
25
26 #include <data/file-name.h>
27 #include <data/make-file.h>
28 #include <language/data-io/file-handle.h>
29 #include <libpspp/assertion.h>
30 #include <libpspp/message.h>
31 #include <libpspp/str.h>
32
33 #include "minmax.h"
34 #include "xalloc.h"
35
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
38
39 /* Data file writer. */
40 struct dfm_writer
41   {
42     struct file_handle *fh;     /* File handle. */
43     struct fh_lock *lock;       /* Exclusive access to file. */
44     FILE *file;                 /* Associated file. */
45     struct replace_file *rf;    /* Atomic file replacement support. */
46   };
47
48 /* Opens a file handle for writing as a data file. */
49 struct dfm_writer *
50 dfm_open_writer (struct file_handle *fh)
51 {
52   struct dfm_writer *w;
53   struct fh_lock *lock;
54
55   lock = fh_lock (fh, FH_REF_FILE, "data file", FH_ACC_WRITE, false);
56   if (lock == NULL)
57     return NULL;
58
59   w = fh_lock_get_aux (lock);
60   if (w != NULL)
61     return w;
62
63   w = xmalloc (sizeof *w);
64   w->fh = fh_ref (fh);
65   w->lock = lock;
66   w->rf = replace_file_start (fh_get_file_name (w->fh), "wb",
67                               (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
68                                | S_IROTH | S_IWOTH), &w->file, NULL);
69   if (w->rf == NULL)
70     {
71       msg (ME, _("An error occurred while opening \"%s\" for writing "
72                  "as a data file: %s."),
73            fh_get_file_name (w->fh), strerror (errno));
74       dfm_close_writer (w);
75       return NULL;
76     }
77   fh_lock_set_aux (lock, w);
78
79   return w;
80 }
81
82 /* Returns false if an I/O error occurred on WRITER, true otherwise. */
83 bool
84 dfm_write_error (const struct dfm_writer *writer)
85 {
86   return ferror (writer->file);
87 }
88
89 /* Writes record REC (which need not be null-terminated) having
90    length LEN to the file corresponding to HANDLE.  Adds any
91    needed formatting, such as a trailing new-line.  Returns true
92    on success, false on failure. */
93 bool
94 dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
95 {
96   assert (w != NULL);
97
98   if (dfm_write_error (w))
99     return false;
100
101   switch (fh_get_mode (w->fh))
102     {
103     case FH_MODE_TEXT:
104       fwrite (rec, len, 1, w->file);
105       putc ('\n', w->file);
106       break;
107
108     case FH_MODE_BINARY:
109       {
110         size_t record_width = fh_get_record_width (w->fh);
111         size_t write_bytes = MIN (len, record_width);
112         size_t pad_bytes = record_width - write_bytes;
113         fwrite (rec, write_bytes, 1, w->file);
114         while (pad_bytes > 0)
115           {
116             static const char spaces[32] = "                                ";
117             size_t chunk = MIN (pad_bytes, sizeof spaces);
118             fwrite (spaces, chunk, 1, w->file);
119             pad_bytes -= chunk;
120           }
121       }
122       break;
123
124     default:
125       NOT_REACHED ();
126     }
127
128   return !dfm_write_error (w);
129 }
130
131 /* Closes data file writer W. */
132 bool
133 dfm_close_writer (struct dfm_writer *w)
134 {
135   bool ok;
136
137   if (w == NULL)
138     return true;
139   if (fh_unlock (w->lock))
140     return true;
141
142   ok = true;
143   if (w->file != NULL)
144     {
145       const char *file_name = fh_get_file_name (w->fh);
146       ok = !dfm_write_error (w) && !fn_close (file_name, w->file);
147
148       if (!ok)
149         msg (ME, _("I/O error occurred writing data file \"%s\"."), file_name);
150
151       if (ok ? !replace_file_commit (w->rf) : !replace_file_abort (w->rf))
152         ok = false;
153     }
154   fh_unref (w->fh);
155   free (w);
156
157   return ok;
158 }