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