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