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