99bfb23bca7b83bf0abb9bbce91b948764bfec94
[pspp-builds.git] / src / language / data-io / inpt-pgm.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "message.h"
22 #include <float.h>
23 #include <stdlib.h>
24 #include "alloc.h"
25 #include "case.h"
26 #include "command.h"
27 #include "data-list.h"
28 #include "data-reader.h"
29 #include "dictionary.h"
30 #include "message.h"
31 #include "expressions/public.h"
32 #include "file-handle.h"
33 #include "lexer.h"
34 #include "misc.h"
35 #include "str.h"
36 #include "variable.h"
37 #include "procedure.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41
42 #include "debug-print.h"
43
44 /* Indicates how a `union value' should be initialized. */
45 enum value_init_type
46   {
47     INP_NUMERIC = 01,           /* Numeric. */
48     INP_STRING = 0,             /* String. */
49     
50     INP_INIT_ONCE = 02,         /* Initialize only once. */
51     INP_REINIT = 0,             /* Reinitialize for each iteration. */
52   };
53
54 struct input_program_pgm 
55   {
56     enum value_init_type *init; /* How to initialize each `union value'. */
57     size_t init_cnt;            /* Number of elements in inp_init. */
58     size_t case_size;           /* Size of case in bytes. */
59   };
60
61 static trns_proc_func end_case_trns_proc, reread_trns_proc, end_file_trns_proc;
62 static trns_free_func reread_trns_free;
63
64 int
65 cmd_input_program (void)
66 {
67   discard_variables ();
68
69   /* FIXME: we shouldn't do this here, but I'm afraid that other
70      code will check the class of vfm_source. */
71   vfm_source = create_case_source (&input_program_source_class, NULL);
72
73   return lex_end_of_command ();
74 }
75
76 int
77 cmd_end_input_program (void)
78 {
79   struct input_program_pgm *inp;
80   size_t i;
81
82   if (!case_source_is_class (vfm_source, &input_program_source_class))
83     {
84       msg (SE, _("No matching INPUT PROGRAM command."));
85       return CMD_CASCADING_FAILURE;
86     }
87   
88   if (dict_get_next_value_idx (default_dict) == 0)
89     msg (SW, _("No data-input or transformation commands specified "
90          "between INPUT PROGRAM and END INPUT PROGRAM."));
91
92   /* Mark the boundary between INPUT PROGRAM transformations and
93      ordinary transformations. */
94   f_trns = n_trns;
95
96   /* Figure out how to initialize each input case. */
97   inp = xmalloc (sizeof *inp);
98   inp->init_cnt = dict_get_next_value_idx (default_dict);
99   inp->init = xnmalloc (inp->init_cnt, sizeof *inp->init);
100   for (i = 0; i < inp->init_cnt; i++)
101     inp->init[i] = -1;
102   for (i = 0; i < dict_get_var_cnt (default_dict); i++)
103     {
104       struct variable *var = dict_get_var (default_dict, i);
105       enum value_init_type value_init;
106       size_t j;
107       
108       value_init = var->type == NUMERIC ? INP_NUMERIC : INP_STRING;
109       value_init |= var->reinit ? INP_REINIT : INP_INIT_ONCE;
110
111       for (j = 0; j < var->nv; j++)
112         inp->init[j + var->fv] = value_init;
113     }
114   for (i = 0; i < inp->init_cnt; i++)
115     assert (inp->init[i] != -1);
116   inp->case_size = dict_get_case_size (default_dict);
117
118   /* Put inp into vfm_source for later use. */
119   vfm_source->aux = inp;
120
121   return lex_end_of_command ();
122 }
123
124 /* Initializes case C.  Called before the first case is read. */
125 static void
126 init_case (const struct input_program_pgm *inp, struct ccase *c)
127 {
128   size_t i;
129
130   for (i = 0; i < inp->init_cnt; i++)
131     switch (inp->init[i]) 
132       {
133       case INP_NUMERIC | INP_INIT_ONCE:
134         case_data_rw (c, i)->f = 0.0;
135         break;
136       case INP_NUMERIC | INP_REINIT:
137         case_data_rw (c, i)->f = SYSMIS;
138         break;
139       case INP_STRING | INP_INIT_ONCE:
140       case INP_STRING | INP_REINIT:
141         memset (case_data_rw (c, i)->s, ' ', sizeof case_data_rw (c, i)->s);
142         break;
143       default:
144         assert (0);
145       }
146 }
147
148 /* Clears case C.  Called between reading successive records. */
149 static void
150 clear_case (const struct input_program_pgm *inp, struct ccase *c)
151 {
152   size_t i;
153
154   for (i = 0; i < inp->init_cnt; i++)
155     switch (inp->init[i]) 
156       {
157       case INP_NUMERIC | INP_INIT_ONCE:
158         break;
159       case INP_NUMERIC | INP_REINIT:
160         case_data_rw (c, i)->f = SYSMIS;
161         break;
162       case INP_STRING | INP_INIT_ONCE:
163         break;
164       case INP_STRING | INP_REINIT:
165         memset (case_data_rw (c, i)->s, ' ', sizeof case_data_rw (c, i)->s);
166         break;
167       default:
168         assert (0);
169       }
170 }
171
172 /* Executes each transformation in turn on a `blank' case.
173    Returns true if successful, false if an I/O error occurred. */
174 static bool
175 input_program_source_read (struct case_source *source,
176                            struct ccase *c,
177                            write_case_func *write_case,
178                            write_case_data wc_data)
179 {
180   struct input_program_pgm *inp = source->aux;
181   size_t i;
182
183   /* Nonzero if there were any END CASE commands in the set of
184      transformations.  If so, we don't automatically write out
185      cases. */
186   int end_case = 0;
187
188   /* FIXME?  This is the number of cases sent out of the input
189      program, not the number of cases written to the procedure.
190      The difference should only show up in $CASENUM in COMPUTE.
191      We should check behavior against SPSS. */
192   int cases_written = 0;
193
194   assert (inp != NULL);
195
196   /* Figure end_case. */
197   for (i = 0; i < f_trns; i++)
198     if (t_trns[i].proc == end_case_trns_proc)
199       end_case = 1;
200
201   /* FIXME: This is an ugly kluge. */
202   for (i = 0; i < f_trns; i++)
203     if (t_trns[i].proc == repeating_data_trns_proc)
204       repeating_data_set_write_case (t_trns[i].private, write_case, wc_data);
205
206   init_case (inp, c);
207   for (;;)
208     {
209       /* Perform transformations on `blank' case. */
210       for (i = 0; i < f_trns; )
211         {
212           int code;
213
214           if (t_trns[i].proc == end_case_trns_proc) 
215             {
216               cases_written++;
217               if (!write_case (wc_data))
218                 return false;
219               clear_case (inp, c);
220               i++;
221               continue;
222             }
223
224           code = t_trns[i].proc (t_trns[i].private, c, cases_written + 1);
225           switch (code)
226             {
227             case TRNS_CONTINUE:
228               i++;
229               break;
230
231             case TRNS_DROP_CASE:
232               abort ();
233
234             case TRNS_ERROR:
235               return false;
236
237             case TRNS_NEXT_CASE:
238               goto next_case;
239
240             case TRNS_END_FILE:
241               return true;
242
243             default:
244               i = code;
245               break;
246             }
247         }
248
249       /* Write the case if appropriate. */
250       if (!end_case) 
251         {
252           cases_written++;
253           if (!write_case (wc_data))
254             return false;
255         }
256
257       /* Blank out the case for the next iteration. */
258     next_case:
259       clear_case (inp, c);
260     }
261 }
262
263 /* Destroys an INPUT PROGRAM source. */
264 static void
265 input_program_source_destroy (struct case_source *source)
266 {
267   struct input_program_pgm *inp = source->aux;
268
269   cancel_transformations ();
270
271   if (inp != NULL) 
272     {
273       free (inp->init);
274       free (inp);
275     }
276 }
277
278 const struct case_source_class input_program_source_class =
279   {
280     "INPUT PROGRAM",
281     NULL,
282     input_program_source_read,
283     input_program_source_destroy,
284   };
285 \f
286 int
287 cmd_end_case (void)
288 {
289   if (!case_source_is_class (vfm_source, &input_program_source_class))
290     {
291       msg (SE, _("This command may only be executed between INPUT PROGRAM "
292                  "and END INPUT PROGRAM."));
293       return CMD_CASCADING_FAILURE;
294     }
295
296   add_transformation (end_case_trns_proc, NULL, NULL);
297
298   return lex_end_of_command ();
299 }
300
301 /* Should never be called, because this is handled in
302    input_program_source_read(). */
303 int
304 end_case_trns_proc (void *trns_ UNUSED, struct ccase *c UNUSED,
305                     int case_num UNUSED)
306 {
307   abort ();
308 }
309
310 /* REREAD transformation. */
311 struct reread_trns
312   {
313     struct dfm_reader *reader;  /* File to move file pointer back on. */
314     struct expression *column;  /* Column to reset file pointer to. */
315   };
316
317 /* Parses REREAD command. */
318 int
319 cmd_reread (void)
320 {
321   struct file_handle *fh;       /* File to be re-read. */
322   struct expression *e;         /* Expression for column to set. */
323   struct reread_trns *t;        /* Created transformation. */
324
325   fh = fh_get_default_handle ();
326   e = NULL;
327   while (token != '.')
328     {
329       if (lex_match_id ("COLUMN"))
330         {
331           lex_match ('=');
332           
333           if (e)
334             {
335               msg (SE, _("COLUMN subcommand multiply specified."));
336               expr_free (e);
337               return CMD_CASCADING_FAILURE;
338             }
339           
340           e = expr_parse (default_dict, EXPR_NUMBER);
341           if (!e)
342             return CMD_CASCADING_FAILURE;
343         }
344       else if (lex_match_id ("FILE"))
345         {
346           lex_match ('=');
347           fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
348           if (fh == NULL)
349             {
350               expr_free (e);
351               return CMD_CASCADING_FAILURE;
352             }
353           lex_get ();
354         }
355       else
356         {
357           lex_error (NULL);
358           expr_free (e);
359         }
360     }
361
362   t = xmalloc (sizeof *t);
363   t->reader = dfm_open_reader (fh);
364   t->column = e;
365   add_transformation (reread_trns_proc, reread_trns_free, t);
366
367   return CMD_SUCCESS;
368 }
369
370 /* Executes a REREAD transformation. */
371 static int
372 reread_trns_proc (void *t_, struct ccase *c, int case_num)
373 {
374   struct reread_trns *t = t_;
375
376   if (t->column == NULL)
377     dfm_reread_record (t->reader, 1);
378   else
379     {
380       double column = expr_evaluate_num (t->column, c, case_num);
381       if (!finite (column) || column < 1)
382         {
383           msg (SE, _("REREAD: Column numbers must be positive finite "
384                "numbers.  Column set to 1."));
385           dfm_reread_record (t->reader, 1);
386         }
387       else
388         dfm_reread_record (t->reader, column);
389     }
390   return TRNS_CONTINUE;
391 }
392
393 /* Frees a REREAD transformation.
394    Returns true if successful, false if an I/O error occurred. */
395 static bool
396 reread_trns_free (void *t_)
397 {
398   struct reread_trns *t = t_;
399   expr_free (t->column);
400   dfm_close_reader (t->reader);
401   return true;
402 }
403
404 /* Parses END FILE command. */
405 int
406 cmd_end_file (void)
407 {
408   if (!case_source_is_class (vfm_source, &input_program_source_class))
409     {
410       msg (SE, _("This command may only be executed between INPUT PROGRAM "
411                  "and END INPUT PROGRAM."));
412       return CMD_CASCADING_FAILURE;
413     }
414
415   add_transformation (end_file_trns_proc, NULL, NULL);
416
417   return lex_end_of_command ();
418 }
419
420 /* Executes an END FILE transformation. */
421 static int
422 end_file_trns_proc (void *trns_ UNUSED, struct ccase *c UNUSED,
423                     int case_num UNUSED)
424 {
425   return TRNS_END_FILE;
426 }