Completely rewrite src/data/format.[ch], to achieve better
[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 void
468 convert_format (struct pfm_reader *r, const int portable_format[3],
469                 struct fmt_spec *format, struct variable *v)
470 {
471   bool ok;
472
473   if (!fmt_from_io (portable_format[0], &format->type))
474     error (r, _("%s: Bad format specifier byte (%d)."),
475            v->name, portable_format[0]);
476   format->w = portable_format[1];
477   format->d = portable_format[2];
478
479   msg_disable ();
480   ok = fmt_check_output (format) && fmt_check_width_compat (format, v->width);
481   msg_enable ();
482
483   if (!ok)
484     {
485       char fmt_string[FMT_STRING_LEN_MAX + 1];
486       error (r, _("%s variable %s has invalid format specifier %s."),
487              v->type == NUMERIC ? _("Numeric") : _("String"),
488              v->name, fmt_to_string (format, fmt_string)); 
489     }
490 }
491
492 static union value parse_value (struct pfm_reader *, struct variable *);
493
494 /* Read information on all the variables.  */
495 static void
496 read_variables (struct pfm_reader *r, struct dictionary *dict)
497 {
498   char *weight_name = NULL;
499   int i;
500   
501   if (!match (r, '4'))
502     error (r, _("Expected variable count record."));
503   
504   r->var_cnt = read_int (r);
505   if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)
506     error (r, _("Invalid number of variables %d."), r->var_cnt);
507   r->widths = pool_nalloc (r->pool, r->var_cnt, sizeof *r->widths);
508
509   /* Purpose of this value is unknown.  It is typically 161. */
510   read_int (r);
511
512   if (match (r, '6'))
513     {
514       weight_name = read_pool_string (r);
515       if (strlen (weight_name) > SHORT_NAME_LEN) 
516         error (r, _("Weight variable name (%s) truncated."), weight_name);
517     }
518   
519   for (i = 0; i < r->var_cnt; i++)
520     {
521       int width;
522       char name[256];
523       int fmt[6];
524       struct variable *v;
525       int j;
526
527       if (!match (r, '7'))
528         error (r, _("Expected variable record."));
529
530       width = read_int (r);
531       if (width < 0)
532         error (r, _("Invalid variable width %d."), width);
533       r->widths[i] = width;
534
535       read_string (r, name);
536       for (j = 0; j < 6; j++)
537         fmt[j] = read_int (r);
538
539       if (!var_is_valid_name (name, false) || *name == '#' || *name == '$')
540         error (r, _("position %d: Invalid variable name `%s'."), i, name);
541       str_uppercase (name);
542
543       if (width < 0 || width > 255)
544         error (r, "Bad width %d for variable %s.", width, name);
545
546       v = dict_create_var (dict, name, width);
547       if (v == NULL)
548         error (r, _("Duplicate variable name %s."), name);
549
550       convert_format (r, &fmt[0], &v->print, v);
551       convert_format (r, &fmt[3], &v->write, v);
552
553       /* Range missing values. */
554       if (match (r, 'B')) 
555         {
556           double x = read_float (r);
557           double y = read_float (r);
558           mv_add_num_range (&v->miss, x, y);
559         }
560       else if (match (r, 'A'))
561         mv_add_num_range (&v->miss, read_float (r), HIGHEST);
562       else if (match (r, '9'))
563         mv_add_num_range (&v->miss, LOWEST, read_float (r));
564
565       /* Single missing values. */
566       while (match (r, '8')) 
567         {
568           union value value = parse_value (r, v);
569           mv_add_value (&v->miss, &value); 
570         }
571
572       if (match (r, 'C')) 
573         {
574           char label[256];
575           read_string (r, label);
576           v->label = xstrdup (label); 
577         }
578     }
579
580   if (weight_name != NULL) 
581     {
582       struct variable *weight_var = dict_lookup_var (dict, weight_name);
583       if (weight_var == NULL)
584         error (r, _("Weighting variable %s not present in dictionary."),
585                weight_name);
586
587       dict_set_weight (dict, weight_var);
588     }
589 }
590
591 /* Parse a value for variable VV into value V. */
592 static union value
593 parse_value (struct pfm_reader *r, struct variable *vv)
594 {
595   union value v;
596   
597   if (vv->type == ALPHA) 
598     {
599       char string[256];
600       read_string (r, string);
601       buf_copy_str_rpad (v.s, 8, string); 
602     }
603   else
604     v.f = read_float (r);
605
606   return v;
607 }
608
609 /* Parse a value label record and return success. */
610 static void
611 read_value_label (struct pfm_reader *r, struct dictionary *dict)
612 {
613   /* Variables. */
614   int nv;
615   struct variable **v;
616
617   /* Labels. */
618   int n_labels;
619
620   int i;
621
622   nv = read_int (r);
623   v = pool_nalloc (r->pool, nv, sizeof *v);
624   for (i = 0; i < nv; i++)
625     {
626       char name[256];
627       read_string (r, name);
628
629       v[i] = dict_lookup_var (dict, name);
630       if (v[i] == NULL)
631         error (r, _("Unknown variable %s while parsing value labels."), name);
632
633       if (v[0]->width != v[i]->width)
634         error (r, _("Cannot assign value labels to %s and %s, which "
635                     "have different variable types or widths."),
636                v[0]->name, v[i]->name);
637     }
638
639   n_labels = read_int (r);
640   for (i = 0; i < n_labels; i++)
641     {
642       union value val;
643       char label[256];
644       int j;
645
646       val = parse_value (r, v[0]);
647       read_string (r, label);
648
649       /* Assign the value_label's to each variable. */
650       for (j = 0; j < nv; j++)
651         {
652           struct variable *var = v[j];
653
654           if (!val_labs_replace (var->val_labs, val, label))
655             continue;
656
657           if (var->type == NUMERIC)
658             error (r, _("Duplicate label for value %g for variable %s."),
659                    val.f, var->name);
660           else
661             error (r, _("Duplicate label for value `%.*s' for variable %s."),
662                    var->width, val.s, var->name);
663         }
664     }
665 }
666
667 /* Reads one case from portable file R into C. */
668 bool
669 pfm_read_case (struct pfm_reader *r, struct ccase *c)
670 {
671   size_t i;
672   size_t idx;
673
674   setjmp (r->bail_out);
675   if (!r->ok)
676     return false;
677   
678   /* Check for end of file. */
679   if (r->cc == 'Z')
680     return false;
681
682   idx = 0;
683   for (i = 0; i < r->var_cnt; i++) 
684     {
685       int width = r->widths[i];
686       
687       if (width == 0)
688         {
689           case_data_rw (c, idx)->f = read_float (r);
690           idx++;
691         }
692       else
693         {
694           char string[256];
695           read_string (r, string);
696           buf_copy_str_rpad (case_data_rw (c, idx)->s, width, string);
697           idx += DIV_RND_UP (width, MAX_SHORT_STRING);
698         }
699     }
700   
701   return true;
702 }
703
704 /* Returns true if an I/O error has occurred on READER, false
705    otherwise. */
706 bool
707 pfm_read_error (const struct pfm_reader *reader) 
708 {
709   return !reader->ok;
710 }
711
712 /* Returns true if FILE is an SPSS portable file,
713    false otherwise. */
714 bool
715 pfm_detect (FILE *file) 
716 {
717   unsigned char header[464];
718   char trans[256];
719   int cooked_cnt, raw_cnt;
720   int i;
721
722   cooked_cnt = raw_cnt = 0;
723   while (cooked_cnt < sizeof header)
724     {
725       int c = getc (file);
726       if (c == EOF || raw_cnt++ > 512)
727         return false;
728       else if (c != '\n' && c != '\r') 
729         header[cooked_cnt++] = c;
730     }
731
732   memset (trans, 0, 256);
733   for (i = 64; i < 256; i++) 
734     {
735       unsigned char c = header[i + 200];
736       if (trans[c] == 0)
737         trans[c] = portable_to_local[i];
738     }
739
740   for (i = 0; i < 8; i++) 
741     if (trans[header[i + 456]] != "SPSSPORT"[i]) 
742       return false; 
743
744   return true;
745 }