FILE HANDLE: Get rid of VAR_NAME_LEN limit on handle name.
[pspp-builds.git] / src / language / data-io / file-handle.q
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2010, 2011 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   struct cmd_file_handle cmd;
55   struct file_handle *handle;
56   char *handle_name;
57
58   if (!lex_force_id (lexer))
59     goto error;
60   handle_name = xstrdup (lex_tokcstr (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       goto error;
69     }
70
71   lex_get (lexer);
72   if (!lex_force_match (lexer, T_SLASH))
73     goto error_free_handle_name;
74
75   if (!parse_file_handle (lexer, ds, &cmd, NULL))
76     goto error_free_handle_name;
77
78   if (lex_end_of_command (lexer) != CMD_SUCCESS)
79     goto error_free_cmd;
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 error_free_cmd;
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 error_free_cmd;
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 error_free_cmd:
151   free_file_handle (&cmd);
152 error_free_handle_name:
153   free (handle_name);
154 error:
155   return CMD_CASCADING_FAILURE;
156 }
157
158 int
159 cmd_close_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
160 {
161   struct file_handle *handle;
162
163   if (!lex_force_id (lexer))
164     return CMD_CASCADING_FAILURE;
165   handle = fh_from_id (lex_tokcstr (lexer));
166   if (handle == NULL)
167     return CMD_CASCADING_FAILURE;
168
169   fh_unname (handle);
170   return CMD_SUCCESS;
171 }
172
173 /* Returns the name for REFERENT. */
174 static const char *
175 referent_name (enum fh_referent referent)
176 {
177   switch (referent)
178     {
179     case FH_REF_FILE:
180       return _("file");
181     case FH_REF_INLINE:
182       return _("inline file");
183     case FH_REF_SCRATCH:
184       return _("scratch file");
185     default:
186       NOT_REACHED ();
187     }
188 }
189
190 /* Parses a file handle name, which may be a file name as a string
191    or a file handle name as an identifier.  The allowed types of
192    file handle are restricted to those in REFERENT_MASK.  Returns
193    the file handle when successful, a null pointer on failure.
194
195    The caller is responsible for fh_unref()'ing the returned
196    file handle when it is no longer needed. */
197 struct file_handle *
198 fh_parse (struct lexer *lexer, enum fh_referent referent_mask)
199 {
200   struct file_handle *handle;
201
202   if (lex_match_id (lexer, "INLINE"))
203     handle = fh_inline_file ();
204   else
205     {
206       if (lex_token (lexer) != T_ID && !lex_is_string (lexer))
207         {
208           lex_error (lexer, _("expecting a file name or handle name"));
209           return NULL;
210         }
211
212       handle = NULL;
213       if (lex_token (lexer) == T_ID)
214         handle = fh_from_id (lex_tokcstr (lexer));
215       if (handle == NULL)
216         {
217           if (lex_token (lexer) != T_ID || lex_tokcstr (lexer)[0] != '#'
218               || settings_get_syntax () != ENHANCED)
219             handle = fh_create_file (NULL, lex_tokcstr (lexer),
220                                      fh_default_properties ());
221           else
222             handle = fh_create_scratch (lex_tokcstr (lexer));
223         }
224       lex_get (lexer);
225     }
226
227   if (!(fh_get_referent (handle) & referent_mask))
228     {
229       msg (SE, _("Handle for %s not allowed here."),
230            referent_name (fh_get_referent (handle)));
231       fh_unref (handle);
232       return NULL;
233     }
234
235   return handle;
236 }
237
238 /*
239    Local variables:
240    mode: c
241    End:
242 */