Beginning of VFM cleanup.
[pspp-builds.git] / src / flip.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 /* FIXME: does this work with long string variables? */
21
22 #include <config.h>
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <float.h>
27 #include <limits.h>
28 #include <stdlib.h>
29 #include "alloc.h"
30 #include "command.h"
31 #include "error.h"
32 #include "lexer.h"
33 #include "misc.h"
34 #include "settings.h"
35 #include "str.h"
36 #include "var.h"
37 #include "vfm.h"
38
39 /* List of variable names. */
40 struct varname
41   {
42     struct varname *next;
43     char name[9];
44   };
45
46 /* Represents a FLIP input program. */
47 struct flip_pgm 
48   {
49     struct variable **var;      /* Variables to transpose. */
50     int var_cnt;                /* Number of variables. */
51     struct variable *newnames;  /* Variable containing new variable names. */
52     struct varname *new_names_head, *new_names_tail;
53                                 /* New variable names. */
54     int case_count;             /* Number of cases. */
55
56   };
57
58 static void destroy_flip_pgm (struct flip_pgm *flip);
59 static struct case_sink *flip_sink_create (struct flip_pgm *);
60 static const struct case_source_class flip_source_class;
61 static int build_dictionary (struct flip_pgm *flip);
62
63 /* Parses and executes FLIP. */
64 int
65 cmd_flip (void)
66 {
67   struct flip_pgm *flip;
68
69   flip = xmalloc (sizeof *flip);
70   flip->var = NULL;
71   flip->var_cnt = 0;
72   flip->newnames = NULL;
73   flip->new_names_head = flip->new_names_tail = NULL;
74   flip->case_count = 0;
75
76   lex_match_id ("FLIP");
77   lex_match ('/');
78   if (lex_match_id ("VARIABLES"))
79     {
80       lex_match ('=');
81       if (!parse_variables (default_dict, &flip->var, &flip->var_cnt, PV_NO_DUPLICATE))
82         return CMD_FAILURE;
83       lex_match ('/');
84     }
85   else
86     dict_get_vars (default_dict, &flip->var, &flip->var_cnt, 1u << DC_SYSTEM);
87
88   lex_match ('/');
89   if (lex_match_id ("NEWNAMES"))
90     {
91       lex_match ('=');
92       flip->newnames = parse_variable ();
93       if (!flip->newnames)
94         goto error;
95     }
96   else
97     flip->newnames = dict_lookup_var (default_dict, "CASE_LBL");
98
99   if (flip->newnames)
100     {
101       int i;
102       
103       for (i = 0; i < flip->var_cnt; i++)
104         if (flip->var[i] == flip->newnames)
105           {
106             memmove (&flip->var[i], &flip->var[i + 1], sizeof *flip->var * (flip->var_cnt - i - 1));
107             flip->var_cnt--;
108             break;
109           }
110     }
111
112   flip->case_count = 0;
113   temp_trns = temporary = 0;
114   vfm_sink = flip_sink_create (flip);
115   flip->new_names_tail = NULL;
116   procedure (NULL, NULL, NULL, NULL);
117
118   dict_clear (default_dict);
119   if (!build_dictionary (flip))
120     {
121       discard_variables ();
122       goto error;
123     }
124
125   return lex_end_of_command ();
126
127  error:
128   destroy_flip_pgm (flip);
129   return CMD_FAILURE;
130 }
131
132 static void
133 destroy_flip_pgm (struct flip_pgm *flip) 
134 {
135   struct varname *iter, *next;
136   
137   free (flip->var);
138   for (iter = flip->new_names_head; iter != NULL; iter = next) 
139     {
140       next = iter->next;
141       free (iter);
142     }
143   free (flip);
144 }
145
146 /* Make a new variable with base name NAME, which is bowdlerized and
147    mangled until acceptable, and returns success. */
148 static int
149 make_new_var (char name[])
150 {
151   /* Fix invalid characters. */
152   {
153     char *cp;
154   
155     for (cp = name; *cp && !isspace (*cp); cp++)
156       {
157         *cp = toupper ((unsigned char) *cp);
158         if (!isalpha (*cp) && *cp != '@' && *cp != '#'
159             && (cp == name || (*cp != '.' && *cp != '$' && *cp != '_'
160                                && !isdigit (*cp))))
161           {
162             if (cp == name)
163               *cp = 'V';        /* _ not valid in first position. */
164             else
165               *cp = '_';
166           }
167       }
168     *cp = 0;
169   }
170   
171   if (dict_create_var (default_dict, name, 0))
172     return 1;
173
174   /* Add numeric extensions until acceptable. */
175   {
176     int len = (int) strlen (name);
177     char n[9];
178     int i;
179
180     for (i = 1; i < 10000000; i++)
181       {
182         int ofs = min (7 - intlog10 (i), len);
183         memcpy (n, name, ofs);
184         sprintf (&n[ofs], "%d", i);
185
186         if (dict_create_var (default_dict, n, 0))
187           return 1;
188       }
189   }
190
191   msg (SE, _("Could not create acceptable variant for variable %s."), name);
192   return 0;
193 }
194
195 /* Make a new dictionary for all the new variable names. */
196 static int
197 build_dictionary (struct flip_pgm *flip)
198 {
199   dict_create_var_assert (default_dict, "CASE_LBL", 8);
200
201   if (flip->new_names_head == NULL)
202     {
203       int i;
204       
205       if (flip->case_count > 99999)
206         {
207           msg (SE, _("Cannot create more than 99999 variable names."));
208           return 0;
209         }
210       
211       for (i = 0; i < flip->case_count; i++)
212         {
213           struct variable *v;
214           char s[9];
215
216           sprintf (s, "VAR%03d", i);
217           v = dict_create_var_assert (default_dict, s, 0);
218         }
219     }
220   else
221     {
222       struct varname *v;
223
224       for (v = flip->new_names_head; v; v = v->next)
225         if (!make_new_var (v->name))
226           return 0;
227     }
228   
229   return 1;
230 }
231      
232 /* Cases during transposition. */
233 struct flip_sink_info 
234   {
235     struct flip_pgm *flip;              /* FLIP program. */
236     int internal;                       /* Internal or external flip. */
237     char *old_names;                    /* Old variable names. */
238     unsigned long case_cnt;             /* Number of cases. */
239     FILE *file;                         /* Temporary file. */
240   };
241
242 /* Source: Cases after transposition. */
243 struct flip_source_info 
244   {
245     struct flip_pgm *flip;              /* FLIP program. */
246     char *old_names;                    /* Old variable names. */
247     unsigned long case_cnt;             /* Number of cases. */
248     FILE *file;                         /* Temporary file. */
249   };
250
251 static const struct case_sink_class flip_sink_class;
252
253 static FILE *flip_file (struct flip_sink_info *info);
254
255 /* Creates a flip sink based on FLIP, of which it takes
256    ownership. */
257 static struct case_sink *
258 flip_sink_create (struct flip_pgm *flip) 
259 {
260   struct flip_sink_info *info = xmalloc (sizeof *info);
261
262   info->flip = flip;
263   info->case_cnt = 0;
264   
265   {
266     size_t n = flip->var_cnt;
267     char *p;
268     int i;
269     
270     for (i = 0; i < flip->var_cnt; i++)
271       n += strlen (flip->var[i]->name);
272     p = info->old_names = xmalloc (n);
273     for (i = 0; i < flip->var_cnt; i++)
274       p = stpcpy (p, flip->var[i]->name) + 1;
275   }
276
277   return create_case_sink (&flip_sink_class, info);
278 }
279
280 /* Open the FLIP sink. */
281 static void
282 flip_sink_open (struct case_sink *sink) 
283 {
284   struct flip_sink_info *info = sink->aux;
285
286   info->file = tmpfile ();
287   if (!info->file)
288     msg (FE, _("Could not create temporary file for FLIP."));
289 }
290
291 /* Writes case C to the FLIP sink. */
292 static void
293 flip_sink_write (struct case_sink *sink, struct ccase *c)
294 {
295   struct flip_sink_info *info = sink->aux;
296   struct flip_pgm *flip = info->flip;
297   
298   info->case_cnt++;
299
300   if (flip->newnames)
301     {
302       struct varname *v = xmalloc (sizeof (struct varname));
303       v->next = NULL;
304       if (flip->newnames->type == NUMERIC) 
305         {
306           double f = c->data[flip->newnames->fv].f;
307
308           if (f == SYSMIS)
309             strcpy (v->name, "VSYSMIS");
310           else if (f < INT_MIN)
311             strcpy (v->name, "VNEGINF");
312           else if (f > INT_MAX)
313             strcpy (v->name, "VPOSINF");
314           else 
315             {
316               char name[INT_DIGITS + 2];
317               sprintf (name, "V%d", (int) f);
318               strncpy (v->name, name, 8);
319               name[8] = 0; 
320             }
321         }
322       else
323         {
324           int width = min (flip->newnames->width, 8);
325           memcpy (v->name, c->data[flip->newnames->fv].s, width);
326           v->name[width] = 0;
327         }
328       
329       if (flip->new_names_head == NULL)
330         flip->new_names_head = v;
331       else
332         flip->new_names_tail->next = v;
333       flip->new_names_tail = v;
334     }
335   else
336     flip->case_count++;
337
338
339   /* Write to external file. */
340   {
341     double *d = local_alloc (sizeof *d * flip->var_cnt);
342     int i;
343
344     for (i = 0; i < flip->var_cnt; i++)
345       if (flip->var[i]->type == NUMERIC)
346         d[i] = c->data[flip->var[i]->fv].f;
347       else
348         d[i] = SYSMIS;
349           
350     if (fwrite (d, sizeof *d, flip->var_cnt, info->file) != (size_t) flip->var_cnt)
351       msg (FE, _("Error writing FLIP file: %s."),
352            strerror (errno));
353
354     local_free (d);
355   }
356 }
357
358 /* Destroy sink's internal data. */
359 static void
360 flip_sink_destroy (struct case_sink *sink)
361 {
362   struct flip_sink_info *info = sink->aux;
363   
364   free (info->old_names);
365   destroy_flip_pgm (info->flip);
366   free (info);
367 }
368
369 /* Convert the FLIP sink into a source. */
370 static struct case_source *
371 flip_sink_make_source (struct case_sink *sink)
372 {
373   struct flip_sink_info *sink_info = sink->aux;
374   struct flip_source_info *source_info;
375
376   source_info = xmalloc (sizeof *source_info);
377   source_info->flip = sink_info->flip;
378   source_info->old_names = sink_info->old_names;
379   source_info->case_cnt = sink_info->case_cnt;
380   source_info->file = flip_file (sink_info);
381   fclose (sink_info->file);
382
383   free (sink_info);
384
385   return create_case_source (&flip_source_class, source_info);
386 }
387
388 /* Transposes the external file into a new file and returns a
389    pointer to the transposed file. */
390 static FILE *
391 flip_file (struct flip_sink_info *info)
392 {
393   struct flip_pgm *flip = info->flip;
394   size_t case_bytes;
395   size_t case_capacity;
396   size_t case_idx;
397   union value *input_buf, *output_buf;
398   FILE *input_file, *output_file;
399
400   /* Allocate memory for many cases. */
401   case_bytes = flip->var_cnt * sizeof *input_buf;
402   case_capacity = set_max_workspace / case_bytes;
403   if (case_capacity > info->case_cnt)
404     case_capacity = info->case_cnt;
405   if (case_capacity < 2)
406     case_capacity = 2;
407   for (;;)
408     {
409       size_t bytes = case_bytes * case_capacity;
410       if (case_capacity > 2)
411         input_buf = malloc (bytes);
412       else
413         input_buf = xmalloc (bytes);
414       if (input_buf != NULL)
415         break;
416
417       case_capacity /= 2;
418       if (case_capacity < 2)
419         case_capacity = 2;
420     }
421
422   /* Use half the allocated memory for input_buf, half for
423      output_buf. */
424   case_capacity /= 2;
425   output_buf = input_buf + flip->var_cnt * case_capacity;
426
427   input_file = info->file;
428   if (fseek (input_file, 0, SEEK_SET) != 0)
429     msg (FE, _("Error rewinding FLIP file: %s."), strerror (errno));
430
431   output_file = tmpfile ();
432   if (output_file == NULL)
433     msg (FE, _("Error creating FLIP source file."));
434   
435   for (case_idx = 0; case_idx < info->case_cnt; )
436     {
437       unsigned long read_cases = min (info->case_cnt - case_idx,
438                                       case_capacity);
439       int i;
440
441       if (read_cases != fread (input_buf, case_bytes, read_cases, input_file))
442         msg (FE, _("Error reading FLIP file: %s."), strerror (errno));
443
444       for (i = 0; i < flip->var_cnt; i++)
445         {
446           unsigned long j;
447           
448           for (j = 0; j < read_cases; j++)
449             output_buf[j] = input_buf[i + j * flip->var_cnt];
450
451           if (fseek (output_file,
452                      sizeof *input_buf * (case_idx + i * info->case_cnt),
453                      SEEK_SET) != 0)
454             msg (FE, _("Error seeking FLIP source file: %s."),
455                        strerror (errno));
456
457           if (fwrite (output_buf, sizeof *output_buf, read_cases, output_file)
458               != read_cases)
459             msg (FE, _("Error writing FLIP source file: %s."),
460                  strerror (errno));
461         }
462
463       case_idx += read_cases;
464     }
465
466   if (fseek (output_file, 0, SEEK_SET) != 0)
467     msg (FE, _("Error rewind FLIP source file: %s."), strerror (errno));
468
469   free (input_buf);
470   return output_file;
471 }
472
473 /* FLIP sink class. */
474 static const struct case_sink_class flip_sink_class = 
475   {
476     "FLIP",
477     flip_sink_open,
478     flip_sink_write,
479     flip_sink_destroy,
480     flip_sink_make_source,
481   };
482
483 /* Reads the FLIP stream and passes it to WRITE_CASE(). */
484 static void
485 flip_source_read (struct case_source *source,
486                   write_case_func *write_case, write_case_data wc_data)
487 {
488   struct flip_source_info *info = source->aux;
489   struct flip_pgm *flip = info->flip;
490   int i;
491   char *p = info->old_names;
492       
493   for (i = 0; i < flip->var_cnt; i++)
494     {
495       st_bare_pad_copy (temp_case->data[0].s, p, 8);
496       p = strchr (p, 0) + 1;
497
498       if (fread (&temp_case->data[1], sizeof (double), info->case_cnt,
499                  info->file) != info->case_cnt)
500         msg (FE, _("Error reading FLIP source file: %s."),
501              strerror (errno));
502
503       if (!write_case (wc_data))
504         return;
505     }
506 }
507
508 /* Destroy source's internal data. */
509 static void
510 flip_source_destroy (struct case_source *source)
511 {
512   struct flip_source_info *info = source->aux;
513
514   destroy_flip_pgm (info->flip);
515   free (info->old_names);
516   fclose (info->file);
517   free (info);
518 }
519
520 static const struct case_source_class flip_source_class = 
521   {
522     "FLIP",
523     flip_source_read,
524     flip_source_destroy
525   };