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