Change license from GPLv2+ to GPLv3+.
[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
25 #include <data/file-name.h>
26 #include <language/data-io/file-handle.h>
27 #include <libpspp/alloc.h>
28 #include <libpspp/assertion.h>
29 #include <libpspp/message.h>
30 #include <libpspp/str.h>
31
32 #include "minmax.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36
37 /* Data file writer. */
38 struct dfm_writer
39   {
40     struct file_handle *fh;     /* File handle. */
41     FILE *file;                 /* Associated file. */
42   };
43
44 /* Opens a file handle for writing as a data file. */
45 struct dfm_writer *
46 dfm_open_writer (struct file_handle *fh)
47 {
48   struct dfm_writer *w;
49   void **aux;
50
51   aux = fh_open (fh, FH_REF_FILE, "data file", "ws");
52   if (aux == NULL)
53     return NULL;
54   if (*aux != NULL)
55     return *aux;
56
57   w = *aux = xmalloc (sizeof *w);
58   w->fh = fh;
59   w->file = fn_open (fh_get_file_name (w->fh), "wb");
60
61   if (w->file == NULL)
62     {
63       msg (ME, _("An error occurred while opening \"%s\" for writing "
64                  "as a data file: %s."),
65            fh_get_file_name (w->fh), strerror (errno));
66       goto error;
67     }
68
69   return w;
70
71  error:
72   dfm_close_writer (w);
73   return NULL;
74 }
75
76 /* Returns false if an I/O error occurred on WRITER, true otherwise. */
77 bool
78 dfm_write_error (const struct dfm_writer *writer)
79 {
80   return ferror (writer->file);
81 }
82
83 /* Writes record REC (which need not be null-terminated) having
84    length LEN to the file corresponding to HANDLE.  Adds any
85    needed formatting, such as a trailing new-line.  Returns true
86    on success, false on failure. */
87 bool
88 dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
89 {
90   assert (w != NULL);
91
92   if (dfm_write_error (w))
93     return false;
94
95   switch (fh_get_mode (w->fh))
96     {
97     case FH_MODE_TEXT:
98       fwrite (rec, len, 1, w->file);
99       putc ('\n', w->file);
100       break;
101
102     case FH_MODE_BINARY:
103       {
104         size_t record_width = fh_get_record_width (w->fh);
105         size_t write_bytes = MIN (len, record_width);
106         size_t pad_bytes = record_width - write_bytes;
107         fwrite (rec, write_bytes, 1, w->file);
108         while (pad_bytes > 0)
109           {
110             static const char spaces[32] = "                                ";
111             size_t chunk = MIN (pad_bytes, sizeof spaces);
112             fwrite (spaces, chunk, 1, w->file);
113             pad_bytes -= chunk;
114           }
115       }
116       break;
117
118     default:
119       NOT_REACHED ();
120     }
121
122   return !dfm_write_error (w);
123 }
124
125 /* Closes data file writer W. */
126 bool
127 dfm_close_writer (struct dfm_writer *w)
128 {
129   char *file_name;
130   bool ok;
131
132   if (w == NULL)
133     return true;
134   file_name = xstrdup (fh_get_name (w->fh));
135   if (fh_close (w->fh, "data file", "ws"))
136     {
137       free (file_name);
138       return true;
139     }
140
141   ok = true;
142   if (w->file != NULL)
143     {
144       ok = !dfm_write_error (w) && !fn_close (file_name, w->file);
145
146       if (!ok)
147         msg (ME, _("I/O error occurred writing data file \"%s\"."), file_name);
148     }
149   free (w);
150   free (file_name);
151
152   return ok;
153 }