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