691bc475dc996a095f495a1a38abbbc79a01d0cb
[pspp-builds.git] / src / data / any-reader.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "any-reader.h"
22 #include <assert.h>
23 #include <errno.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <libpspp/assertion.h>
28 #include <libpspp/message.h>
29 #include "file-handle-def.h"
30 #include "file-name.h"
31 #include "por-file-reader.h"
32 #include "sys-file-reader.h"
33 #include <libpspp/str.h>
34 #include "scratch-reader.h"
35 #include "xalloc.h"
36
37 #include "gettext.h"
38 #define _(msgid) gettext (msgid)
39
40 /* Type of file backing an any_reader. */
41 enum any_reader_type
42   {
43     SYSTEM_FILE,                /* System file. */
44     PORTABLE_FILE,              /* Portable file. */
45     SCRATCH_FILE                /* Scratch file. */
46   };
47
48 /* Reader for any type of case-structured file. */
49 struct any_reader 
50   {
51     enum any_reader_type type;  /* Type of file. */
52     void *private;              /* Private data. */
53   };
54
55 /* Result of type detection. */
56 enum detect_result 
57   {
58     YES,                        /* It is this type. */
59     NO,                         /* It is not this type. */
60     IO_ERROR                    /* File couldn't be opened. */
61   };
62
63 /* Tries to detect whether HANDLE represents a given type of
64    file, by opening the file and passing it to DETECT, and
65    returns a detect_result. */
66 static enum detect_result
67 try_detect (struct file_handle *handle, bool (*detect) (FILE *))
68 {
69   FILE *file;
70   bool is_type;
71
72   file = fn_open (fh_get_file_name (handle), "rb");
73   if (file == NULL)
74     {
75       msg (ME, _("An error occurred while opening \"%s\": %s."),
76            fh_get_file_name (handle), strerror (errno));
77       return IO_ERROR;
78     }
79     
80   is_type = detect (file);
81   
82   fn_close (fh_get_file_name (handle), file);
83
84   return is_type ? YES : NO;
85 }
86
87 /* If PRIVATE is non-null, creates and returns a new any_reader,
88    initializing its fields to TYPE and PRIVATE.  If PRIVATE is a
89    null pointer, just returns a null pointer. */   
90 static struct any_reader *
91 make_any_reader (enum any_reader_type type, void *private) 
92 {
93   if (private != NULL) 
94     {
95       struct any_reader *reader = xmalloc (sizeof *reader);
96       reader->type = type;
97       reader->private = private;
98       return reader;
99     }
100   else
101     return NULL;
102 }
103
104 /* Creates an any_reader for HANDLE.  On success, returns the new
105    any_reader and stores the file's dictionary into *DICT.  On
106    failure, returns a null pointer. */
107 struct any_reader *
108 any_reader_open (struct file_handle *handle, struct dictionary **dict)
109 {
110   switch (fh_get_referent (handle)) 
111     {
112     case FH_REF_FILE:
113       {
114         enum detect_result result;
115
116         result = try_detect (handle, sfm_detect);
117         if (result == IO_ERROR)
118           return NULL;
119         else if (result == YES)
120           return make_any_reader (SYSTEM_FILE,
121                                   sfm_open_reader (handle, dict, NULL));
122
123         result = try_detect (handle, pfm_detect);
124         if (result == IO_ERROR)
125           return NULL;
126         else if (result == YES)
127           return make_any_reader (PORTABLE_FILE,
128                                   pfm_open_reader (handle, dict, NULL));
129
130         msg (SE, _("\"%s\" is not a system or portable file."),
131              fh_get_file_name (handle));
132         return NULL;
133       }
134
135     case FH_REF_INLINE:
136       msg (SE, _("The inline file is not allowed here."));
137       return NULL;
138
139     case FH_REF_SCRATCH:
140       return make_any_reader (SCRATCH_FILE,
141                               scratch_reader_open (handle, dict));
142     }
143   NOT_REACHED ();
144 }
145
146 /* Reads a single case from READER into C.
147    Returns true if successful, false at end of file or on error. */
148 bool
149 any_reader_read (struct any_reader *reader, struct ccase *c) 
150 {
151   switch (reader->type) 
152     {
153     case SYSTEM_FILE:
154       return sfm_read_case (reader->private, c);
155
156     case PORTABLE_FILE:
157       return pfm_read_case (reader->private, c);
158
159     case SCRATCH_FILE:
160       return scratch_reader_read_case (reader->private, c);
161     }
162   NOT_REACHED ();
163 }
164
165 /* Returns true if an I/O error has occurred on READER, false
166    otherwise. */
167 bool
168 any_reader_error (struct any_reader *reader) 
169 {
170   switch (reader->type) 
171     {
172     case SYSTEM_FILE:
173       return sfm_read_error (reader->private);
174
175     case PORTABLE_FILE:
176       return pfm_read_error (reader->private);
177
178     case SCRATCH_FILE:
179       return scratch_reader_error (reader->private);
180     }
181   NOT_REACHED ();
182 }
183
184 /* Closes READER. */
185 void
186 any_reader_close (struct any_reader *reader) 
187 {
188   if (reader == NULL)
189     return;
190
191   switch (reader->type) 
192     {
193     case SYSTEM_FILE:
194       sfm_close_reader (reader->private);
195       break;
196
197     case PORTABLE_FILE:
198       pfm_close_reader (reader->private);
199       break;
200
201     case SCRATCH_FILE:
202       scratch_reader_close (reader->private);
203       break;
204
205     default:
206       NOT_REACHED ();
207     }
208
209   free (reader);
210 }