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