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