better tests
[pspp] / src / data / any-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2006, 2010, 2011, 2012, 2014 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/any-reader.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "data/casereader.h"
28 #include "data/dataset.h"
29 #include "data/dictionary.h"
30 #include "data/file-handle-def.h"
31 #include "data/file-name.h"
32 #include "libpspp/assertion.h"
33 #include "libpspp/cast.h"
34 #include "libpspp/message.h"
35 #include "libpspp/str.h"
36
37 #include "gl/xalloc.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41 #define N_(msgid) (msgid)
42
43 static const struct any_reader_class dataset_reader_class;
44
45 static const struct any_reader_class *classes[] =
46   {
47     &sys_file_reader_class,
48     &por_file_reader_class,
49     &pcp_file_reader_class,
50   };
51 enum { N_CLASSES = sizeof classes / sizeof *classes };
52
53 int
54 any_reader_detect (const struct file_handle *file_handle,
55                    const struct any_reader_class **classp)
56 {
57   struct detector
58     {
59       enum any_type type;
60       int (*detect) (FILE *);
61     };
62
63   FILE *file;
64   int retval;
65
66   if (classp)
67     *classp = NULL;
68
69   file = fn_open (file_handle, "rb");
70   if (file == NULL)
71     {
72       msg (ME, _("An error occurred while opening `%s': %s."),
73            fh_get_file_name (file_handle), strerror (errno));
74       return -errno;
75     }
76
77   retval = 0;
78   for (int i = 0; i < N_CLASSES; i++)
79     {
80       int rc = classes[i]->detect (file);
81       if (rc == 1)
82         {
83           retval = 1;
84           if (classp)
85             *classp = classes[i];
86           break;
87         }
88       else if (rc < 0)
89         retval = rc;
90     }
91
92   if (retval < 0)
93     msg (ME, _("Error reading `%s': %s."), fh_get_file_name (file_handle), strerror (-retval));
94
95   fn_close (file_handle, file);
96
97   return retval;
98 }
99
100 struct any_reader *
101 any_reader_open (struct file_handle *handle)
102 {
103   switch (fh_get_referent (handle))
104     {
105     case FH_REF_FILE:
106       {
107         const struct any_reader_class *class;
108         int retval;
109
110         retval = any_reader_detect (handle, &class);
111         if (retval <= 0)
112           {
113             if (retval == 0)
114               msg (SE, _("`%s' is not a system or portable file."),
115                    fh_get_file_name (handle));
116             return NULL;
117           }
118
119         return class->open (handle);
120       }
121
122     case FH_REF_INLINE:
123       msg (SE, _("The inline file is not allowed here."));
124       return NULL;
125
126     case FH_REF_DATASET:
127       return dataset_reader_class.open (handle);
128     }
129   NOT_REACHED ();
130 }
131
132 /* gnulib on some systems defines "close" as something else,
133    which causes problems for this code.  So undefine it here.  */
134 #undef close
135
136 bool
137 any_reader_close (struct any_reader *any_reader)
138 {
139   return any_reader ? any_reader->klass->close (any_reader) : true;
140 }
141
142 struct casereader *
143 any_reader_decode (struct any_reader *any_reader,
144                    const char *encoding,
145                    struct dictionary **dictp,
146                    struct any_read_info *info)
147 {
148   const struct any_reader_class *class = any_reader->klass;
149   struct casereader *reader;
150
151   reader = any_reader->klass->decode (any_reader, encoding, dictp, info);
152   if (reader && info)
153     info->klass = class;
154   return reader;
155 }
156
157 size_t
158 any_reader_get_strings (const struct any_reader *any_reader, struct pool *pool,
159                         char ***labels, bool **ids, char ***values)
160 {
161   return (any_reader->klass->get_strings
162           ? any_reader->klass->get_strings (any_reader, pool, labels, ids,
163                                             values)
164           : 0);
165 }
166
167 struct casereader *
168 any_reader_open_and_decode (struct file_handle *handle,
169                             const char *encoding,
170                             struct dictionary **dictp,
171                             struct any_read_info *info)
172 {
173   struct any_reader *any_reader = any_reader_open (handle);
174   return (any_reader
175           ? any_reader_decode (any_reader, encoding, dictp, info)
176           : NULL);
177 }
178 \f
179 struct dataset_reader
180   {
181     struct any_reader any_reader;
182     struct dictionary *dict;
183     struct casereader *reader;
184   };
185
186 /* Opens FH, which must have referent type FH_REF_DATASET, and returns a
187    dataset_reader for it, or a null pointer on failure.  Stores a copy of the
188    dictionary for the dataset file into *DICT.  The caller takes ownership of
189    the casereader and the dictionary.  */
190 static struct any_reader *
191 dataset_reader_open (struct file_handle *fh)
192 {
193   struct dataset_reader *reader;
194   struct dataset *ds;
195
196   /* We don't bother doing fh_lock or fh_ref on the file handle,
197      as there's no advantage in this case, and doing these would
198      require us to keep track of the "struct file_handle" and
199      "struct fh_lock" and undo our work later. */
200   assert (fh_get_referent (fh) == FH_REF_DATASET);
201
202   ds = fh_get_dataset (fh);
203   if (ds == NULL || !dataset_has_source (ds))
204     {
205       msg (SE, _("Cannot read from dataset %s because no dictionary or data "
206                  "has been written to it yet."),
207            fh_get_name (fh));
208       return NULL;
209     }
210
211   reader = xmalloc (sizeof *reader);
212   reader->any_reader.klass = &dataset_reader_class;
213   reader->dict = dict_clone (dataset_dict (ds));
214   reader->reader = casereader_clone (dataset_source (ds));
215   return &reader->any_reader;
216 }
217
218 static struct dataset_reader *
219 dataset_reader_cast (const struct any_reader *r_)
220 {
221   assert (r_->klass == &dataset_reader_class);
222   return UP_CAST (r_, struct dataset_reader, any_reader);
223 }
224
225 static bool
226 dataset_reader_close (struct any_reader *r_)
227 {
228   struct dataset_reader *r = dataset_reader_cast (r_);
229   dict_unref (r->dict);
230   casereader_destroy (r->reader);
231   free (r);
232
233   return true;
234 }
235
236 static struct casereader *
237 dataset_reader_decode (struct any_reader *r_, const char *encoding UNUSED,
238                        struct dictionary **dictp, struct any_read_info *info)
239 {
240   struct dataset_reader *r = dataset_reader_cast (r_);
241   struct casereader *reader;
242
243   *dictp = r->dict;
244   reader = r->reader;
245   if (info)
246     {
247       memset (info, 0, sizeof *info);
248       info->integer_format = INTEGER_NATIVE;
249       info->float_format = FLOAT_NATIVE_DOUBLE;
250       info->compression = ANY_COMP_NONE;
251       info->n_cases = casereader_get_n_cases (reader);
252     }
253   free (r);
254
255   return reader;
256 }
257
258 static const struct any_reader_class dataset_reader_class =
259   {
260     N_("Dataset"),
261     NULL,
262     dataset_reader_open,
263     dataset_reader_close,
264     dataset_reader_decode,
265     NULL,
266   };