CROSSTABS: Handle case where all cases in a crosstabulation are missing.
[pspp] / src / language / data-io / file-handle.q
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 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 <limits.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <data/file-name.h>
23 #include <language/command.h>
24 #include <language/data-io/file-handle.h>
25 #include <language/lexer/lexer.h>
26 #include <libpspp/assertion.h>
27 #include <libpspp/message.h>
28 #include <libpspp/str.h>
29 #include <data/variable.h>
30 #include <data/file-handle-def.h>
31
32 #include "xalloc.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36
37 /* (headers) */
38
39
40 /* (specification)
41    "FILE HANDLE" (fh_):
42      name=string;
43      lrecl=integer;
44      tabwidth=integer "x>=0" "%s must be nonnegative";
45      mode=mode:!character/binary/image/360/scratch;
46      recform=recform:fixed/f/variable/v/spanned/vs.
47 */
48 /* (declarations) */
49 /* (functions) */
50
51 int
52 cmd_file_handle (struct lexer *lexer, struct dataset *ds)
53 {
54   char handle_name[VAR_NAME_LEN + 1];
55   struct cmd_file_handle cmd;
56   struct file_handle *handle;
57
58   if (!lex_force_id (lexer))
59     return CMD_CASCADING_FAILURE;
60   str_copy_trunc (handle_name, sizeof handle_name, lex_tokid (lexer));
61
62   handle = fh_from_id (handle_name);
63   if (handle != NULL)
64     {
65       msg (SE, _("File handle %s is already defined.  "
66                  "Use CLOSE FILE HANDLE before redefining a file handle."),
67            handle_name);
68       return CMD_CASCADING_FAILURE;
69     }
70
71   lex_get (lexer);
72   if (!lex_force_match (lexer, '/'))
73     return CMD_CASCADING_FAILURE;
74
75   if (!parse_file_handle (lexer, ds, &cmd, NULL))
76     return CMD_CASCADING_FAILURE;
77
78   if (lex_end_of_command (lexer) != CMD_SUCCESS)
79     goto lossage;
80
81   if (cmd.mode != FH_SCRATCH)
82     {
83       struct fh_properties properties = *fh_default_properties ();
84
85       if (cmd.s_name == NULL)
86         {
87           lex_sbc_missing (lexer, "NAME");
88           goto lossage;
89         }
90
91       switch (cmd.mode)
92         {
93         case FH_CHARACTER:
94           properties.mode = FH_MODE_TEXT;
95           if (cmd.sbc_tabwidth)
96             properties.tab_width = cmd.n_tabwidth[0];
97           break;
98         case FH_IMAGE:
99           properties.mode = FH_MODE_FIXED;
100           break;
101         case FH_BINARY:
102           properties.mode = FH_MODE_VARIABLE;
103           break;
104         case FH_360:
105           properties.encoding = "EBCDIC-US";
106           if (cmd.recform == FH_FIXED || cmd.recform == FH_F)
107             properties.mode = FH_MODE_FIXED;
108           else if (cmd.recform == FH_VARIABLE || cmd.recform == FH_V)
109             {
110               properties.mode = FH_MODE_360_VARIABLE;
111               properties.record_width = 8192;
112             }
113           else if (cmd.recform == FH_SPANNED || cmd.recform == FH_VS)
114             {
115               properties.mode = FH_MODE_360_SPANNED;
116               properties.record_width = 8192;
117             }
118           else
119             {
120               msg (SE, _("RECFORM must be specified with MODE=360."));
121               goto lossage;
122             }
123           break;
124         default:
125           NOT_REACHED ();
126         }
127
128       if (properties.mode == FH_MODE_FIXED || cmd.n_lrecl[0] != LONG_MIN)
129         {
130           if (cmd.n_lrecl[0] == LONG_MIN)
131             msg (SE, _("The specified file mode requires LRECL.  "
132                        "Assuming %zu-character records."),
133                  properties.record_width);
134           else if (cmd.n_lrecl[0] < 1 || cmd.n_lrecl[0] >= (1UL << 31))
135             msg (SE, _("Record length (%ld) must be between 1 and %lu bytes.  "
136                        "Assuming %d-character records."),
137                  cmd.n_lrecl[0], (1UL << 31) - 1, properties.record_width);
138           else
139             properties.record_width = cmd.n_lrecl[0];
140         }
141
142       fh_create_file (handle_name, cmd.s_name, &properties);
143     }
144   else
145     fh_create_scratch (handle_name);
146
147   free_file_handle (&cmd);
148   return CMD_SUCCESS;
149
150  lossage:
151   free_file_handle (&cmd);
152   return CMD_CASCADING_FAILURE;
153 }
154
155 int
156 cmd_close_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
157 {
158   struct file_handle *handle;
159
160   if (!lex_force_id (lexer))
161     return CMD_CASCADING_FAILURE;
162   handle = fh_from_id (lex_tokid (lexer));
163   if (handle == NULL)
164     return CMD_CASCADING_FAILURE;
165
166   fh_unname (handle);
167   return CMD_SUCCESS;
168 }
169
170 /* Returns the name for REFERENT. */
171 static const char *
172 referent_name (enum fh_referent referent)
173 {
174   switch (referent)
175     {
176     case FH_REF_FILE:
177       return _("file");
178     case FH_REF_INLINE:
179       return _("inline file");
180     case FH_REF_SCRATCH:
181       return _("scratch file");
182     default:
183       NOT_REACHED ();
184     }
185 }
186
187 /* Parses a file handle name, which may be a file name as a string
188    or a file handle name as an identifier.  The allowed types of
189    file handle are restricted to those in REFERENT_MASK.  Returns
190    the file handle when successful, a null pointer on failure.
191
192    The caller is responsible for fh_unref()'ing the returned
193    file handle when it is no longer needed. */
194 struct file_handle *
195 fh_parse (struct lexer *lexer, enum fh_referent referent_mask)
196 {
197   struct file_handle *handle;
198
199   if (lex_match_id (lexer, "INLINE"))
200     handle = fh_inline_file ();
201   else
202     {
203       if (lex_token (lexer) != T_ID && !lex_is_string (lexer))
204         {
205           lex_error (lexer, _("expecting a file name or handle name"));
206           return NULL;
207         }
208
209       handle = NULL;
210       if (lex_token (lexer) == T_ID)
211         handle = fh_from_id (lex_tokid (lexer));
212       if (handle == NULL)
213         {
214           if (lex_token (lexer) != T_ID || lex_tokid (lexer)[0] != '#' || settings_get_syntax () != ENHANCED)
215             handle = fh_create_file (NULL, ds_cstr (lex_tokstr (lexer)),
216                                      fh_default_properties ());
217           else
218             handle = fh_create_scratch (lex_tokid (lexer));
219         }
220       lex_get (lexer);
221     }
222
223   if (!(fh_get_referent (handle) & referent_mask))
224     {
225       msg (SE, _("Handle for %s not allowed here."),
226            referent_name (fh_get_referent (handle)));
227       fh_unref (handle);
228       return NULL;
229     }
230
231   return handle;
232 }
233
234 /*
235    Local variables:
236    mode: c
237    End:
238 */