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