Beginning of VFM cleanup.
[pspp-builds.git] / src / 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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <float.h>
23 #include <stdlib.h>
24 #include "alloc.h"
25 #include "command.h"
26 #include "data-list.h"
27 #include "dfm.h"
28 #include "error.h"
29 #include "expr.h"
30 #include "file-handle.h"
31 #include "lexer.h"
32 #include "misc.h"
33 #include "str.h"
34 #include "var.h"
35 #include "vfm.h"
36
37 #include "debug-print.h"
38
39 /* Indicates how a `union value' should be initialized. */
40 enum value_init_type
41   {
42     INP_NUMERIC = 01,           /* Numeric. */
43     INP_STRING = 0,             /* String. */
44     
45     INP_INIT_ONCE = 02,         /* Initialize only once. */
46     INP_REINIT = 0,             /* Reinitialize for each iteration. */
47   };
48
49 struct input_program_pgm 
50   {
51     enum value_init_type *init; /* How to initialize each `union value'. */
52     size_t init_cnt;            /* Number of elements in inp_init. */
53   };
54
55 static int end_case_trns_proc (struct trns_header *, struct ccase *);
56 static int end_file_trns_proc (struct trns_header * t, struct ccase * c);
57 static int reread_trns_proc (struct trns_header *, struct ccase *);
58 static void reread_trns_free (struct trns_header *);
59
60 int
61 cmd_input_program (void)
62 {
63   lex_match_id ("INPUT");
64   lex_match_id ("PROGRAM");
65   discard_variables ();
66
67   vfm_source = create_case_source (&input_program_source_class, NULL);
68
69   return lex_end_of_command ();
70 }
71
72 int
73 cmd_end_input_program (void)
74 {
75   struct input_program_pgm *inp;
76   size_t i;
77
78   lex_match_id ("END");
79   lex_match_id ("INPUT");
80   lex_match_id ("PROGRAM");
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_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 temp_case. */
97   inp = xmalloc (sizeof *inp);
98   inp->init_cnt = dict_get_next_value_idx (default_dict);
99   inp->init = xmalloc (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
117   /* Put inp into vfm_source for later use. */
118   vfm_source->aux = inp;
119
120   return lex_end_of_command ();
121 }
122
123 /* Initializes temp_case.  Called before the first case is read. */
124 static void
125 init_case (struct input_program_pgm *inp)
126 {
127   size_t i;
128
129   for (i = 0; i < inp->init_cnt; i++)
130     switch (inp->init[i]) 
131       {
132       case INP_NUMERIC | INP_INIT_ONCE:
133         temp_case->data[i].f = 0.0;
134         break;
135       case INP_NUMERIC | INP_REINIT:
136         temp_case->data[i].f = SYSMIS;
137         break;
138       case INP_STRING | INP_INIT_ONCE:
139       case INP_STRING | INP_REINIT:
140         memset (temp_case->data[i].s, ' ', sizeof temp_case->data[i].s);
141         break;
142       default:
143         assert (0);
144       }
145 }
146
147 /* Clears temp_case.  Called between reading successive records. */
148 static void
149 clear_case (struct input_program_pgm *inp)
150 {
151   size_t i;
152
153   for (i = 0; i < inp->init_cnt; i++)
154     switch (inp->init[i]) 
155       {
156       case INP_NUMERIC | INP_INIT_ONCE:
157         break;
158       case INP_NUMERIC | INP_REINIT:
159         temp_case->data[i].f = SYSMIS;
160         break;
161       case INP_STRING | INP_INIT_ONCE:
162         break;
163       case INP_STRING | INP_REINIT:
164         memset (temp_case->data[i].s, ' ', sizeof temp_case->data[i].s);
165         break;
166       default:
167         assert (0);
168       }
169 }
170
171 /* Executes each transformation in turn on a `blank' case.  When a
172    transformation fails, returning -2, then that's the end of the
173    file.  -1 means go on to the next transformation.  Otherwise the
174    return value is the index of the transformation to go to next. */
175 static void
176 input_program_source_read (struct case_source *source,
177                            write_case_func *write_case,
178                            write_case_data wc_data)
179 {
180   struct input_program_pgm *inp = source->aux;
181   int 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   assert (inp != NULL);
189   
190   /* Figure end_case. */
191   for (i = 0; i < f_trns; i++)
192     if (t_trns[i]->proc == end_case_trns_proc)
193       end_case = 1;
194
195   /* FIXME: This code should not be necessary.  It is an ugly
196      kluge. */
197   for (i = 0; i < f_trns; i++)
198     if (t_trns[i]->proc == repeating_data_trns_proc)
199       repeating_data_set_write_case (t_trns[i], write_case, wc_data);
200
201   init_case (inp);
202   for (;;)
203     {
204       /* Index of current transformation. */
205       int i;
206
207       /* Return value of last-called transformation. */
208       int code;
209
210       debug_printf (("input-program: "));
211
212       /* Perform transformations on `blank' case. */
213       for (i = 0; i < f_trns;)
214         {
215 #if DEBUGGING
216           printf ("/%d", i);
217           if (t_trns[i]->proc == end_case_trns_proc)
218             printf ("\n");
219 #endif
220
221           if (t_trns[i]->proc == end_case_trns_proc) 
222             {
223               if (!write_case (wc_data))
224                 return;
225               clear_case (inp);
226               i++;
227               continue;
228             }
229
230           code = t_trns[i]->proc (t_trns[i], temp_case);
231           switch (code)
232             {
233             case -1:
234               i++;
235               break;
236             case -2:
237               return;
238             case -3:
239               goto next_case;
240             default:
241               i = code;
242               break;
243             }
244         }
245
246 #if DEBUGGING
247       if (!end_case)
248         printf ("\n");
249 #endif
250
251       /* Write the case if appropriate. */
252       if (!end_case)
253         if (!write_case (wc_data))
254           return;
255
256       /* Blank out the case for the next iteration. */
257     next_case:
258       clear_case (inp);
259     }
260 }
261
262 static void
263 input_program_source_destroy (struct case_source *source)
264 {
265   struct input_program_pgm *inp = source->aux;
266
267   cancel_transformations ();
268
269   if (inp != NULL) 
270     {
271       free (inp->init);
272       free (inp);
273     }
274 }
275
276 const struct case_source_class input_program_source_class =
277   {
278     "INPUT PROGRAM",
279     input_program_source_read,
280     input_program_source_destroy,
281   };
282 \f
283 int
284 cmd_end_case (void)
285 {
286   struct trns_header *t;
287
288   lex_match_id ("END");
289   lex_match_id ("CASE");
290
291   if (!case_source_is_class (vfm_source, &input_program_source_class))
292     {
293       msg (SE, _("This command may only be executed between INPUT PROGRAM "
294                  "and END INPUT PROGRAM."));
295       return CMD_FAILURE;
296     }
297
298   t = xmalloc (sizeof *t);
299   t->proc = end_case_trns_proc;
300   t->free = NULL;
301   add_transformation ((struct trns_header *) t);
302
303   return lex_end_of_command ();
304 }
305
306 int
307 end_case_trns_proc (struct trns_header *t UNUSED, struct ccase * c UNUSED)
308 {
309   assert (0);
310 }
311
312 /* REREAD transformation. */
313 struct reread_trns
314   {
315     struct trns_header h;
316
317     struct file_handle *handle; /* File to move file pointer back on. */
318     struct expression *column;  /* Column to reset file pointer to. */
319   };
320
321 /* Parses REREAD command. */
322 int
323 cmd_reread (void)
324 {
325   /* File to be re-read. */
326   struct file_handle *h;
327   
328   /* Expression for column to set file pointer to. */
329   struct expression *e;
330
331   /* Created transformation. */
332   struct reread_trns *t;
333
334   lex_match_id ("REREAD");
335
336   h = default_handle;
337   e = NULL;
338   while (token != '.')
339     {
340       if (lex_match_id ("COLUMN"))
341         {
342           lex_match ('=');
343           
344           if (e)
345             {
346               msg (SE, _("COLUMN subcommand multiply specified."));
347               expr_free (e);
348               return CMD_FAILURE;
349             }
350           
351           e = expr_parse (PXP_NUMERIC);
352           if (!e)
353             return CMD_FAILURE;
354         }
355       else if (lex_match_id ("FILE"))
356         {
357           lex_match ('=');
358           if (token != T_ID)
359             {
360               lex_error (_("expecting file handle name"));
361               expr_free (e);
362               return CMD_FAILURE;
363             }
364           h = fh_get_handle_by_name (tokid);
365           if (!h)
366             {
367               expr_free (e);
368               return CMD_FAILURE;
369             }
370           lex_get ();
371         }
372       else
373         {
374           lex_error (NULL);
375           expr_free (e);
376         }
377     }
378
379   t = xmalloc (sizeof *t);
380   t->h.proc = reread_trns_proc;
381   t->h.free = reread_trns_free;
382   t->handle = h;
383   t->column = e;
384   add_transformation ((struct trns_header *) t);
385
386   return CMD_SUCCESS;
387 }
388
389 static int
390 reread_trns_proc (struct trns_header * pt, struct ccase * c)
391 {
392   struct reread_trns *t = (struct reread_trns *) pt;
393
394   if (t->column == NULL)
395     dfm_bkwd_record (t->handle, 1);
396   else
397     {
398       union value column;
399
400       expr_evaluate (t->column, c, &column);
401       if (!finite (column.f) || column.f < 1)
402         {
403           msg (SE, _("REREAD: Column numbers must be positive finite "
404                "numbers.  Column set to 1."));
405           dfm_bkwd_record (t->handle, 1);
406         }
407       else
408         dfm_bkwd_record (t->handle, column.f);
409     }
410   return -1;
411 }
412
413 static void
414 reread_trns_free (struct trns_header * t)
415 {
416   expr_free (((struct reread_trns *) t)->column);
417 }
418
419 /* Parses END FILE command. */
420 int
421 cmd_end_file (void)
422 {
423   struct trns_header *t;
424
425   lex_match_id ("END");
426   lex_match_id ("FILE");
427
428   if (!case_source_is_class (vfm_source, &input_program_source_class))
429     {
430       msg (SE, _("This command may only be executed between INPUT PROGRAM "
431                  "and END INPUT PROGRAM."));
432       return CMD_FAILURE;
433     }
434
435   t = xmalloc (sizeof *t);
436   t->proc = end_file_trns_proc;
437   t->free = NULL;
438   add_transformation ((struct trns_header *) t);
439
440   return lex_end_of_command ();
441 }
442
443 static int
444 end_file_trns_proc (struct trns_header * t UNUSED, struct ccase * c UNUSED)
445 {
446 #if DEBUGGING
447   printf ("END FILE\n");
448 #endif
449   return -2;
450 }