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