425c9362931336491abd4641e4f175a4c178af94
[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 /* Array that tells INPUT PROGRAM how to initialize each `union
50    value'.  */
51 static enum value_init_type *inp_init;
52
53 /* Number of bytes allocated for inp_init. */
54 static size_t inp_nval;
55
56 static int end_case_trns_proc (struct trns_header *, struct ccase *);
57 static int end_file_trns_proc (struct trns_header * t, struct ccase * c);
58 static int reread_trns_proc (struct trns_header *, struct ccase *);
59 static void reread_trns_free (struct trns_header *);
60
61 int
62 cmd_input_program (void)
63 {
64   lex_match_id ("INPUT");
65   lex_match_id ("PROGRAM");
66   discard_variables ();
67
68   vfm_source = &input_program_source;
69
70   return lex_end_of_command ();
71 }
72
73 int
74 cmd_end_input_program (void)
75 {
76   size_t i;
77
78   lex_match_id ("END");
79   lex_match_id ("INPUT");
80   lex_match_id ("PROGRAM");
81
82   if (vfm_source != &input_program_source)
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 and more-mundane
93      transformations. */
94   f_trns = n_trns;
95
96   /* Figure out how to initialize temp_case. */
97   inp_nval = dict_get_next_value_idx (default_dict);
98   inp_init = xmalloc (inp_nval * sizeof *inp_init);
99   for (i = 0; i < inp_nval; i++)
100     inp_init[i] = -1;
101   for (i = 0; i < dict_get_var_cnt (default_dict); i++)
102     {
103       struct variable *var = dict_get_var (default_dict, i);
104       enum value_init_type value_init;
105       size_t j;
106       
107       value_init = var->type == NUMERIC ? INP_NUMERIC : INP_STRING;
108       value_init |= var->reinit ? INP_REINIT : INP_INIT_ONCE;
109
110       for (j = 0; j < var->nv; j++)
111         inp_init[j + var->fv] = value_init;
112     }
113   for (i = 0; i < inp_nval; i++)
114     assert (inp_init[i] != -1);
115
116   return lex_end_of_command ();
117 }
118
119 /* Initializes temp_case.  Called before the first case is read. */
120 static void
121 init_case (void)
122 {
123   size_t i;
124
125   for (i = 0; i < inp_nval; i++)
126     switch (inp_init[i]) 
127       {
128       case INP_NUMERIC | INP_INIT_ONCE:
129         temp_case->data[i].f = 0.0;
130         break;
131       case INP_NUMERIC | INP_REINIT:
132         temp_case->data[i].f = SYSMIS;
133         break;
134       case INP_STRING | INP_INIT_ONCE:
135       case INP_STRING | INP_REINIT:
136         memset (temp_case->data[i].s, ' ', sizeof temp_case->data[i].s);
137         break;
138       default:
139         assert (0);
140       }
141 }
142
143 /* Clears temp_case.  Called between reading successive records. */
144 static void
145 clear_case (void)
146 {
147   size_t i;
148
149   for (i = 0; i < inp_nval; i++)
150     switch (inp_init[i]) 
151       {
152       case INP_NUMERIC | INP_INIT_ONCE:
153         break;
154       case INP_NUMERIC | INP_REINIT:
155         temp_case->data[i].f = SYSMIS;
156         break;
157       case INP_STRING | INP_INIT_ONCE:
158         break;
159       case INP_STRING | INP_REINIT:
160         memset (temp_case->data[i].s, ' ', sizeof temp_case->data[i].s);
161         break;
162       default:
163         assert (0);
164       }
165 }
166
167 /* Executes each transformation in turn on a `blank' case.  When a
168    transformation fails, returning -2, then that's the end of the
169    file.  -1 means go on to the next transformation.  Otherwise the
170    return value is the index of the transformation to go to next. */
171 static void
172 input_program_source_read (write_case_func *write_case,
173                            write_case_data wc_data)
174 {
175   int i;
176
177   /* Nonzero if there were any END CASE commands in the set of
178      transformations. */
179   int end_case = 0;
180
181   /* We don't automatically write out cases if the user took over
182      that prerogative.  */
183   for (i = 0; i < f_trns; i++)
184     if (t_trns[i]->proc == end_case_trns_proc)
185       end_case = 1;
186
187   /* FIXME: This code should not be necessary.  It is an ugly
188      kluge. */
189   for (i = 0; i < f_trns; i++)
190     if (t_trns[i]->proc == repeating_data_trns_proc)
191       repeating_data_set_write_case (t_trns[i], write_case, wc_data);
192
193   init_case ();
194   for (;;)
195     {
196       /* Index of current transformation. */
197       int i;
198
199       /* Return value of last-called transformation. */
200       int code;
201
202       debug_printf (("input-program: "));
203
204       /* Perform transformations on `blank' case. */
205       for (i = 0; i < f_trns;)
206         {
207 #if DEBUGGING
208           printf ("/%d", i);
209           if (t_trns[i]->proc == end_case_trns_proc)
210             printf ("\n");
211 #endif
212
213           if (t_trns[i]->proc == end_case_trns_proc) 
214             {
215               if (!write_case (wc_data))
216                 return;
217               clear_case ();
218               i++;
219               continue;
220             }
221
222           code = t_trns[i]->proc (t_trns[i], temp_case);
223           switch (code)
224             {
225             case -1:
226               i++;
227               break;
228             case -2:
229               return;
230             case -3:
231               goto next_case;
232             default:
233               i = code;
234               break;
235             }
236         }
237
238 #if DEBUGGING
239       if (!end_case)
240         printf ("\n");
241 #endif
242
243       /* Write the case if appropriate. */
244       if (!end_case)
245         if (!write_case (wc_data))
246           return;
247
248       /* Blank out the case for the next iteration. */
249     next_case:
250       clear_case ();
251     }
252 }
253
254 static void
255 input_program_source_destroy_source (void)
256 {
257   cancel_transformations ();
258   free (inp_init);
259   inp_init = NULL;
260 }
261
262 struct case_stream input_program_source =
263   {
264     NULL,
265     input_program_source_read,
266     NULL,
267     NULL,
268     input_program_source_destroy_source,
269     NULL,
270     "INPUT PROGRAM",
271   };
272 \f
273 int
274 cmd_end_case (void)
275 {
276   struct trns_header *t;
277
278   lex_match_id ("END");
279   lex_match_id ("CASE");
280
281   if (vfm_source != &input_program_source)
282     {
283       msg (SE, _("This command may only be executed between INPUT PROGRAM "
284                  "and END INPUT PROGRAM."));
285       return CMD_FAILURE;
286     }
287
288   t = xmalloc (sizeof *t);
289   t->proc = end_case_trns_proc;
290   t->free = NULL;
291   add_transformation ((struct trns_header *) t);
292
293   return lex_end_of_command ();
294 }
295
296 int
297 end_case_trns_proc (struct trns_header *t UNUSED, struct ccase * c UNUSED)
298 {
299   assert (0);
300 }
301
302 /* REREAD transformation. */
303 struct reread_trns
304   {
305     struct trns_header h;
306
307     struct file_handle *handle; /* File to move file pointer back on. */
308     struct expression *column;  /* Column to reset file pointer to. */
309   };
310
311 /* Parses REREAD command. */
312 int
313 cmd_reread (void)
314 {
315   /* File to be re-read. */
316   struct file_handle *h;
317   
318   /* Expression for column to set file pointer to. */
319   struct expression *e;
320
321   /* Created transformation. */
322   struct reread_trns *t;
323
324   lex_match_id ("REREAD");
325
326   h = default_handle;
327   e = NULL;
328   while (token != '.')
329     {
330       if (lex_match_id ("COLUMN"))
331         {
332           lex_match ('=');
333           
334           if (e)
335             {
336               msg (SE, _("COLUMN subcommand multiply specified."));
337               expr_free (e);
338               return CMD_FAILURE;
339             }
340           
341           e = expr_parse (PXP_NUMERIC);
342           if (!e)
343             return CMD_FAILURE;
344         }
345       else if (lex_match_id ("FILE"))
346         {
347           lex_match ('=');
348           if (token != T_ID)
349             {
350               lex_error (_("expecting file handle name"));
351               expr_free (e);
352               return CMD_FAILURE;
353             }
354           h = fh_get_handle_by_name (tokid);
355           if (!h)
356             {
357               expr_free (e);
358               return CMD_FAILURE;
359             }
360           lex_get ();
361         }
362       else
363         {
364           lex_error (NULL);
365           expr_free (e);
366         }
367     }
368
369   t = xmalloc (sizeof *t);
370   t->h.proc = reread_trns_proc;
371   t->h.free = reread_trns_free;
372   t->handle = h;
373   t->column = e;
374   add_transformation ((struct trns_header *) t);
375
376   return CMD_SUCCESS;
377 }
378
379 static int
380 reread_trns_proc (struct trns_header * pt, struct ccase * c)
381 {
382   struct reread_trns *t = (struct reread_trns *) pt;
383
384   if (t->column == NULL)
385     dfm_bkwd_record (t->handle, 1);
386   else
387     {
388       union value column;
389
390       expr_evaluate (t->column, c, &column);
391       if (!finite (column.f) || column.f < 1)
392         {
393           msg (SE, _("REREAD: Column numbers must be positive finite "
394                "numbers.  Column set to 1."));
395           dfm_bkwd_record (t->handle, 1);
396         }
397       else
398         dfm_bkwd_record (t->handle, column.f);
399     }
400   return -1;
401 }
402
403 static void
404 reread_trns_free (struct trns_header * t)
405 {
406   expr_free (((struct reread_trns *) t)->column);
407 }
408
409 /* Parses END FILE command. */
410 int
411 cmd_end_file (void)
412 {
413   struct trns_header *t;
414
415   lex_match_id ("END");
416   lex_match_id ("FILE");
417
418   if (vfm_source != &input_program_source)
419     {
420       msg (SE, _("This command may only be executed between INPUT PROGRAM "
421                  "and END INPUT PROGRAM."));
422       return CMD_FAILURE;
423     }
424
425   t = xmalloc (sizeof *t);
426   t->proc = end_file_trns_proc;
427   t->free = NULL;
428   add_transformation ((struct trns_header *) t);
429
430   return lex_end_of_command ();
431 }
432
433 static int
434 end_file_trns_proc (struct trns_header * t UNUSED, struct ccase * c UNUSED)
435 {
436 #if DEBUGGING
437   printf ("END FILE\n");
438 #endif
439   return -2;
440 }