Cite tokens when reporting invalid identifiers.
[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/minmax.h"
47 #include "gl/xalloc.h"
48 #include "gl/xmemdup0.h"
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52 #define N_(msgid) (msgid)
53
54 /* portable_to_local[PORTABLE] translates the given portable
55    character into the local character set. */
56 static const char portable_to_local[256] =
57   {
58     "                                                                "
59     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
60     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
61     "                                                                "
62   };
63
64 /* Portable file reader. */
65 struct pfm_reader
66   {
67     struct any_reader any_reader;
68     struct pool *pool;          /* All the portable file state. */
69
70     jmp_buf bail_out;           /* longjmp() target for error handling. */
71
72     struct dictionary *dict;
73     struct any_read_info info;
74     struct file_handle *fh;     /* File handle. */
75     struct fh_lock *lock;       /* Read lock for file. */
76     FILE *file;                 /* File stream. */
77     int line_length;            /* Number of characters so far on this line. */
78     char cc;                    /* Current character. */
79     char *trans;                /* 256-byte character set translation table. */
80     int n_vars;                 /* Number of variables. */
81     int weight_index;           /* 0-based index of weight variable, or -1. */
82     struct caseproto *proto;    /* Format of output cases. */
83     bool ok;                    /* Set false on I/O error. */
84   };
85
86 static const struct casereader_class por_file_casereader_class;
87
88 static struct pfm_reader *
89 pfm_reader_cast (const struct any_reader *r_)
90 {
91   assert (r_->klass == &por_file_reader_class);
92   return UP_CAST (r_, struct pfm_reader, any_reader);
93 }
94
95 static void
96 error (struct pfm_reader *r, const char *msg,...)
97      PRINTF_FORMAT (2, 3)
98      NO_RETURN;
99
100 /* Displays MSG as an error message and aborts reading the
101    portable file via longjmp(). */
102 static void
103 error (struct pfm_reader *r, const char *msg, ...)
104 {
105   struct string text;
106   va_list args;
107
108   ds_init_empty (&text);
109   ds_put_format (&text, _("portable file %s corrupt at offset 0x%llx: "),
110                  fh_get_file_name (r->fh), (long long int) ftello (r->file));
111   va_start (args, msg);
112   ds_put_vformat (&text, msg, args);
113   va_end (args);
114
115   struct msg *m = xmalloc (sizeof *m);
116   *m = (struct msg) {
117     .category = MSG_C_GENERAL,
118     .severity = MSG_S_ERROR,
119     .text = ds_steal_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 = xmalloc (sizeof *m);
144   *m = (struct msg) {
145     .category = MSG_C_GENERAL,
146     .severity = MSG_S_WARNING,
147     .text = ds_steal_cstr (&text),
148   };
149   msg_emit (m);
150 }
151
152 /* Close and destroy R.
153    Returns false if an error was detected on R, true otherwise. */
154 static bool
155 pfm_close (struct any_reader *r_)
156 {
157   struct pfm_reader *r = pfm_reader_cast (r_);
158   bool ok;
159
160   dict_unref (r->dict);
161   any_read_info_destroy (&r->info);
162   if (r->file)
163     {
164       if (fn_close (r->fh, r->file) == EOF)
165         {
166           msg (ME, _("Error closing portable file `%s': %s."),
167                fh_get_file_name (r->fh), strerror (errno));
168           r->ok = false;
169         }
170       r->file = NULL;
171     }
172
173   fh_unlock (r->lock);
174   fh_unref (r->fh);
175
176   ok = r->ok;
177   pool_destroy (r->pool);
178
179   return ok;
180 }
181
182 /* Closes portable file reader R, after we're done with it. */
183 static void
184 por_file_casereader_destroy (struct casereader *reader, void *r_)
185 {
186   struct pfm_reader *r = r_;
187   if (!pfm_close (&r->any_reader))
188     casereader_force_error (reader);
189 }
190
191 /* Read a single character into cur_char.  */
192 static void
193 advance (struct pfm_reader *r)
194 {
195   int c;
196
197   /* Read the next character from the file.
198      Ignore carriage returns entirely.
199      Mostly ignore new-lines, but if a new-line occurs before the
200      line has reached 80 bytes in length, then treat the
201      "missing" bytes as spaces. */
202   for (;;)
203     {
204       while ((c = getc (r->file)) == '\r')
205         continue;
206       if (c != '\n')
207         break;
208
209       if (r->line_length < 80)
210         {
211           c = ' ';
212           ungetc ('\n', r->file);
213           break;
214         }
215       r->line_length = 0;
216     }
217   if (c == EOF)
218     error (r, _("Unexpected end of file"));
219
220   if (r->trans != NULL)
221     c = r->trans[c];
222   r->cc = c;
223   r->line_length++;
224 }
225
226 /* Skip a single character if present, and return whether it was
227    skipped. */
228 static inline bool
229 match (struct pfm_reader *r, int c)
230 {
231   if (r->cc == c)
232     {
233       advance (r);
234       return true;
235     }
236   else
237     return false;
238 }
239
240 static void read_header (struct pfm_reader *);
241 static void read_version_data (struct pfm_reader *, struct any_read_info *);
242 static void read_variables (struct pfm_reader *, struct dictionary *);
243 static void read_value_label (struct pfm_reader *, struct dictionary *);
244 static void read_documents (struct pfm_reader *, struct dictionary *);
245
246 /* Reads the dictionary from file with handle H, and returns it in a
247    dictionary structure.  This dictionary may be modified in order to
248    rename, reorder, and delete variables, etc. */
249 static struct any_reader *
250 pfm_open (struct file_handle *fh)
251 {
252   struct pool *volatile pool = NULL;
253   struct pfm_reader *volatile r = NULL;
254
255   /* Create and initialize reader. */
256   pool = pool_create ();
257   r = pool_alloc (pool, sizeof *r);
258   r->any_reader.klass = &por_file_reader_class;
259   r->dict = dict_create (get_default_encoding ());
260   memset (&r->info, 0, sizeof r->info);
261   r->pool = pool;
262   r->fh = fh_ref (fh);
263   r->lock = NULL;
264   r->file = NULL;
265   r->line_length = 0;
266   r->weight_index = -1;
267   r->trans = NULL;
268   r->n_vars = 0;
269   r->proto = NULL;
270   r->ok = true;
271   if (setjmp (r->bail_out))
272     goto error;
273
274   /* Lock file. */
275   /* TRANSLATORS: this fragment will be interpolated into
276      messages in fh_lock() that identify types of files. */
277   r->lock = fh_lock (fh, FH_REF_FILE, N_("portable file"), FH_ACC_READ, false);
278   if (r->lock == NULL)
279     goto error;
280
281   /* Open file. */
282   r->file = fn_open (r->fh, "rb");
283   if (r->file == NULL)
284     {
285       msg (ME, _("An error occurred while opening `%s' for reading "
286                  "as a portable file: %s."),
287            fh_get_file_name (r->fh), strerror (errno));
288       goto error;
289     }
290
291   /* Read header, version, date info, product id, variables. */
292   read_header (r);
293   read_version_data (r, &r->info);
294   read_variables (r, r->dict);
295
296   /* Read value labels. */
297   while (match (r, 'D'))
298     read_value_label (r, r->dict);
299
300   /* Read documents. */
301   if (match (r, 'E'))
302     read_documents (r, r->dict);
303
304   /* Check that we've made it to the data. */
305   if (!match (r, 'F'))
306     error (r, _("Data record expected."));
307
308   r->proto = caseproto_ref_pool (dict_get_proto (r->dict), r->pool);
309   return &r->any_reader;
310
311  error:
312   pfm_close (&r->any_reader);
313   return NULL;
314 }
315
316 static struct casereader *
317 pfm_decode (struct any_reader *r_, const char *encoding UNUSED,
318             struct dictionary **dictp, struct any_read_info *info)
319 {
320   struct pfm_reader *r = pfm_reader_cast (r_);
321
322   *dictp = r->dict;
323   r->dict = NULL;
324
325   if (info)
326     {
327       *info = r->info;
328       memset (&r->info, 0, sizeof r->info);
329     }
330
331   return casereader_create_sequential (NULL, r->proto, CASENUMBER_MAX,
332                                        &por_file_casereader_class, r);
333 }
334 \f
335 /* Returns the value of base-30 digit C,
336    or -1 if C is not a base-30 digit. */
337 static int
338 base_30_value (unsigned char c)
339 {
340   static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST";
341   const char *p = strchr (base_30_digits, c);
342   return p != NULL ? p - base_30_digits : -1;
343 }
344
345 /* Read a floating point value and return its value. */
346 static double
347 read_float (struct pfm_reader *r)
348 {
349   double num = 0.;
350   int exponent = 0;
351   bool got_dot = false;         /* Seen a decimal point? */
352   bool got_digit = false;       /* Seen any digits? */
353   bool negative = false;        /* Number is negative? */
354
355   /* Skip leading spaces. */
356   while (match (r, ' '))
357     continue;
358
359   /* `*' indicates system-missing. */
360   if (match (r, '*'))
361     {
362       advance (r);      /* Probably a dot (.) but doesn't appear to matter. */
363       return SYSMIS;
364     }
365
366   negative = match (r, '-');
367   for (;;)
368     {
369       int digit = base_30_value (r->cc);
370       if (digit != -1)
371         {
372           got_digit = true;
373
374           /* Make sure that multiplication by 30 will not overflow.  */
375           if (num > DBL_MAX * (1. / 30.))
376             /* The value of the digit doesn't matter, since we have already
377                gotten as many digits as can be represented in a `double'.
378                This doesn't necessarily mean the result will overflow.
379                The exponent may reduce it to within range.
380
381                We just need to record that there was another
382                digit so that we can multiply by 10 later.  */
383             ++exponent;
384           else
385             num = (num * 30.0) + digit;
386
387           /* Keep track of the number of digits after the decimal point.
388              If we just divided by 30 here, we would lose precision.  */
389           if (got_dot)
390             --exponent;
391         }
392       else if (!got_dot && r->cc == '.')
393         /* Record that we have found the decimal point.  */
394         got_dot = 1;
395       else
396         /* Any other character terminates the number.  */
397         break;
398
399       advance (r);
400     }
401
402   /* Check that we had some digits. */
403   if (!got_digit)
404     error (r, _("Number expected."));
405
406   /* Get exponent if any. */
407   if (r->cc == '+' || r->cc == '-')
408     {
409       long int exp = 0;
410       bool negative_exponent = r->cc == '-';
411       int digit;
412
413       for (advance (r); (digit = base_30_value (r->cc)) != -1; advance (r))
414         {
415           if (exp > LONG_MAX / 30)
416             {
417               exp = LONG_MAX;
418               break;
419             }
420           exp = exp * 30 + digit;
421         }
422
423       /* We don't check whether there were actually any digits, but we
424          probably should. */
425       if (negative_exponent)
426         exp = -exp;
427       exponent += exp;
428     }
429
430   /* Numbers must end with `/'. */
431   if (!match (r, '/'))
432     error (r, _("Missing numeric terminator."));
433
434   /* Multiply `num' by 30 to the `exponent' power, checking for
435      overflow.  */
436   if (exponent < 0)
437     num *= pow (30.0, (double) exponent);
438   else if (exponent > 0)
439     {
440       if (num > DBL_MAX * pow (30.0, (double) -exponent))
441         num = DBL_MAX;
442       else
443         num *= pow (30.0, (double) exponent);
444     }
445
446   return negative ? -num : num;
447 }
448
449 /* Read an integer and return its value. */
450 static int
451 read_int (struct pfm_reader *r)
452 {
453   double f = read_float (r);
454   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
455     error (r, _("Invalid integer."));
456   return f;
457 }
458
459 /* Reads a string into BUF, which must have room for 256
460    characters. */
461 static void
462 read_string (struct pfm_reader *r, char *buf)
463 {
464   int n = read_int (r);
465   if (n < 0 || n > 255)
466     error (r, _("Bad string length %d."), n);
467
468   while (n-- > 0)
469     {
470       *buf++ = r->cc;
471       advance (r);
472     }
473   *buf = '\0';
474 }
475
476
477 /* Reads a string into BUF, which must have room for 256
478    characters.
479    Returns the number of bytes read.
480 */
481 static size_t
482 read_bytes (struct pfm_reader *r, uint8_t *buf)
483 {
484   int n = read_int (r);
485   if (n < 0 || n > 255)
486     error (r, _("Bad string length %d."), n);
487
488   while (n-- > 0)
489     {
490       *buf++ = r->cc;
491       advance (r);
492     }
493   return n;
494 }
495
496
497
498 /* Reads a string and returns a copy of it allocated from R's
499    pool. */
500 static char *
501 read_pool_string (struct pfm_reader *r)
502 {
503   char string[256];
504   read_string (r, string);
505   return pool_strdup (r->pool, string);
506 }
507 \f
508 /* Reads the 464-byte file header. */
509 static void
510 read_header (struct pfm_reader *r)
511 {
512   char *trans;
513   int i;
514
515   /* Read and ignore vanity splash strings. */
516   for (i = 0; i < 200; i++)
517     advance (r);
518
519   /* Skip the first 64 characters of the translation table.
520      We don't care about these.  They are probably all set to
521      '0', marking them as untranslatable, and that would screw
522      up our actual translation of the real '0'. */
523   for (i = 0; i < 64; i++)
524     advance (r);
525
526   /* Read the rest of the translation table. */
527   trans = pool_malloc (r->pool, 256);
528   memset (trans, 0, 256);
529   for (; i < 256; i++)
530     {
531       unsigned char c;
532
533       advance (r);
534
535       c = r->cc;
536       if (trans[c] == 0)
537         trans[c] = portable_to_local[i];
538     }
539
540   /* Set up the translation table, then read the first
541      translated character. */
542   r->trans = trans;
543   advance (r);
544
545   /* Skip and verify signature. */
546   for (i = 0; i < 8; i++)
547     if (!match (r, "SPSSPORT"[i]))
548       {
549         msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh));
550         longjmp (r->bail_out, 1);
551       }
552 }
553
554 /* Reads the version and date info record, as well as product and
555    subproduct identification records if present. */
556 static void
557 read_version_data (struct pfm_reader *r, struct any_read_info *info)
558 {
559   static const char empty_string[] = "";
560   char *date, *time;
561   const char *product, *subproduct;
562   int i;
563
564   /* Read file. */
565   if (!match (r, 'A'))
566     error (r, _("Unrecognized version code `%c'."), r->cc);
567   date = read_pool_string (r);
568   time = read_pool_string (r);
569   product = match (r, '1') ? read_pool_string (r) : empty_string;
570   if (match (r, '2'))
571     {
572       /* Skip "author" field. */
573       read_pool_string (r);
574     }
575   subproduct = match (r, '3') ? read_pool_string (r) : empty_string;
576
577   /* Validate file. */
578   if (strlen (date) != 8)
579     error (r, _("Bad date string length %zu."), strlen (date));
580   if (strlen (time) != 6)
581     error (r, _("Bad time string length %zu."), strlen (time));
582
583   /* Save file info. */
584   if (info != NULL)
585     {
586       memset (info, 0, sizeof *info);
587
588       info->float_format = FLOAT_NATIVE_DOUBLE;
589       info->integer_format = INTEGER_NATIVE;
590       info->compression = ANY_COMP_NONE;
591       info->n_cases = -1;
592
593       /* Date. */
594       info->creation_date = xmalloc (11);
595       for (i = 0; i < 8; i++)
596         {
597           static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
598           info->creation_date[map[i]] = date[i];
599         }
600       info->creation_date[2] = info->creation_date[5] = ' ';
601       info->creation_date[10] = '\0';
602
603       /* Time. */
604       info->creation_time = xmalloc (9);
605       for (i = 0; i < 6; i++)
606         {
607           static const int map[] = {0, 1, 3, 4, 6, 7};
608           info->creation_time[map[i]] = time[i];
609         }
610       info->creation_time[2] = info->creation_time[5] = ' ';
611       info->creation_time[8] = 0;
612
613       /* Product. */
614       info->product = xstrdup (product);
615       info->product_ext = xstrdup (subproduct);
616     }
617 }
618
619 /* Translates a format specification read from portable file R as
620    the three integers INTS into a normal format specifier FORMAT,
621    checking that the format is appropriate for variable V. */
622 static struct fmt_spec
623 convert_format (struct pfm_reader *r, const int portable_format[3],
624                 struct variable *v, bool *report_error)
625 {
626   struct fmt_spec format;
627   bool ok;
628
629   if (!fmt_from_io (portable_format[0], &format.type))
630     {
631       if (*report_error)
632         warning (r, _("%s: Bad format specifier byte (%d).  Variable "
633                       "will be assigned a default format."),
634                  var_get_name (v), portable_format[0]);
635       goto assign_default;
636     }
637
638   format.w = portable_format[1];
639   format.d = portable_format[2];
640
641   msg_disable ();
642   ok = (fmt_check_output (&format)
643         && fmt_check_width_compat (&format, var_get_width (v)));
644   msg_enable ();
645
646   if (!ok)
647     {
648       if (*report_error)
649         {
650           char fmt_string[FMT_STRING_LEN_MAX + 1];
651           fmt_to_string (&format, fmt_string);
652           if (var_is_numeric (v))
653             warning (r, _("Numeric variable %s has invalid format "
654                           "specifier %s."),
655                      var_get_name (v), fmt_string);
656           else
657             warning (r, _("String variable %s with width %d has "
658                           "invalid format specifier %s."),
659                      var_get_name (v), var_get_width (v), fmt_string);
660         }
661       goto assign_default;
662     }
663
664   return format;
665
666 assign_default:
667   *report_error = false;
668   return fmt_default_for_width (var_get_width (v));
669 }
670
671 static void parse_value (struct pfm_reader *, int width, union value *);
672
673 /* Read information on all the variables.  */
674 static void
675 read_variables (struct pfm_reader *r, struct dictionary *dict)
676 {
677   char *weight_name = NULL;
678   int i;
679
680   if (!match (r, '4'))
681     error (r, _("Expected variable count record."));
682
683   r->n_vars = read_int (r);
684   if (r->n_vars <= 0)
685     error (r, _("Invalid number of variables %d."), r->n_vars);
686
687   if (match (r, '5'))
688     read_int (r);
689
690   if (match (r, '6'))
691     {
692       weight_name = read_pool_string (r);
693       if (strlen (weight_name) > SHORT_NAME_LEN)
694         error (r, _("Weight variable name (%s) truncated."), weight_name);
695     }
696
697   for (i = 0; i < r->n_vars; i++)
698     {
699       int width;
700       char name[256];
701       int fmt[6];
702       struct variable *v;
703       struct missing_values miss;
704       struct fmt_spec print, write;
705       bool report_error = true;
706       int j;
707
708       if (!match (r, '7'))
709         error (r, _("Expected variable record."));
710
711       width = read_int (r);
712       if (width < 0)
713         error (r, _("Invalid variable width %d."), width);
714
715       read_string (r, name);
716       for (j = 0; j < 6; j++)
717         fmt[j] = read_int (r);
718
719       if (!dict_id_is_valid (dict, name) || *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 = xasprintf ("%s_%lu", name, i);
733               v = dict_create_var (dict, try_name, width);
734               free (try_name);
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 n_lines = read_int (r);
862   for (int i = 0; i < n_lines; i++)
863     {
864       char line[256];
865       read_string (r, line);
866       dict_add_document_line (dict, line, false);
867     }
868 }
869
870 /* Reads and returns one case from portable file R.  Returns a
871    null pointer on failure. */
872 static struct ccase *
873 por_file_casereader_read (struct casereader *reader, void *r_)
874 {
875   struct pfm_reader *r = r_;
876   struct ccase *volatile c;
877   size_t i;
878
879   c = case_create (r->proto);
880   setjmp (r->bail_out);
881   if (!r->ok)
882     {
883       casereader_force_error (reader);
884       case_unref (c);
885       return NULL;
886     }
887
888   /* Check for end of file. */
889   if (r->cc == 'Z')
890     {
891       case_unref (c);
892       return NULL;
893     }
894
895   for (i = 0; i < r->n_vars; i++)
896     {
897       int width = caseproto_get_width (r->proto, i);
898
899       if (width == 0)
900         *case_num_rw_idx (c, i) = read_float (r);
901       else
902         {
903           uint8_t buf[256];
904           size_t n_bytes = read_bytes (r, buf);
905           u8_buf_copy_rpad (case_str_rw_idx (c, i), width, buf, n_bytes, ' ');
906         }
907     }
908
909   return c;
910 }
911
912 /* Detects whether FILE is an SPSS portable file.  Returns 1 if so, 0 if not,
913    and a negative errno value if there is an error reading FILE. */
914 static int
915 pfm_detect (FILE *file)
916 {
917   unsigned char header[464];
918   char trans[256];
919   int n_cooked, n_raws, line_len;
920   int i;
921
922   n_cooked = n_raws = 0;
923   line_len = 0;
924   while (n_cooked < sizeof header)
925     {
926       int c = getc (file);
927       if (c == EOF || n_raws++ > 512)
928         return ferror (file) ? -errno : 0;
929       else if (c == '\n')
930         {
931           while (line_len < 80 && n_cooked < sizeof header)
932             {
933               header[n_cooked++] = ' ';
934               line_len++;
935             }
936           line_len = 0;
937         }
938       else if (c != '\r')
939         {
940           header[n_cooked++] = c;
941           line_len++;
942         }
943     }
944
945   memset (trans, 0, 256);
946   for (i = 64; i < 256; i++)
947     {
948       unsigned char c = header[i + 200];
949       if (trans[c] == 0)
950         trans[c] = portable_to_local[i];
951     }
952
953   for (i = 0; i < 8; i++)
954     if (trans[header[i + 456]] != "SPSSPORT"[i])
955       return 0;
956
957   return 1;
958 }
959
960 static const struct casereader_class por_file_casereader_class =
961   {
962     por_file_casereader_read,
963     por_file_casereader_destroy,
964     NULL,
965     NULL,
966   };
967
968 const struct any_reader_class por_file_reader_class =
969   {
970     N_("SPSS Portable File"),
971     pfm_detect,
972     pfm_open,
973     pfm_close,
974     pfm_decode,
975     NULL,                       /* get_strings */
976   };