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