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