* variable.h: (struct variable) Rename `reinit' member as `leave' and
[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.h>
28 #include <data/dictionary.h>
29 #include <data/variable.h>
30 #include <language/command.h>
31 #include <language/data-io/data-list.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   /* FIXME: This is an ugly kluge. */
227   for (i = 0; i < f_trns; i++)
228     if (t_trns[i].proc == repeating_data_trns_proc)
229       repeating_data_set_write_case (t_trns[i].private, write_case, wc_data);
230
231   init_case (inp, c);
232   for (;;)
233     {
234       /* Perform transformations on `blank' case. */
235       for (i = 0; i < f_trns; )
236         {
237           int code;
238
239           if (t_trns[i].proc == end_case_trns_proc) 
240             {
241               cases_written++;
242               if (!write_case (wc_data))
243                 return false;
244               clear_case (inp, c);
245               i++;
246               continue;
247             }
248
249           code = t_trns[i].proc (t_trns[i].private, c, cases_written + 1);
250           switch (code)
251             {
252             case TRNS_CONTINUE:
253               i++;
254               break;
255
256             case TRNS_DROP_CASE:
257               abort ();
258
259             case TRNS_ERROR:
260               return false;
261
262             case TRNS_NEXT_CASE:
263               goto next_case;
264
265             case TRNS_END_FILE:
266               return true;
267
268             default:
269               i = code;
270               break;
271             }
272         }
273
274       /* Write the case if appropriate. */
275       if (!end_case) 
276         {
277           cases_written++;
278           if (!write_case (wc_data))
279             return false;
280         }
281
282       /* Blank out the case for the next iteration. */
283     next_case:
284       clear_case (inp, c);
285     }
286 }
287
288 /* Destroys an INPUT PROGRAM source. */
289 static void
290 input_program_source_destroy (struct case_source *source)
291 {
292   struct input_program_pgm *inp = source->aux;
293
294   cancel_transformations ();
295
296   if (inp != NULL) 
297     {
298       free (inp->init);
299       free (inp);
300     }
301 }
302
303 static const struct case_source_class input_program_source_class =
304   {
305     "INPUT PROGRAM",
306     NULL,
307     input_program_source_read,
308     input_program_source_destroy,
309   };
310 \f
311 int
312 cmd_end_case (void)
313 {
314   assert (in_input_program ());
315   add_transformation (end_case_trns_proc, NULL, NULL);
316
317   return lex_end_of_command ();
318 }
319
320 /* Should never be called, because this is handled in
321    input_program_source_read(). */
322 int
323 end_case_trns_proc (void *trns_ UNUSED, struct ccase *c UNUSED,
324                     int case_num UNUSED)
325 {
326   abort ();
327 }
328
329 /* REREAD transformation. */
330 struct reread_trns
331   {
332     struct dfm_reader *reader;  /* File to move file pointer back on. */
333     struct expression *column;  /* Column to reset file pointer to. */
334   };
335
336 /* Parses REREAD command. */
337 int
338 cmd_reread (void)
339 {
340   struct file_handle *fh;       /* File to be re-read. */
341   struct expression *e;         /* Expression for column to set. */
342   struct reread_trns *t;        /* Created transformation. */
343
344   fh = fh_get_default_handle ();
345   e = NULL;
346   while (token != '.')
347     {
348       if (lex_match_id ("COLUMN"))
349         {
350           lex_match ('=');
351           
352           if (e)
353             {
354               msg (SE, _("COLUMN subcommand multiply specified."));
355               expr_free (e);
356               return CMD_CASCADING_FAILURE;
357             }
358           
359           e = expr_parse (default_dict, EXPR_NUMBER);
360           if (!e)
361             return CMD_CASCADING_FAILURE;
362         }
363       else if (lex_match_id ("FILE"))
364         {
365           lex_match ('=');
366           fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
367           if (fh == NULL)
368             {
369               expr_free (e);
370               return CMD_CASCADING_FAILURE;
371             }
372           lex_get ();
373         }
374       else
375         {
376           lex_error (NULL);
377           expr_free (e);
378         }
379     }
380
381   t = xmalloc (sizeof *t);
382   t->reader = dfm_open_reader (fh);
383   t->column = e;
384   add_transformation (reread_trns_proc, reread_trns_free, t);
385
386   return CMD_SUCCESS;
387 }
388
389 /* Executes a REREAD transformation. */
390 static int
391 reread_trns_proc (void *t_, struct ccase *c, int case_num)
392 {
393   struct reread_trns *t = t_;
394
395   if (t->column == NULL)
396     dfm_reread_record (t->reader, 1);
397   else
398     {
399       double column = expr_evaluate_num (t->column, c, case_num);
400       if (!finite (column) || column < 1)
401         {
402           msg (SE, _("REREAD: Column numbers must be positive finite "
403                "numbers.  Column set to 1."));
404           dfm_reread_record (t->reader, 1);
405         }
406       else
407         dfm_reread_record (t->reader, column);
408     }
409   return TRNS_CONTINUE;
410 }
411
412 /* Frees a REREAD transformation.
413    Returns true if successful, false if an I/O error occurred. */
414 static bool
415 reread_trns_free (void *t_)
416 {
417   struct reread_trns *t = t_;
418   expr_free (t->column);
419   dfm_close_reader (t->reader);
420   return true;
421 }
422
423 /* Parses END FILE command. */
424 int
425 cmd_end_file (void)
426 {
427   assert (in_input_program ());
428
429   add_transformation (end_file_trns_proc, NULL, NULL);
430
431   return lex_end_of_command ();
432 }
433
434 /* Executes an END FILE transformation. */
435 static int
436 end_file_trns_proc (void *trns_ UNUSED, struct ccase *c UNUSED,
437                     int case_num UNUSED)
438 {
439   return TRNS_END_FILE;
440 }