Actually implement the new procedure code and adapt all of its clients
[pspp-builds.git] / src / data / por-file-reader.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3    Code for parsing floating-point numbers adapted from GNU C
4    library.
5
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19    02110-1301, USA. */
20
21 #include <config.h>
22 #include "por-file-reader.h"
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <math.h>
27 #include <setjmp.h>
28 #include <stdarg.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include <data/casereader-provider.h>
34 #include <data/casereader.h>
35 #include <data/dictionary.h>
36 #include <data/file-handle-def.h>
37 #include <data/format.h>
38 #include <data/missing-values.h>
39 #include <data/value-labels.h>
40 #include <data/variable.h>
41 #include <libpspp/alloc.h>
42 #include <libpspp/compiler.h>
43 #include <libpspp/hash.h>
44 #include <libpspp/magic.h>
45 #include <libpspp/message.h>
46 #include <libpspp/misc.h>
47 #include <libpspp/pool.h>
48 #include <libpspp/str.h>
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52
53 /* portable_to_local[PORTABLE] translates the given portable
54    character into the local character set. */
55 static const char portable_to_local[256] =
56   {
57     "                                                                "
58     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
59     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
60     "                                                                "
61   };
62
63 /* Portable file reader. */
64 struct pfm_reader
65   {
66     struct pool *pool;          /* All the portable file state. */
67
68     jmp_buf bail_out;           /* longjmp() target for error handling. */
69
70     struct file_handle *fh;     /* File handle. */
71     FILE *file;                 /* File stream. */
72     char cc;                    /* Current character. */
73     char *trans;                /* 256-byte character set translation table. */
74     int var_cnt;                /* Number of variables. */
75     int weight_index;           /* 0-based index of weight variable, or -1. */
76     int *widths;                /* Variable widths, 0 for numeric. */
77     size_t value_cnt;           /* Number of `value's per case. */
78     bool ok;                    /* Set false on I/O error. */
79   };
80
81 static struct casereader_class por_file_casereader_class;
82
83 static void
84 error (struct pfm_reader *r, const char *msg,...)
85      PRINTF_FORMAT (2, 3)
86      NO_RETURN;
87
88 /* Displays MSG as an error message and aborts reading the
89    portable file via longjmp(). */
90 static void
91 error (struct pfm_reader *r, const char *msg, ...)
92 {
93   struct msg m;
94   struct string text;
95   va_list args;
96
97   ds_init_empty (&text);
98   ds_put_format (&text, _("portable file %s corrupt at offset %ld: "),
99                  fh_get_file_name (r->fh), ftell (r->file));
100   va_start (args, msg);
101   ds_put_vformat (&text, msg, args);
102   va_end (args);
103
104   m.category = MSG_GENERAL;
105   m.severity = MSG_ERROR;
106   m.where.file_name = NULL;
107   m.where.line_number = 0;
108   m.text = ds_cstr (&text);
109   
110   msg_emit (&m);
111
112   r->ok = false;
113
114   longjmp (r->bail_out, 1);
115 }
116
117 /* Closes portable file reader R, after we're done with it. */
118 static void
119 por_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
120 {
121   struct pfm_reader *r = r_;
122   pool_destroy (r->pool);
123 }
124
125 /* Read a single character into cur_char.  */
126 static void
127 advance (struct pfm_reader *r)
128 {
129   int c;
130
131   while ((c = getc (r->file)) == '\r' || c == '\n')
132     continue;
133   if (c == EOF)
134     error (r, _("unexpected end of file")); 
135
136   if (r->trans != NULL)
137     c = r->trans[c]; 
138   r->cc = c;
139 }
140
141 /* Skip a single character if present, and return whether it was
142    skipped. */
143 static inline bool
144 match (struct pfm_reader *r, int c)
145 {
146   if (r->cc == c)
147     {
148       advance (r);
149       return true;
150     }
151   else
152     return false;
153 }
154
155 static void read_header (struct pfm_reader *);
156 static void read_version_data (struct pfm_reader *, struct pfm_read_info *);
157 static void read_variables (struct pfm_reader *, struct dictionary *);
158 static void read_value_label (struct pfm_reader *, struct dictionary *);
159 void dump_dictionary (struct dictionary *);
160
161 /* Reads the dictionary from file with handle H, and returns it in a
162    dictionary structure.  This dictionary may be modified in order to
163    rename, reorder, and delete variables, etc. */
164 struct casereader *
165 pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
166                  struct pfm_read_info *info)
167 {
168   struct pool *volatile pool = NULL;
169   struct pfm_reader *volatile r = NULL;
170
171   *dict = dict_create ();
172   if (!fh_open (fh, FH_REF_FILE, "portable file", "rs"))
173     goto error;
174
175   /* Create and initialize reader. */
176   pool = pool_create ();
177   r = pool_alloc (pool, sizeof *r);
178   r->pool = pool;
179   if (setjmp (r->bail_out))
180     goto error;
181   r->fh = fh;
182   r->file = pool_fopen (r->pool, fh_get_file_name (r->fh), "rb");
183   r->weight_index = -1;
184   r->trans = NULL;
185   r->var_cnt = 0;
186   r->widths = NULL;
187   r->value_cnt = 0;
188   r->ok = true;
189
190   /* Check that file open succeeded, prime reading. */
191   if (r->file == NULL)
192     {
193       msg (ME, _("An error occurred while opening \"%s\" for reading "
194                  "as a portable file: %s."),
195            fh_get_file_name (r->fh), strerror (errno));
196       goto error;
197     }
198   
199   /* Read header, version, date info, product id, variables. */
200   read_header (r);
201   read_version_data (r, info);
202   read_variables (r, *dict);
203
204   /* Read value labels. */
205   while (match (r, 'D'))
206     read_value_label (r, *dict);
207
208   /* Check that we've made it to the data. */
209   if (!match (r, 'F'))
210     error (r, _("Data record expected."));
211
212   r->value_cnt = dict_get_next_value_idx (*dict);
213   return casereader_create_sequential (NULL, r->value_cnt, CASENUMBER_MAX,
214                                        &por_file_casereader_class, r);
215
216  error:
217   pool_destroy (r->pool);
218   dict_destroy (*dict);
219   *dict = NULL;
220   return NULL;
221 }
222 \f
223 /* Returns the value of base-30 digit C,
224    or -1 if C is not a base-30 digit. */
225 static int
226 base_30_value (unsigned char c) 
227 {
228   static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST";
229   const char *p = strchr (base_30_digits, c);
230   return p != NULL ? p - base_30_digits : -1;
231 }
232
233 /* Read a floating point value and return its value. */
234 static double
235 read_float (struct pfm_reader *r)
236 {
237   double num = 0.;
238   int exponent = 0;
239   bool got_dot = false;         /* Seen a decimal point? */
240   bool got_digit = false;       /* Seen any digits? */
241   bool negative = false;        /* Number is negative? */
242
243   /* Skip leading spaces. */
244   while (match (r, ' '))
245     continue;
246
247   /* `*' indicates system-missing. */
248   if (match (r, '*'))
249     {
250       advance (r);      /* Probably a dot (.) but doesn't appear to matter. */
251       return SYSMIS;
252     }
253
254   negative = match (r, '-');
255   for (;;)
256     {
257       int digit = base_30_value (r->cc);
258       if (digit != -1)
259         {
260           got_digit = true;
261
262           /* Make sure that multiplication by 30 will not overflow.  */
263           if (num > DBL_MAX * (1. / 30.))
264             /* The value of the digit doesn't matter, since we have already
265                gotten as many digits as can be represented in a `double'.
266                This doesn't necessarily mean the result will overflow.
267                The exponent may reduce it to within range.
268
269                We just need to record that there was another
270                digit so that we can multiply by 10 later.  */
271             ++exponent;
272           else
273             num = (num * 30.0) + digit;
274
275           /* Keep track of the number of digits after the decimal point.
276              If we just divided by 30 here, we would lose precision.  */
277           if (got_dot)
278             --exponent;
279         }
280       else if (!got_dot && r->cc == '.')
281         /* Record that we have found the decimal point.  */
282         got_dot = 1;
283       else
284         /* Any other character terminates the number.  */
285         break;
286
287       advance (r);
288     }
289
290   /* Check that we had some digits. */
291   if (!got_digit)
292     error (r, _("Number expected."));
293
294   /* Get exponent if any. */
295   if (r->cc == '+' || r->cc == '-')
296     {
297       long int exp = 0;
298       bool negative_exponent = r->cc == '-';
299       int digit;
300
301       for (advance (r); (digit = base_30_value (r->cc)) != -1; advance (r))
302         {
303           if (exp > LONG_MAX / 30)
304             {
305               exp = LONG_MAX;
306               break;
307             }
308           exp = exp * 30 + digit;
309         }
310
311       /* We don't check whether there were actually any digits, but we
312          probably should. */
313       if (negative_exponent)
314         exp = -exp;
315       exponent += exp;
316     }
317
318   /* Numbers must end with `/'. */
319   if (!match (r, '/'))
320     error (r, _("Missing numeric terminator."));
321
322   /* Multiply `num' by 30 to the `exponent' power, checking for
323      overflow.  */
324   if (exponent < 0)
325     num *= pow (30.0, (double) exponent);
326   else if (exponent > 0)
327     {
328       if (num > DBL_MAX * pow (30.0, (double) -exponent))
329         num = DBL_MAX;
330       else
331         num *= pow (30.0, (double) exponent);
332     }
333
334   return negative ? -num : num;
335 }
336   
337 /* Read an integer and return its value. */
338 static int
339 read_int (struct pfm_reader *r)
340 {
341   double f = read_float (r);
342   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
343     error (r, _("Invalid integer."));
344   return f;
345 }
346
347 /* Reads a string into BUF, which must have room for 256
348    characters. */
349 static void
350 read_string (struct pfm_reader *r, char *buf)
351 {
352   int n = read_int (r);
353   if (n < 0 || n > 255)
354     error (r, _("Bad string length %d."), n);
355   
356   while (n-- > 0)
357     {
358       *buf++ = r->cc;
359       advance (r);
360     }
361   *buf = '\0';
362 }
363
364 /* Reads a string and returns a copy of it allocated from R's
365    pool. */
366 static char *
367 read_pool_string (struct pfm_reader *r) 
368 {
369   char string[256];
370   read_string (r, string);
371   return pool_strdup (r->pool, string);
372 }
373 \f
374 /* Reads the 464-byte file header. */
375 static void
376 read_header (struct pfm_reader *r)
377 {
378   char *trans;
379   int i;
380
381   /* Read and ignore vanity splash strings. */
382   for (i = 0; i < 200; i++)
383     advance (r);
384   
385   /* Skip the first 64 characters of the translation table.
386      We don't care about these.  They are probably all set to
387      '0', marking them as untranslatable, and that would screw
388      up our actual translation of the real '0'. */
389   for (i = 0; i < 64; i++)
390     advance (r);
391
392   /* Read the rest of the translation table. */
393   trans = pool_malloc (r->pool, 256);
394   memset (trans, 0, 256);
395   for (; i < 256; i++) 
396     {
397       unsigned char c;
398
399       advance (r);
400
401       c = r->cc;
402       if (trans[c] == 0)
403         trans[c] = portable_to_local[i];
404     }
405
406   /* Set up the translation table, then read the first
407      translated character. */
408   r->trans = trans;
409   advance (r); 
410
411   /* Skip and verify signature. */
412   for (i = 0; i < 8; i++) 
413     if (!match (r, "SPSSPORT"[i])) 
414       {
415         msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh));
416         longjmp (r->bail_out, 1);
417       }
418 }
419
420 /* Reads the version and date info record, as well as product and
421    subproduct identification records if present. */
422 static void
423 read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
424 {
425   static char empty_string[] = "";
426   char *date, *time, *product, *author, *subproduct;
427   int i;
428
429   /* Read file. */
430   if (!match (r, 'A'))
431     error (r, _("Unrecognized version code `%c'."), r->cc);
432   date = read_pool_string (r);
433   time = read_pool_string (r);
434   product = match (r, '1') ? read_pool_string (r) : empty_string;
435   author = match (r, '2') ? read_pool_string (r) : empty_string;
436   subproduct = match (r, '3') ? read_pool_string (r) : empty_string;
437
438   /* Validate file. */
439   if (strlen (date) != 8)
440     error (r, _("Bad date string length %d."), (int) strlen (date));
441   if (strlen (time) != 6)
442     error (r, _("Bad time string length %d."), (int) strlen (time));
443
444   /* Save file info. */
445   if (info != NULL) 
446     {
447       /* Date. */
448       for (i = 0; i < 8; i++) 
449         {
450           static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
451           info->creation_date[map[i]] = date[i]; 
452         }
453       info->creation_date[2] = info->creation_date[5] = ' ';
454       info->creation_date[10] = 0;
455
456       /* Time. */
457       for (i = 0; i < 6; i++)
458         {
459           static const int map[] = {0, 1, 3, 4, 6, 7};
460           info->creation_time[map[i]] = time[i];
461         }
462       info->creation_time[2] = info->creation_time[5] = ' ';
463       info->creation_time[8] = 0;
464
465       /* Product. */
466       str_copy_trunc (info->product, sizeof info->product, product);
467       str_copy_trunc (info->subproduct, sizeof info->subproduct, subproduct);
468     }
469 }
470
471 /* Translates a format specification read from portable file R as
472    the three integers INTS into a normal format specifier FORMAT,
473    checking that the format is appropriate for variable V. */
474 static struct fmt_spec
475 convert_format (struct pfm_reader *r, const int portable_format[3],
476                 struct variable *v)
477 {
478   struct fmt_spec format;
479   bool ok;
480
481   if (!fmt_from_io (portable_format[0], &format.type))
482     error (r, _("%s: Bad format specifier byte (%d)."),
483            var_get_name (v), portable_format[0]);
484   format.w = portable_format[1];
485   format.d = portable_format[2];
486
487   msg_disable ();
488   ok = (fmt_check_output (&format)
489         && fmt_check_width_compat (&format, var_get_width (v)));
490   msg_enable ();
491
492   if (!ok)
493     {
494       char fmt_string[FMT_STRING_LEN_MAX + 1];
495       error (r, _("%s variable %s has invalid format specifier %s."),
496              var_is_numeric (v) ? _("Numeric") : _("String"),
497              var_get_name (v), fmt_to_string (&format, fmt_string));
498       format = fmt_default_for_width (var_get_width (v));
499     }
500
501   return format;
502 }
503
504 static union value parse_value (struct pfm_reader *, struct variable *);
505
506 /* Read information on all the variables.  */
507 static void
508 read_variables (struct pfm_reader *r, struct dictionary *dict)
509 {
510   char *weight_name = NULL;
511   int i;
512   
513   if (!match (r, '4'))
514     error (r, _("Expected variable count record."));
515   
516   r->var_cnt = read_int (r);
517   if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)
518     error (r, _("Invalid number of variables %d."), r->var_cnt);
519   r->widths = pool_nalloc (r->pool, r->var_cnt, sizeof *r->widths);
520
521   /* Purpose of this value is unknown.  It is typically 161. */
522   read_int (r);
523
524   if (match (r, '6'))
525     {
526       weight_name = read_pool_string (r);
527       if (strlen (weight_name) > SHORT_NAME_LEN) 
528         error (r, _("Weight variable name (%s) truncated."), weight_name);
529     }
530   
531   for (i = 0; i < r->var_cnt; i++)
532     {
533       int width;
534       char name[256];
535       int fmt[6];
536       struct variable *v;
537       struct missing_values miss;
538       struct fmt_spec print, write;
539       int j;
540
541       if (!match (r, '7'))
542         error (r, _("Expected variable record."));
543
544       width = read_int (r);
545       if (width < 0)
546         error (r, _("Invalid variable width %d."), width);
547       r->widths[i] = width;
548
549       read_string (r, name);
550       for (j = 0; j < 6; j++)
551         fmt[j] = read_int (r);
552
553       if (!var_is_valid_name (name, false) || *name == '#' || *name == '$')
554         error (r, _("position %d: Invalid variable name `%s'."), i, name);
555       str_uppercase (name);
556
557       if (width < 0 || width > 255)
558         error (r, _("Bad width %d for variable %s."), width, name);
559
560       v = dict_create_var (dict, name, width);
561       if (v == NULL)
562         error (r, _("Duplicate variable name %s."), name);
563
564       print = convert_format (r, &fmt[0], v);
565       write = convert_format (r, &fmt[3], v);
566       var_set_print_format (v, &print);
567       var_set_write_format (v, &write);
568
569       /* Range missing values. */
570       mv_init (&miss, var_get_width (v));
571       if (match (r, 'B')) 
572         {
573           double x = read_float (r);
574           double y = read_float (r);
575           mv_add_num_range (&miss, x, y);
576         }
577       else if (match (r, 'A'))
578         mv_add_num_range (&miss, read_float (r), HIGHEST);
579       else if (match (r, '9'))
580         mv_add_num_range (&miss, LOWEST, read_float (r));
581
582       /* Single missing values. */
583       while (match (r, '8')) 
584         {
585           union value value = parse_value (r, v);
586           mv_add_value (&miss, &value); 
587         }
588
589       var_set_missing_values (v, &miss);
590
591       if (match (r, 'C')) 
592         {
593           char label[256];
594           read_string (r, label);
595           var_set_label (v, label);
596         }
597     }
598
599   if (weight_name != NULL) 
600     {
601       struct variable *weight_var = dict_lookup_var (dict, weight_name);
602       if (weight_var == NULL)
603         error (r, _("Weighting variable %s not present in dictionary."),
604                weight_name);
605
606       dict_set_weight (dict, weight_var);
607     }
608 }
609
610 /* Parse a value for variable VV into value V. */
611 static union value
612 parse_value (struct pfm_reader *r, struct variable *vv)
613 {
614   union value v;
615   
616   if (var_is_alpha (vv)) 
617     {
618       char string[256];
619       read_string (r, string);
620       buf_copy_str_rpad (v.s, 8, string); 
621     }
622   else
623     v.f = read_float (r);
624
625   return v;
626 }
627
628 /* Parse a value label record and return success. */
629 static void
630 read_value_label (struct pfm_reader *r, struct dictionary *dict)
631 {
632   /* Variables. */
633   int nv;
634   struct variable **v;
635
636   /* Labels. */
637   int n_labels;
638
639   int i;
640
641   nv = read_int (r);
642   v = pool_nalloc (r->pool, nv, sizeof *v);
643   for (i = 0; i < nv; i++)
644     {
645       char name[256];
646       read_string (r, name);
647
648       v[i] = dict_lookup_var (dict, name);
649       if (v[i] == NULL)
650         error (r, _("Unknown variable %s while parsing value labels."), name);
651
652       if (var_get_width (v[0]) != var_get_width (v[i]))
653         error (r, _("Cannot assign value labels to %s and %s, which "
654                     "have different variable types or widths."),
655                var_get_name (v[0]), var_get_name (v[i]));
656     }
657
658   n_labels = read_int (r);
659   for (i = 0; i < n_labels; i++)
660     {
661       union value val;
662       char label[256];
663       int j;
664
665       val = parse_value (r, v[0]);
666       read_string (r, label);
667
668       /* Assign the value_label's to each variable. */
669       for (j = 0; j < nv; j++)
670         {
671           struct variable *var = v[j];
672
673           if (!var_add_value_label (var, &val, label))
674             continue;
675
676           if (var_is_numeric (var))
677             error (r, _("Duplicate label for value %g for variable %s."),
678                    val.f, var_get_name (var));
679           else
680             error (r, _("Duplicate label for value `%.*s' for variable %s."),
681                    var_get_width (var), val.s, var_get_name (var));
682         }
683     }
684 }
685
686 /* Reads one case from portable file R into C. */
687 static bool
688 por_file_casereader_read (struct casereader *reader, void *r_, struct ccase *c)
689 {
690   struct pfm_reader *r = r_;
691   size_t i;
692   size_t idx;
693
694   case_create (c, casereader_get_value_cnt (reader));
695   setjmp (r->bail_out);
696   if (!r->ok) 
697     {
698       casereader_force_error (reader);
699       case_destroy (c);
700       return false; 
701     }
702   
703   /* Check for end of file. */
704   if (r->cc == 'Z') 
705     {
706       case_destroy (c);
707       return false; 
708     }
709
710   idx = 0;
711   for (i = 0; i < r->var_cnt; i++) 
712     {
713       int width = r->widths[i];
714       
715       if (width == 0)
716         {
717           case_data_rw_idx (c, idx)->f = read_float (r);
718           idx++;
719         }
720       else
721         {
722           char string[256];
723           read_string (r, string);
724           buf_copy_str_rpad (case_data_rw_idx (c, idx)->s, width, string);
725           idx += DIV_RND_UP (width, MAX_SHORT_STRING);
726         }
727     }
728   
729   return true;
730 }
731
732 /* Returns true if FILE is an SPSS portable file,
733    false otherwise. */
734 bool
735 pfm_detect (FILE *file) 
736 {
737   unsigned char header[464];
738   char trans[256];
739   int cooked_cnt, raw_cnt;
740   int i;
741
742   cooked_cnt = raw_cnt = 0;
743   while (cooked_cnt < sizeof header)
744     {
745       int c = getc (file);
746       if (c == EOF || raw_cnt++ > 512)
747         return false;
748       else if (c != '\n' && c != '\r') 
749         header[cooked_cnt++] = c;
750     }
751
752   memset (trans, 0, 256);
753   for (i = 64; i < 256; i++) 
754     {
755       unsigned char c = header[i + 200];
756       if (trans[c] == 0)
757         trans[c] = portable_to_local[i];
758     }
759
760   for (i = 0; i < 8; i++) 
761     if (trans[header[i + 456]] != "SPSSPORT"[i]) 
762       return false; 
763
764   return true;
765 }
766
767 static struct casereader_class por_file_casereader_class = 
768   {
769     por_file_casereader_read,
770     por_file_casereader_destroy,
771     NULL,
772     NULL,
773   };