posix-xprintf-functions.patch from patch #6230.
[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/image/scratch.
45 */
46 /* (declarations) */
47 /* (functions) */
48
49 int
50 cmd_file_handle (struct lexer *lexer, struct dataset *ds)
51 {
52   char handle_name[LONG_NAME_LEN + 1];
53   struct fh_properties properties = *fh_default_properties ();
54
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.s_name == NULL && cmd.mode != FH_SCRATCH)
82     {
83       lex_sbc_missing (lexer, "NAME");
84       goto lossage;
85     }
86
87   switch (cmd.mode)
88     {
89     case FH_CHARACTER:
90       properties.mode = FH_MODE_TEXT;
91       if (cmd.sbc_tabwidth)
92         properties.tab_width = cmd.n_tabwidth[0];
93       break;
94     case FH_IMAGE:
95       properties.mode = FH_MODE_BINARY;
96       if (cmd.n_lrecl[0] == LONG_MIN)
97         msg (SE, _("Fixed-length records were specified on /RECFORM, but "
98                    "record length was not specified on /LRECL.  "
99                    "Assuming %zu-character records."),
100              properties.record_width);
101       else if (cmd.n_lrecl[0] < 1)
102         msg (SE, _("Record length (%ld) must be at least one byte.  "
103                    "Assuming %zu-character records."),
104              cmd.n_lrecl[0], properties.record_width);
105       else
106         properties.record_width = cmd.n_lrecl[0];
107       break;
108     default:
109       NOT_REACHED ();
110     }
111
112   if (cmd.mode != FH_SCRATCH)
113     fh_create_file (handle_name, cmd.s_name, &properties);
114   else
115     fh_create_scratch (handle_name);
116
117   free_file_handle (&cmd);
118   return CMD_SUCCESS;
119
120  lossage:
121   free_file_handle (&cmd);
122   return CMD_CASCADING_FAILURE;
123 }
124
125 int
126 cmd_close_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
127 {
128   struct file_handle *handle;
129
130   if (!lex_force_id (lexer))
131     return CMD_CASCADING_FAILURE;
132   handle = fh_from_id (lex_tokid (lexer));
133   if (handle == NULL)
134     return CMD_CASCADING_FAILURE;
135
136   fh_free (handle);
137
138   return CMD_SUCCESS;
139 }
140
141 /* Returns the name for REFERENT. */
142 static const char *
143 referent_name (enum fh_referent referent)
144 {
145   switch (referent)
146     {
147     case FH_REF_FILE:
148       return _("file");
149     case FH_REF_INLINE:
150       return _("inline file");
151     case FH_REF_SCRATCH:
152       return _("scratch file");
153     default:
154       NOT_REACHED ();
155     }
156 }
157
158 /* Parses a file handle name, which may be a file name as a string
159    or a file handle name as an identifier.  The allowed types of
160    file handle are restricted to those in REFERENT_MASK.  Returns
161    the file handle when successful, a null pointer on failure. */
162 struct file_handle *
163 fh_parse (struct lexer *lexer, enum fh_referent referent_mask)
164 {
165   struct file_handle *handle;
166
167   if (lex_match_id (lexer, "INLINE"))
168     handle = fh_inline_file ();
169   else
170     {
171       if (lex_token (lexer) != T_ID && lex_token (lexer) != T_STRING)
172         {
173           lex_error (lexer, _("expecting a file name or handle name"));
174           return NULL;
175         }
176
177       handle = NULL;
178       if (lex_token (lexer) == T_ID)
179         handle = fh_from_id (lex_tokid (lexer));
180       if (handle == NULL)
181         handle = fh_from_file_name (ds_cstr (lex_tokstr (lexer)));
182       if (handle == NULL)
183         {
184           if (lex_token (lexer) != T_ID || lex_tokid (lexer)[0] != '#' || get_syntax () != ENHANCED)
185             handle = fh_create_file (NULL, ds_cstr (lex_tokstr (lexer)),
186                                      fh_default_properties ());
187           else
188             handle = fh_create_scratch (lex_tokid (lexer));
189         }
190       lex_get (lexer);
191     }
192
193   if (!(fh_get_referent (handle) & referent_mask))
194     {
195       msg (SE, _("Handle for %s not allowed here."),
196            referent_name (fh_get_referent (handle)));
197       return NULL;
198     }
199
200   return handle;
201 }
202
203 /*
204    Local variables:
205    mode: c
206    End:
207 */