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