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