1dcbeac71c540eb81c9654d619b575f85367316b
[pspp] / src / data / por-file-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <math.h>
22 #include <setjmp.h>
23 #include <stdarg.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include "data/any-reader.h"
29 #include "data/casereader-provider.h"
30 #include "data/casereader.h"
31 #include "data/dictionary.h"
32 #include "data/file-handle-def.h"
33 #include "data/file-name.h"
34 #include "data/format.h"
35 #include "data/missing-values.h"
36 #include "data/short-names.h"
37 #include "data/value-labels.h"
38 #include "data/variable.h"
39 #include "libpspp/compiler.h"
40 #include "libpspp/i18n.h"
41 #include "libpspp/message.h"
42 #include "libpspp/misc.h"
43 #include "libpspp/pool.h"
44 #include "libpspp/str.h"
45
46 #include "gl/intprops.h"
47 #include "gl/minmax.h"
48 #include "gl/xalloc.h"
49 #include "gl/xmemdup0.h"
50
51 #include "gettext.h"
52 #define _(msgid) gettext (msgid)
53 #define N_(msgid) (msgid)
54
55 /* portable_to_local[PORTABLE] translates the given portable
56    character into the local character set. */
57 static const char portable_to_local[256] =
58   {
59     "                                                                "
60     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
61     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
62     "                                                                "
63   };
64
65 /* Portable file reader. */
66 struct pfm_reader
67   {
68     struct any_reader any_reader;
69     struct pool *pool;          /* All the portable file state. */
70
71     jmp_buf bail_out;           /* longjmp() target for error handling. */
72
73     struct dictionary *dict;
74     struct any_read_info info;
75     struct file_handle *fh;     /* File handle. */
76     struct fh_lock *lock;       /* Read lock for file. */
77     FILE *file;                 /* File stream. */
78     int line_length;            /* Number of characters so far on this line. */
79     char cc;                    /* Current character. */
80     char *trans;                /* 256-byte character set translation table. */
81     int var_cnt;                /* Number of variables. */
82     int weight_index;           /* 0-based index of weight variable, or -1. */
83     struct caseproto *proto;    /* Format of output cases. */
84     bool ok;                    /* Set false on I/O error. */
85   };
86
87 static const struct casereader_class por_file_casereader_class;
88
89 static struct pfm_reader *
90 pfm_reader_cast (const struct any_reader *r_)
91 {
92   assert (r_->klass == &por_file_reader_class);
93   return UP_CAST (r_, struct pfm_reader, any_reader);
94 }
95
96 static void
97 error (struct pfm_reader *r, const char *msg,...)
98      PRINTF_FORMAT (2, 3)
99      NO_RETURN;
100
101 /* Displays MSG as an error message and aborts reading the
102    portable file via longjmp(). */
103 static void
104 error (struct pfm_reader *r, const char *msg, ...)
105 {
106   struct string text;
107   va_list args;
108
109   ds_init_empty (&text);
110   ds_put_format (&text, _("portable file %s corrupt at offset 0x%llx: "),
111                  fh_get_file_name (r->fh), (long long int) ftello (r->file));
112   va_start (args, msg);
113   ds_put_vformat (&text, msg, args);
114   va_end (args);
115
116   struct msg m = {
117     .category = MSG_C_GENERAL,
118     .severity = MSG_S_ERROR,
119     .text = ds_cstr (&text),
120   };
121   msg_emit (&m);
122
123   r->ok = false;
124
125   longjmp (r->bail_out, 1);
126 }
127
128 /* Displays MSG as an warning for the current position in
129    portable file reader R. */
130 static void
131 warning (struct pfm_reader *r, const char *msg, ...)
132 {
133   struct string text;
134   va_list args;
135
136   ds_init_empty (&text);
137   ds_put_format (&text, _("reading portable file %s at offset 0x%llx: "),
138                  fh_get_file_name (r->fh), (long long int) ftello (r->file));
139   va_start (args, msg);
140   ds_put_vformat (&text, msg, args);
141   va_end (args);
142
143   struct msg m = {
144     .category = MSG_C_GENERAL,
145     .severity = MSG_S_WARNING,
146     .text = ds_cstr (&text),
147   };
148   msg_emit (&m);
149 }
150
151 /* Close and destroy R.
152    Returns false if an error was detected on R, true otherwise. */
153 static bool
154 pfm_close (struct any_reader *r_)
155 {
156   struct pfm_reader *r = pfm_reader_cast (r_);
157   bool ok;
158
159   dict_unref (r->dict);
160   any_read_info_destroy (&r->info);
161   if (r->file)
162     {
163       if (fn_close (r->fh, r->file) == EOF)
164         {
165           msg (ME, _("Error closing portable file `%s': %s."),
166                fh_get_file_name (r->fh), strerror (errno));
167           r->ok = false;
168         }
169       r->file = NULL;
170     }
171
172   fh_unlock (r->lock);
173   fh_unref (r->fh);
174
175   ok = r->ok;
176   pool_destroy (r->pool);
177
178   return ok;
179 }
180
181 /* Closes portable file reader R, after we're done with it. */
182 static void
183 por_file_casereader_destroy (struct casereader *reader, void *r_)
184 {
185   struct pfm_reader *r = r_;
186   if (!pfm_close (&r->any_reader))
187     casereader_force_error (reader);
188 }
189
190 /* Read a single character into cur_char.  */
191 static void
192 advance (struct pfm_reader *r)
193 {
194   int c;
195
196   /* Read the next character from the file.
197      Ignore carriage returns entirely.
198      Mostly ignore new-lines, but if a new-line occurs before the
199      line has reached 80 bytes in length, then treat the
200      "missing" bytes as spaces. */
201   for (;;)
202     {
203       while ((c = getc (r->file)) == '\r')
204         continue;
205       if (c != '\n')
206         break;
207
208       if (r->line_length < 80)
209         {
210           c = ' ';
211           ungetc ('\n', r->file);
212           break;
213         }
214       r->line_length = 0;
215     }
216   if (c == EOF)
217     error (r, _("unexpected end of file"));
218
219   if (r->trans != NULL)
220     c = r->trans[c];
221   r->cc = c;
222   r->line_length++;
223 }
224
225 /* Skip a single character if present, and return whether it was
226    skipped. */
227 static inline bool
228 match (struct pfm_reader *r, int c)
229 {
230   if (r->cc == c)
231     {
232       advance (r);
233       return true;
234     }
235   else
236     return false;
237 }
238
239 static void read_header (struct pfm_reader *);
240 static void read_version_data (struct pfm_reader *, struct any_read_info *);
241 static void read_variables (struct pfm_reader *, struct dictionary *);
242 static void read_value_label (struct pfm_reader *, struct dictionary *);
243 static void read_documents (struct pfm_reader *, struct dictionary *);
244
245 /* Reads the dictionary from file with handle H, and returns it in a
246    dictionary structure.  This dictionary may be modified in order to
247    rename, reorder, and delete variables, etc. */
248 static struct any_reader *
249 pfm_open (struct file_handle *fh)
250 {
251   struct pool *volatile pool = NULL;
252   struct pfm_reader *volatile r = NULL;
253
254   /* Create and initialize reader. */
255   pool = pool_create ();
256   r = pool_alloc (pool, sizeof *r);
257   r->any_reader.klass = &por_file_reader_class;
258   r->dict = dict_create (get_default_encoding ());
259   memset (&r->info, 0, sizeof r->info);
260   r->pool = pool;
261   r->fh = fh_ref (fh);
262   r->lock = NULL;
263   r->file = NULL;
264   r->line_length = 0;
265   r->weight_index = -1;
266   r->trans = NULL;
267   r->var_cnt = 0;
268   r->proto = NULL;
269   r->ok = true;
270   if (setjmp (r->bail_out))
271     goto error;
272
273   /* Lock file. */
274   /* TRANSLATORS: this fragment will be interpolated into
275      messages in fh_lock() that identify types of files. */
276   r->lock = fh_lock (fh, FH_REF_FILE, N_("portable file"), FH_ACC_READ, false);
277   if (r->lock == NULL)
278     goto error;
279
280   /* Open file. */
281   r->file = fn_open (r->fh, "rb");
282   if (r->file == NULL)
283     {
284       msg (ME, _("An error occurred while opening `%s' for reading "
285                  "as a portable file: %s."),
286            fh_get_file_name (r->fh), strerror (errno));
287       goto error;
288     }
289
290   /* Read header, version, date info, product id, variables. */
291   read_header (r);
292   read_version_data (r, &r->info);
293   read_variables (r, r->dict);
294
295   /* Read value labels. */
296   while (match (r, 'D'))
297     read_value_label (r, r->dict);
298
299   /* Read documents. */
300   if (match (r, 'E'))
301     read_documents (r, r->dict);
302
303   /* Check that we've made it to the data. */
304   if (!match (r, 'F'))
305     error (r, _("Data record expected."));
306
307   r->proto = caseproto_ref_pool (dict_get_proto (r->dict), r->pool);
308   return &r->any_reader;
309
310  error:
311   pfm_close (&r->any_reader);
312   return NULL;
313 }
314
315 static struct casereader *
316 pfm_decode (struct any_reader *r_, const char *encoding UNUSED,
317             struct dictionary **dictp, struct any_read_info *info)
318 {
319   struct pfm_reader *r = pfm_reader_cast (r_);
320
321   *dictp = r->dict;
322   r->dict = NULL;
323
324   if (info)
325     {
326       *info = r->info;
327       memset (&r->info, 0, sizeof r->info);
328     }
329
330   return casereader_create_sequential (NULL, r->proto, CASENUMBER_MAX,
331                                        &por_file_casereader_class, r);
332 }
333 \f
334 /* Returns the value of base-30 digit C,
335    or -1 if C is not a base-30 digit. */
336 static int
337 base_30_value (unsigned char c)
338 {
339   static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST";
340   const char *p = strchr (base_30_digits, c);
341   return p != NULL ? p - base_30_digits : -1;
342 }
343
344 /* Read a floating point value and return its value. */
345 static double
346 read_float (struct pfm_reader *r)
347 {
348   double num = 0.;
349   int exponent = 0;
350   bool got_dot = false;         /* Seen a decimal point? */
351   bool got_digit = false;       /* Seen any digits? */
352   bool negative = false;        /* Number is negative? */
353
354   /* Skip leading spaces. */
355   while (match (r, ' '))
356     continue;
357
358   /* `*' indicates system-missing. */
359   if (match (r, '*'))
360     {
361       advance (r);      /* Probably a dot (.) but doesn't appear to matter. */
362       return SYSMIS;
363     }
364
365   negative = match (r, '-');
366   for (;;)
367     {
368       int digit = base_30_value (r->cc);
369       if (digit != -1)
370         {
371           got_digit = true;
372
373           /* Make sure that multiplication by 30 will not overflow.  */
374           if (num > DBL_MAX * (1. / 30.))
375             /* The value of the digit doesn't matter, since we have already
376                gotten as many digits as can be represented in a `double'.
377                This doesn't necessarily mean the result will overflow.
378                The exponent may reduce it to within range.
379
380                We just need to record that there was another
381                digit so that we can multiply by 10 later.  */
382             ++exponent;
383           else
384             num = (num * 30.0) + digit;
385
386           /* Keep track of the number of digits after the decimal point.
387              If we just divided by 30 here, we would lose precision.  */
388           if (got_dot)
389             --exponent;
390         }
391       else if (!got_dot && r->cc == '.')
392         /* Record that we have found the decimal point.  */
393         got_dot = 1;
394       else
395         /* Any other character terminates the number.  */
396         break;
397
398       advance (r);
399     }
400
401   /* Check that we had some digits. */
402   if (!got_digit)
403     error (r, _("Number expected."));
404
405   /* Get exponent if any. */
406   if (r->cc == '+' || r->cc == '-')
407     {
408       long int exp = 0;
409       bool negative_exponent = r->cc == '-';
410       int digit;
411
412       for (advance (r); (digit = base_30_value (r->cc)) != -1; advance (r))
413         {
414           if (exp > LONG_MAX / 30)
415             {
416               exp = LONG_MAX;
417               break;
418             }
419           exp = exp * 30 + digit;
420         }
421
422       /* We don't check whether there were actually any digits, but we
423          probably should. */
424       if (negative_exponent)
425         exp = -exp;
426       exponent += exp;
427     }
428
429   /* Numbers must end with `/'. */
430   if (!match (r, '/'))
431     error (r, _("Missing numeric terminator."));
432
433   /* Multiply `num' by 30 to the `exponent' power, checking for
434      overflow.  */
435   if (exponent < 0)
436     num *= pow (30.0, (double) exponent);
437   else if (exponent > 0)
438     {
439       if (num > DBL_MAX * pow (30.0, (double) -exponent))
440         num = DBL_MAX;
441       else
442         num *= pow (30.0, (double) exponent);
443     }
444
445   return negative ? -num : num;
446 }
447
448 /* Read an integer and return its value. */
449 static int
450 read_int (struct pfm_reader *r)
451 {
452   double f = read_float (r);
453   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
454     error (r, _("Invalid integer."));
455   return f;
456 }
457
458 /* Reads a string into BUF, which must have room for 256
459    characters. */
460 static void
461 read_string (struct pfm_reader *r, char *buf)
462 {
463   int n = read_int (r);
464   if (n < 0 || n > 255)
465     error (r, _("Bad string length %d."), n);
466
467   while (n-- > 0)
468     {
469       *buf++ = r->cc;
470       advance (r);
471     }
472   *buf = '\0';
473 }
474
475
476 /* Reads a string into BUF, which must have room for 256
477    characters.
478    Returns the number of bytes read.
479 */
480 static size_t
481 read_bytes (struct pfm_reader *r, uint8_t *buf)
482 {
483   int n = read_int (r);
484   if (n < 0 || n > 255)
485     error (r, _("Bad string length %d."), n);
486
487   while (n-- > 0)
488     {
489       *buf++ = r->cc;
490       advance (r);
491     }
492   return n;
493 }
494
495
496
497 /* Reads a string and returns a copy of it allocated from R's
498    pool. */
499 static char *
500 read_pool_string (struct pfm_reader *r)
501 {
502   char string[256];
503   read_string (r, string);
504   return pool_strdup (r->pool, string);
505 }
506 \f
507 /* Reads the 464-byte file header. */
508 static void
509 read_header (struct pfm_reader *r)
510 {
511   char *trans;
512   int i;
513
514   /* Read and ignore vanity splash strings. */
515   for (i = 0; i < 200; i++)
516     advance (r);
517
518   /* Skip the first 64 characters of the translation table.
519      We don't care about these.  They are probably all set to
520      '0', marking them as untranslatable, and that would screw
521      up our actual translation of the real '0'. */
522   for (i = 0; i < 64; i++)
523     advance (r);
524
525   /* Read the rest of the translation table. */
526   trans = pool_malloc (r->pool, 256);
527   memset (trans, 0, 256);
528   for (; i < 256; i++)
529     {
530       unsigned char c;
531
532       advance (r);
533
534       c = r->cc;
535       if (trans[c] == 0)
536         trans[c] = portable_to_local[i];
537     }
538
539   /* Set up the translation table, then read the first
540      translated character. */
541   r->trans = trans;
542   advance (r);
543
544   /* Skip and verify signature. */
545   for (i = 0; i < 8; i++)
546     if (!match (r, "SPSSPORT"[i]))
547       {
548         msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh));
549         longjmp (r->bail_out, 1);
550       }
551 }
552
553 /* Reads the version and date info record, as well as product and
554    subproduct identification records if present. */
555 static void
556 read_version_data (struct pfm_reader *r, struct any_read_info *info)
557 {
558   static const char empty_string[] = "";
559   char *date, *time;
560   const char *product, *subproduct;
561   int i;
562
563   /* Read file. */
564   if (!match (r, 'A'))
565     error (r, _("Unrecognized version code `%c'."), r->cc);
566   date = read_pool_string (r);
567   time = read_pool_string (r);
568   product = match (r, '1') ? read_pool_string (r) : empty_string;
569   if (match (r, '2'))
570     {
571       /* Skip "author" field. */
572       read_pool_string (r);
573     }
574   subproduct = match (r, '3') ? read_pool_string (r) : empty_string;
575
576   /* Validate file. */
577   if (strlen (date) != 8)
578     error (r, _("Bad date string length %zu."), strlen (date));
579   if (strlen (time) != 6)
580     error (r, _("Bad time string length %zu."), strlen (time));
581
582   /* Save file info. */
583   if (info != NULL)
584     {
585       memset (info, 0, sizeof *info);
586
587       info->float_format = FLOAT_NATIVE_DOUBLE;
588       info->integer_format = INTEGER_NATIVE;
589       info->compression = ANY_COMP_NONE;
590       info->case_cnt = -1;
591
592       /* Date. */
593       info->creation_date = xmalloc (11);
594       for (i = 0; i < 8; i++)
595         {
596           static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
597           info->creation_date[map[i]] = date[i];
598         }
599       info->creation_date[2] = info->creation_date[5] = ' ';
600       info->creation_date[10] = '\0';
601
602       /* Time. */
603       info->creation_time = xmalloc (9);
604       for (i = 0; i < 6; i++)
605         {
606           static const int map[] = {0, 1, 3, 4, 6, 7};
607           info->creation_time[map[i]] = time[i];
608         }
609       info->creation_time[2] = info->creation_time[5] = ' ';
610       info->creation_time[8] = 0;
611
612       /* Product. */
613       info->product = xstrdup (product);
614       info->product_ext = xstrdup (subproduct);
615     }
616 }
617
618 /* Translates a format specification read from portable file R as
619    the three integers INTS into a normal format specifier FORMAT,
620    checking that the format is appropriate for variable V. */
621 static struct fmt_spec
622 convert_format (struct pfm_reader *r, const int portable_format[3],
623                 struct variable *v, bool *report_error)
624 {
625   struct fmt_spec format;
626   bool ok;
627
628   if (!fmt_from_io (portable_format[0], &format.type))
629     {
630       if (*report_error)
631         warning (r, _("%s: Bad format specifier byte (%d).  Variable "
632                       "will be assigned a default format."),
633                  var_get_name (v), portable_format[0]);
634       goto assign_default;
635     }
636
637   format.w = portable_format[1];
638   format.d = portable_format[2];
639
640   msg_disable ();
641   ok = (fmt_check_output (&format)
642         && fmt_check_width_compat (&format, var_get_width (v)));
643   msg_enable ();
644
645   if (!ok)
646     {
647       if (*report_error)
648         {
649           char fmt_string[FMT_STRING_LEN_MAX + 1];
650           fmt_to_string (&format, fmt_string);
651           if (var_is_numeric (v))
652             warning (r, _("Numeric variable %s has invalid format "
653                           "specifier %s."),
654                      var_get_name (v), fmt_string);
655           else
656             warning (r, _("String variable %s with width %d has "
657                           "invalid format specifier %s."),
658                      var_get_name (v), var_get_width (v), fmt_string);
659         }
660       goto assign_default;
661     }
662
663   return format;
664
665 assign_default:
666   *report_error = false;
667   return fmt_default_for_width (var_get_width (v));
668 }
669
670 static void parse_value (struct pfm_reader *, int width, union value *);
671
672 /* Read information on all the variables.  */
673 static void
674 read_variables (struct pfm_reader *r, struct dictionary *dict)
675 {
676   char *weight_name = NULL;
677   int i;
678
679   if (!match (r, '4'))
680     error (r, _("Expected variable count record."));
681
682   r->var_cnt = read_int (r);
683   if (r->var_cnt <= 0)
684     error (r, _("Invalid number of variables %d."), r->var_cnt);
685
686   if (match (r, '5'))
687     read_int (r);
688
689   if (match (r, '6'))
690     {
691       weight_name = read_pool_string (r);
692       if (strlen (weight_name) > SHORT_NAME_LEN)
693         error (r, _("Weight variable name (%s) truncated."), weight_name);
694     }
695
696   for (i = 0; i < r->var_cnt; i++)
697     {
698       int width;
699       char name[256];
700       int fmt[6];
701       struct variable *v;
702       struct missing_values miss;
703       struct fmt_spec print, write;
704       bool report_error = true;
705       int j;
706
707       if (!match (r, '7'))
708         error (r, _("Expected variable record."));
709
710       width = read_int (r);
711       if (width < 0)
712         error (r, _("Invalid variable width %d."), width);
713
714       read_string (r, name);
715       for (j = 0; j < 6; j++)
716         fmt[j] = read_int (r);
717
718       if (!dict_id_is_valid (dict, name, false)
719           || *name == '#' || *name == '$')
720         error (r, _("Invalid variable name `%s' in position %d."), name, i);
721       str_uppercase (name);
722
723       if (width < 0 || width > 255)
724         error (r, _("Bad width %d for variable %s."), width, name);
725
726       v = dict_create_var (dict, name, width);
727       if (v == NULL)
728         {
729           unsigned long int i;
730           for (i = 1; ; i++)
731             {
732               char try_name[8 + 1 + INT_STRLEN_BOUND (i) + 1];
733               sprintf (try_name, "%s_%lu", name, i);
734               v = dict_create_var (dict, try_name, width);
735               if (v != NULL)
736                 break;
737             }
738           warning (r, _("Duplicate variable name %s in position %d renamed "
739                         "to %s."), name, i, var_get_name (v));
740         }
741
742       print = convert_format (r, &fmt[0], v, &report_error);
743       write = convert_format (r, &fmt[3], v, &report_error);
744       var_set_print_format (v, &print);
745       var_set_write_format (v, &write);
746
747       /* Range missing values. */
748       mv_init (&miss, width);
749       if (match (r, 'B'))
750         {
751           double x = read_float (r);
752           double y = read_float (r);
753           mv_add_range (&miss, x, y);
754         }
755       else if (match (r, 'A'))
756         mv_add_range (&miss, read_float (r), HIGHEST);
757       else if (match (r, '9'))
758         mv_add_range (&miss, LOWEST, read_float (r));
759
760       /* Single missing values. */
761       while (match (r, '8'))
762         {
763           int mv_width = MIN (width, 8);
764           union value value;
765
766           parse_value (r, mv_width, &value);
767           value_resize (&value, mv_width, width);
768           mv_add_value (&miss, &value);
769           value_destroy (&value, width);
770         }
771
772       var_set_missing_values (v, &miss);
773       mv_destroy (&miss);
774
775       if (match (r, 'C'))
776         {
777           char label[256];
778           read_string (r, label);
779           var_set_label (v, label); /* XXX */
780         }
781     }
782
783   if (weight_name != NULL)
784     {
785       struct variable *weight_var = dict_lookup_var (dict, weight_name);
786       if (weight_var == NULL)
787         error (r, _("Weighting variable %s not present in dictionary."),
788                weight_name);
789
790       dict_set_weight (dict, weight_var);
791     }
792 }
793
794 /* Parse a value of with WIDTH into value V. */
795 static void
796 parse_value (struct pfm_reader *r, int width, union value *v)
797 {
798   value_init (v, width);
799   if (width > 0)
800     {
801       uint8_t buf[256];
802       size_t n_bytes = read_bytes (r, buf);
803       value_copy_buf_rpad (v, width, buf, n_bytes, ' ');
804     }
805   else
806     v->f = read_float (r);
807 }
808
809 /* Parse a value label record and return success. */
810 static void
811 read_value_label (struct pfm_reader *r, struct dictionary *dict)
812 {
813   /* Variables. */
814   int nv;
815   struct variable **v;
816
817   /* Labels. */
818   int n_labels;
819
820   int i;
821
822   nv = read_int (r);
823   v = pool_nalloc (r->pool, nv, sizeof *v);
824   for (i = 0; i < nv; i++)
825     {
826       char name[256];
827       read_string (r, name);
828
829       v[i] = dict_lookup_var (dict, name);
830       if (v[i] == NULL)
831         error (r, _("Unknown variable %s while parsing value labels."), name);
832
833       if (var_get_type (v[0]) != var_get_type (v[i]))
834         error (r, _("Cannot assign value labels to %s and %s, which "
835                     "have different variable types."),
836                var_get_name (v[0]), var_get_name (v[i]));
837     }
838
839   n_labels = read_int (r);
840   for (i = 0; i < n_labels; i++)
841     {
842       union value val;
843       char label[256];
844       int j;
845
846       parse_value (r, var_get_width (v[0]), &val);
847       read_string (r, label);
848
849       /* Assign the value label to each variable. */
850       for (j = 0; j < nv; j++)
851         var_replace_value_label (v[j], &val, label);
852
853       value_destroy (&val, var_get_width (v[0]));
854     }
855 }
856
857 /* Reads a set of documents from portable file R into DICT. */
858 static void
859 read_documents (struct pfm_reader *r, struct dictionary *dict)
860 {
861   int line_cnt;
862   int i;
863
864   line_cnt = read_int (r);
865   for (i = 0; i < line_cnt; i++)
866     {
867       char line[256];
868       read_string (r, line);
869       dict_add_document_line (dict, line, false);
870     }
871 }
872
873 /* Reads and returns one case from portable file R.  Returns a
874    null pointer on failure. */
875 static struct ccase *
876 por_file_casereader_read (struct casereader *reader, void *r_)
877 {
878   struct pfm_reader *r = r_;
879   struct ccase *volatile c;
880   size_t i;
881
882   c = case_create (r->proto);
883   setjmp (r->bail_out);
884   if (!r->ok)
885     {
886       casereader_force_error (reader);
887       case_unref (c);
888       return NULL;
889     }
890
891   /* Check for end of file. */
892   if (r->cc == 'Z')
893     {
894       case_unref (c);
895       return NULL;
896     }
897
898   for (i = 0; i < r->var_cnt; i++)
899     {
900       int width = caseproto_get_width (r->proto, i);
901
902       if (width == 0)
903         case_data_rw_idx (c, i)->f = read_float (r);
904       else
905         {
906           uint8_t buf[256];
907           size_t n_bytes = read_bytes (r, buf);
908           u8_buf_copy_rpad (case_str_rw_idx (c, i), width, buf, n_bytes, ' ');
909         }
910     }
911
912   return c;
913 }
914
915 /* Detects whether FILE is an SPSS portable file.  Returns 1 if so, 0 if not,
916    and a negative errno value if there is an error reading FILE. */
917 static int
918 pfm_detect (FILE *file)
919 {
920   unsigned char header[464];
921   char trans[256];
922   int cooked_cnt, raw_cnt, line_len;
923   int i;
924
925   cooked_cnt = raw_cnt = 0;
926   line_len = 0;
927   while (cooked_cnt < sizeof header)
928     {
929       int c = getc (file);
930       if (c == EOF || raw_cnt++ > 512)
931         return ferror (file) ? -errno : 0;
932       else if (c == '\n')
933         {
934           while (line_len < 80 && cooked_cnt < sizeof header)
935             {
936               header[cooked_cnt++] = ' ';
937               line_len++;
938             }
939           line_len = 0;
940         }
941       else if (c != '\r')
942         {
943           header[cooked_cnt++] = c;
944           line_len++;
945         }
946     }
947
948   memset (trans, 0, 256);
949   for (i = 64; i < 256; i++)
950     {
951       unsigned char c = header[i + 200];
952       if (trans[c] == 0)
953         trans[c] = portable_to_local[i];
954     }
955
956   for (i = 0; i < 8; i++)
957     if (trans[header[i + 456]] != "SPSSPORT"[i])
958       return 0;
959
960   return 1;
961 }
962
963 static const struct casereader_class por_file_casereader_class =
964   {
965     por_file_casereader_read,
966     por_file_casereader_destroy,
967     NULL,
968     NULL,
969   };
970
971 const struct any_reader_class por_file_reader_class =
972   {
973     N_("SPSS Portable File"),
974     pfm_detect,
975     pfm_open,
976     pfm_close,
977     pfm_decode,
978     NULL,                       /* get_strings */
979   };