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