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