GNU standards require "file name" instead of "filename" in
[pspp-builds.git] / src / data / sys-file-reader.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <float.h>
25 #include <c-ctype.h>
26
27 #include <libpspp/alloc.h>
28 #include <libpspp/message.h>
29 #include <libpspp/compiler.h>
30 #include <libpspp/magic.h>
31 #include <libpspp/misc.h>
32 #include <libpspp/str.h>
33
34 #include "sys-file-reader.h"
35 #include "sfm-private.h"
36 #include "case.h"
37 #include "dictionary.h"
38 #include "file-handle-def.h"
39 #include "file-name.h"
40 #include "format.h"
41 #include "value-labels.h"
42 #include "variable.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46
47 /* System file reader. */
48 struct sfm_reader
49   {
50     struct file_handle *fh;     /* File handle. */
51     FILE *file;                 /* File stream. */
52
53     int reverse_endian;         /* 1=file has endianness opposite us. */
54     int fix_specials;           /* 1=SYSMIS/HIGHEST/LOWEST differs from us. */
55     int value_cnt;              /* Number of `union values's per case. */
56     long case_cnt;              /* Number of cases, -1 if unknown. */
57     int compressed;             /* 1=compressed, 0=not compressed. */
58     double bias;                /* Compression bias, usually 100.0. */
59     int weight_idx;             /* 0-based index of weighting variable, or -1. */
60     bool ok;                    /* False after an I/O error or corrupt data. */
61
62     /* Variables. */
63     struct sfm_var *vars;       /* Variables. */
64
65     /* File's special constants. */
66     flt64 sysmis;
67     flt64 highest;
68     flt64 lowest;
69
70     /* Decompression buffer. */
71     flt64 *buf;                 /* Buffer data. */
72     flt64 *ptr;                 /* Current location in buffer. */
73     flt64 *end;                 /* End of buffer data. */
74
75     /* Compression instruction octet. */
76     unsigned char x[8];         /* Current instruction octet. */
77     unsigned char *y;           /* Location in current instruction octet. */
78   };
79
80 /* A variable in a system file. */
81 struct sfm_var 
82   {
83     int width;                  /* 0=numeric, otherwise string width. */
84     int fv;                     /* Index into case. */
85   };
86 \f
87 /* Utilities. */
88
89 /* Swap bytes *A and *B. */
90 static inline void
91 bswap (char *a, char *b) 
92 {
93   char t = *a;
94   *a = *b;
95   *b = t;
96 }
97
98 /* Reverse the byte order of 32-bit integer *X. */
99 static inline void
100 bswap_int32 (int32_t *x_)
101 {
102   char *x = (char *) x_;
103   bswap (x + 0, x + 3);
104   bswap (x + 1, x + 2);
105 }
106
107 /* Reverse the byte order of 64-bit floating point *X. */
108 static inline void
109 bswap_flt64 (flt64 *x_)
110 {
111   char *x = (char *) x_;
112   bswap (x + 0, x + 7);
113   bswap (x + 1, x + 6);
114   bswap (x + 2, x + 5);
115   bswap (x + 3, x + 4);
116 }
117
118 static void
119 corrupt_msg (int class, const char *format,...)
120      PRINTF_FORMAT (2, 3);
121
122 /* Displays a corrupt sysfile error. */
123 static void
124 corrupt_msg (int class, const char *format,...)
125 {
126   struct error e;
127   va_list args;
128
129   e.class = class;
130   e.where.file_name = NULL;
131   e.where.line_number = 0;
132   e.title = _("corrupt system file: ");
133
134   va_start (args, format);
135   err_vmsg (&e, format, args);
136   va_end (args);
137 }
138
139 /* Closes a system file after we're done with it. */
140 void
141 sfm_close_reader (struct sfm_reader *r)
142 {
143   if (r == NULL)
144     return;
145
146   if (r->file)
147     {
148       if (fn_close (fh_get_file_name (r->fh), r->file) == EOF)
149         msg (ME, _("%s: Closing system file: %s."),
150              fh_get_file_name (r->fh), strerror (errno));
151       r->file = NULL;
152     }
153
154   if (r->fh != NULL)
155     fh_close (r->fh, "system file", "rs");
156   
157   free (r->vars);
158   free (r->buf);
159   free (r);
160 }
161 \f
162 /* Dictionary reader. */
163
164 static void buf_unread(struct sfm_reader *r, size_t byte_cnt);
165
166 static void *buf_read (struct sfm_reader *, void *buf, size_t byte_cnt,
167                        size_t min_alloc);
168
169 static int read_header (struct sfm_reader *,
170                         struct dictionary *, struct sfm_read_info *);
171 static int parse_format_spec (struct sfm_reader *, int32_t,
172                               struct fmt_spec *, const struct variable *);
173 static int read_value_labels (struct sfm_reader *, struct dictionary *,
174                               struct variable **var_by_idx);
175 static int read_variables (struct sfm_reader *,
176                            struct dictionary *, struct variable ***var_by_idx);
177 static int read_machine_int32_info (struct sfm_reader *, int size, int count);
178 static int read_machine_flt64_info (struct sfm_reader *, int size, int count);
179 static int read_documents (struct sfm_reader *, struct dictionary *);
180
181 static int fread_ok (struct sfm_reader *, void *, size_t);
182
183 /* Displays the message X with corrupt_msg, then jumps to the error
184    label. */
185 #define lose(X)                                 \
186         do {                                    \
187             corrupt_msg X;                      \
188             goto error;                         \
189         } while (0)
190
191 /* Calls buf_read with the specified arguments, and jumps to
192    error if the read fails. */
193 #define assertive_buf_read(a,b,c,d)             \
194         do {                                    \
195             if (!buf_read (a,b,c,d))            \
196               goto error;                       \
197         } while (0)
198
199 /* Opens the system file designated by file handle FH for
200    reading.  Reads the system file's dictionary into *DICT.
201    If INFO is non-null, then it receives additional info about the
202    system file. */
203 struct sfm_reader *
204 sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
205                  struct sfm_read_info *info)
206 {
207   struct sfm_reader *r = NULL;
208   struct variable **var_by_idx = NULL;
209
210   *dict = dict_create ();
211   if (!fh_open (fh, FH_REF_FILE, "system file", "rs"))
212     goto error;
213
214   /* Create and initialize reader. */
215   r = xmalloc (sizeof *r);
216   r->fh = fh;
217   r->file = fn_open (fh_get_file_name (fh), "rb");
218
219   r->reverse_endian = 0;
220   r->fix_specials = 0;
221   r->value_cnt = 0;
222   r->case_cnt = 0;
223   r->compressed = 0;
224   r->bias = 100.0;
225   r->weight_idx = -1;
226   r->ok = true;
227
228   r->vars = NULL;
229
230   r->sysmis = -FLT64_MAX;
231   r->highest = FLT64_MAX;
232   r->lowest = second_lowest_flt64;
233
234   r->buf = r->ptr = r->end = NULL;
235   r->y = r->x + sizeof r->x;
236
237   /* Check that file open succeeded. */
238   if (r->file == NULL)
239     {
240       msg (ME, _("An error occurred while opening \"%s\" for reading "
241                  "as a system file: %s."),
242            fh_get_file_name (r->fh), strerror (errno));
243       goto error;
244     }
245
246   /* Read header and variables. */
247   if (!read_header (r, *dict, info) || !read_variables (r, *dict, &var_by_idx))
248     goto error;
249
250
251   /* Handle weighting. */
252   if (r->weight_idx != -1)
253     {
254       struct variable *weight_var;
255
256       if (r->weight_idx < 0 || r->weight_idx >= r->value_cnt)
257         lose ((ME, _("%s: Index of weighting variable (%d) is not between 0 "
258                      "and number of elements per case (%d)."),
259                fh_get_file_name (r->fh), r->weight_idx, r->value_cnt));
260
261
262       weight_var = var_by_idx[r->weight_idx];
263
264       if (weight_var == NULL)
265         lose ((ME,
266                _("%s: Weighting variable may not be a continuation of "
267                "a long string variable."), fh_get_file_name (fh)));
268       else if (weight_var->type == ALPHA)
269         lose ((ME, _("%s: Weighting variable may not be a string variable."),
270                fh_get_file_name (fh)));
271
272       dict_set_weight (*dict, weight_var);
273     }
274   else
275     dict_set_weight (*dict, NULL);
276
277   /* Read records of types 3, 4, 6, and 7. */
278   for (;;)
279     {
280       int32_t rec_type;
281
282       assertive_buf_read (r, &rec_type, sizeof rec_type, 0);
283       if (r->reverse_endian)
284         bswap_int32 (&rec_type);
285
286       switch (rec_type)
287         {
288         case 3:
289           if (!read_value_labels (r, *dict, var_by_idx))
290             goto error;
291           break;
292
293         case 4:
294           lose ((ME, _("%s: Orphaned variable index record (type 4).  Type 4 "
295                        "records must always immediately follow type 3 "
296                        "records."),
297                  fh_get_file_name (r->fh)));
298
299         case 6:
300           if (!read_documents (r, *dict))
301             goto error;
302           break;
303
304         case 7:
305           {
306             struct
307               {
308                 int32_t subtype P;
309                 int32_t size P;
310                 int32_t count P;
311               }
312             data;
313             unsigned long bytes;
314
315             int skip = 0;
316
317             assertive_buf_read (r, &data, sizeof data, 0);
318             if (r->reverse_endian)
319               {
320                 bswap_int32 (&data.subtype);
321                 bswap_int32 (&data.size);
322                 bswap_int32 (&data.count);
323               }
324             bytes = data.size * data.count;
325             if (bytes < data.size || bytes < data.count)
326               lose ((ME, "%s: Record type %d subtype %d too large.",
327                      fh_get_file_name (r->fh), rec_type, data.subtype));
328
329             switch (data.subtype)
330               {
331               case 3:
332                 if (!read_machine_int32_info (r, data.size, data.count))
333                   goto error;
334                 break;
335
336               case 4:
337                 if (!read_machine_flt64_info (r, data.size, data.count))
338                   goto error;
339                 break;
340
341               case 5:
342               case 6:  /* ?? Used by SPSS 8.0. */
343                 skip = 1;
344                 break;
345                 
346               case 11: /* Variable display parameters */
347                 {
348                   const int  n_vars = data.count / 3 ;
349                   int i;
350                   if ( data.count % 3 || n_vars > dict_get_var_cnt(*dict) ) 
351                     {
352                       msg (MW, _("%s: Invalid subrecord length. "
353                                  "Record: 7; Subrecord: 11"), 
354                            fh_get_file_name (r->fh));
355                       skip = 1;
356                     }
357
358                   for ( i = 0 ; i < min(n_vars, dict_get_var_cnt(*dict)) ; ++i ) 
359                     {
360                       struct
361                       {
362                         int32_t measure P;
363                         int32_t width P;
364                         int32_t align P;
365                       }
366                       params;
367
368                       struct variable *v;
369
370                       assertive_buf_read (r, &params, sizeof(params), 0);
371
372                       v = dict_get_var(*dict, i);
373
374                       v->measure = params.measure;
375                       v->display_width = params.width;
376                       v->alignment = params.align;
377                     }
378                 }
379                 break;
380
381               case 13: /* SPSS 12.0 Long variable name map */
382                 {
383                   char *buf, *short_name, *save_ptr;
384                   int idx;
385
386                   /* Read data. */
387                   buf = xmalloc (bytes + 1);
388                   if (!buf_read (r, buf, bytes, 0)) 
389                     {
390                       free (buf);
391                       goto error;
392                     }
393                   buf[bytes] = '\0';
394
395                   /* Parse data. */
396                   for (short_name = strtok_r (buf, "=", &save_ptr), idx = 0;
397                        short_name != NULL;
398                        short_name = strtok_r (NULL, "=", &save_ptr), idx++)
399                     {
400                       char *long_name = strtok_r (NULL, "\t", &save_ptr);
401                       struct variable *v;
402
403                       /* Validate long name. */
404                       if (long_name == NULL)
405                         {
406                           msg (MW, _("%s: Trailing garbage in long variable "
407                                      "name map."),
408                                fh_get_file_name (r->fh));
409                           break;
410                         }
411                       if (!var_is_valid_name (long_name, false))
412                         {
413                           msg (MW, _("%s: Long variable mapping to invalid "
414                                      "variable name `%s'."),
415                                fh_get_file_name (r->fh), long_name);
416                           break;
417                         }
418                       
419                       /* Find variable using short name. */
420                       v = dict_lookup_var (*dict, short_name);
421                       if (v == NULL)
422                         {
423                           msg (MW, _("%s: Long variable mapping for "
424                                      "nonexistent variable %s."),
425                                fh_get_file_name (r->fh), short_name);
426                           break;
427                         }
428
429                       /* Identify any duplicates. */
430                       if ( compare_var_names(short_name, long_name, 0) &&
431                           NULL != dict_lookup_var (*dict, long_name))
432                         lose ((ME, _("%s: Duplicate long variable name `%s' "
433                                      "within system file."),
434                                fh_get_file_name (r->fh), long_name));
435
436
437                       /* Set long name.
438                          Renaming a variable may clear the short
439                          name, but we want to retain it, so
440                          re-set it explicitly. */
441                       dict_rename_var (*dict, v, long_name);
442                       var_set_short_name (v, short_name);
443
444                       /* For compatability, make sure dictionary
445                          is in long variable name map order.  In
446                          the common case, this has no effect,
447                          because the dictionary and the long
448                          variable name map are already in the
449                          same order. */
450                       dict_reorder_var (*dict, v, idx);
451                     }
452
453                   /* Free data. */
454                   free (buf);
455                 }
456                 break;
457
458               default:
459                 msg (MW, _("%s: Unrecognized record type 7, subtype %d "
460                            "encountered in system file."),
461                      fh_get_file_name (r->fh), data.subtype);
462                 skip = 1;
463               }
464
465             if (skip)
466               {
467                 void *x = buf_read (r, NULL, data.size * data.count, 0);
468                 if (x == NULL)
469                   goto error;
470                 free (x);
471               }
472           }
473           break;
474
475         case 999:
476           {
477             int32_t filler;
478
479             assertive_buf_read (r, &filler, sizeof filler, 0);
480             goto success;
481           }
482
483         default:
484           corrupt_msg(MW, _("%s: Unrecognized record type %d."),
485                  fh_get_file_name (r->fh), rec_type);
486         }
487     }
488
489 success:
490   /* Come here on successful completion. */
491   free (var_by_idx);
492   return r;
493
494 error:
495   /* Come here on unsuccessful completion. */
496   sfm_close_reader (r);
497   free (var_by_idx);
498   if (*dict != NULL) 
499     {
500       dict_destroy (*dict);
501       *dict = NULL; 
502     }
503   return NULL;
504 }
505
506 /* Read record type 7, subtype 3. */
507 static int
508 read_machine_int32_info (struct sfm_reader *r, int size, int count)
509 {
510   int32_t data[8];
511   int file_bigendian;
512
513   int i;
514
515   if (size != sizeof (int32_t) || count != 8)
516     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
517                  "subtype 3.    Expected size %d, count 8."),
518            fh_get_file_name (r->fh), size, count, sizeof (int32_t)));
519
520   assertive_buf_read (r, data, sizeof data, 0);
521   if (r->reverse_endian)
522     for (i = 0; i < 8; i++)
523       bswap_int32 (&data[i]);
524
525 #ifdef FPREP_IEEE754
526   if (data[4] != 1)
527     lose ((ME, _("%s: Floating-point representation in system file is not "
528                  "IEEE-754.  PSPP cannot convert between floating-point "
529                  "formats."),
530            fh_get_file_name (r->fh)));
531 #else
532 #error Add support for your floating-point format.
533 #endif
534
535 #ifdef WORDS_BIGENDIAN
536   file_bigendian = 1;
537 #else
538   file_bigendian = 0;
539 #endif
540   if (r->reverse_endian)
541     file_bigendian ^= 1;
542   if (file_bigendian ^ (data[6] == 1))
543     lose ((ME, _("%s: File-indicated endianness (%s) does not match "
544                  "endianness intuited from file header (%s)."),
545            fh_get_file_name (r->fh),
546            file_bigendian ? _("big-endian") : _("little-endian"),
547            data[6] == 1 ? _("big-endian") : (data[6] == 2 ? _("little-endian")
548                                           : _("unknown"))));
549
550   /* PORTME: Character representation code. */
551   if (data[7] != 2 && data[7] != 3) 
552     lose ((ME, _("%s: File-indicated character representation code (%s) is "
553                  "not ASCII."),
554            fh_get_file_name (r->fh),
555            (data[7] == 1 ? "EBCDIC"
556             : (data[7] == 4 ? _("DEC Kanji") : _("Unknown")))));
557
558   return 1;
559
560 error:
561   return 0;
562 }
563
564 /* Read record type 7, subtype 4. */
565 static int
566 read_machine_flt64_info (struct sfm_reader *r, int size, int count)
567 {
568   flt64 data[3];
569   int i;
570
571   if (size != sizeof (flt64) || count != 3)
572     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
573                  "subtype 4.    Expected size %d, count 8."),
574            fh_get_file_name (r->fh), size, count, sizeof (flt64)));
575
576   assertive_buf_read (r, data, sizeof data, 0);
577   if (r->reverse_endian)
578     for (i = 0; i < 3; i++)
579       bswap_flt64 (&data[i]);
580
581   if (data[0] != SYSMIS || data[1] != FLT64_MAX
582       || data[2] != second_lowest_flt64)
583     {
584       r->sysmis = data[0];
585       r->highest = data[1];
586       r->lowest = data[2];
587       msg (MW, _("%s: File-indicated value is different from internal value "
588                  "for at least one of the three system values.  SYSMIS: "
589                  "indicated %g, expected %g; HIGHEST: %g, %g; LOWEST: "
590                  "%g, %g."),
591            fh_get_file_name (r->fh), (double) data[0], (double) SYSMIS,
592            (double) data[1], (double) FLT64_MAX,
593            (double) data[2], (double) second_lowest_flt64);
594     }
595   
596   return 1;
597
598 error:
599   return 0;
600 }
601
602 static int
603 read_header (struct sfm_reader *r,
604              struct dictionary *dict, struct sfm_read_info *info)
605 {
606   struct sysfile_header hdr;            /* Disk buffer. */
607   char prod_name[sizeof hdr.prod_name + 1];     /* Buffer for product name. */
608   int skip_amt = 0;                     /* Amount of product name to omit. */
609   int i;
610
611   /* Read header, check magic. */
612   assertive_buf_read (r, &hdr, sizeof hdr, 0);
613   if (strncmp ("$FL2", hdr.rec_type, 4) != 0)
614     lose ((ME, _("%s: Bad magic.  Proper system files begin with "
615                  "the four characters `$FL2'. This file will not be read."),
616            fh_get_file_name (r->fh)));
617
618   /* Check eye-category.her string. */
619   memcpy (prod_name, hdr.prod_name, sizeof hdr.prod_name);
620   for (i = 0; i < 60; i++)
621     if (!c_isprint ((unsigned char) prod_name[i]))
622       prod_name[i] = ' ';
623   for (i = 59; i >= 0; i--)
624     if (!c_isgraph ((unsigned char) prod_name[i]))
625       {
626         prod_name[i] = '\0';
627         break;
628       }
629   prod_name[60] = '\0';
630   
631   {
632 #define N_PREFIXES 2
633     static const char *prefix[N_PREFIXES] =
634       {
635         "@(#) SPSS DATA FILE",
636         "SPSS SYSTEM FILE.",
637       };
638
639     int i;
640
641     for (i = 0; i < N_PREFIXES; i++)
642       if (!strncmp (prefix[i], hdr.prod_name, strlen (prefix[i])))
643         {
644           skip_amt = strlen (prefix[i]);
645           break;
646         }
647   }
648   
649   /* Check endianness. */
650   if (hdr.layout_code == 2)
651     r->reverse_endian = 0;
652   else
653     {
654       bswap_int32 (&hdr.layout_code);
655       if (hdr.layout_code != 2)
656         lose ((ME, _("%s: File layout code has unexpected value %d.  Value "
657                      "should be 2, in big-endian or little-endian format."),
658                fh_get_file_name (r->fh), hdr.layout_code));
659
660       r->reverse_endian = 1;
661       bswap_int32 (&hdr.case_size);
662       bswap_int32 (&hdr.compress);
663       bswap_int32 (&hdr.weight_idx);
664       bswap_int32 (&hdr.case_cnt);
665       bswap_flt64 (&hdr.bias);
666     }
667
668
669   /* Copy basic info and verify correctness. */
670   r->value_cnt = hdr.case_size;
671
672   /* If value count is rediculous, then force it to -1 (a sentinel value) */
673   if ( r->value_cnt < 0 || 
674        r->value_cnt > (INT_MAX / (int) sizeof (union value) / 2))
675     r->value_cnt = -1;
676
677   r->compressed = hdr.compress;
678
679   r->weight_idx = hdr.weight_idx - 1;
680
681   r->case_cnt = hdr.case_cnt;
682   if (r->case_cnt < -1 || r->case_cnt > INT_MAX / 2)
683     lose ((ME,
684            _("%s: Number of cases in file (%ld) is not between -1 and %d."),
685            fh_get_file_name (r->fh), (long) r->case_cnt, INT_MAX / 2));
686
687   r->bias = hdr.bias;
688   if (r->bias != 100.0)
689     corrupt_msg (MW, _("%s: Compression bias (%g) is not the usual "
690                        "value of 100."),
691                  fh_get_file_name (r->fh), r->bias);
692
693   /* Make a file label only on the condition that the given label is
694      not all spaces or nulls. */
695   {
696     int i;
697
698     for (i = sizeof hdr.file_label - 1; i >= 0; i--)
699       {
700         if (!c_isspace ((unsigned char) hdr.file_label[i])
701             && hdr.file_label[i] != 0)
702           {
703             char *label = xmalloc (i + 2);
704             memcpy (label, hdr.file_label, i + 1);
705             label[i + 1] = 0;
706             dict_set_label (dict, label);
707             free (label);
708             break;
709           }
710       }
711   }
712
713   if (info)
714     {
715       char *cp;
716
717       memcpy (info->creation_date, hdr.creation_date, 9);
718       info->creation_date[9] = 0;
719
720       memcpy (info->creation_time, hdr.creation_time, 8);
721       info->creation_time[8] = 0;
722
723 #ifdef WORDS_BIGENDIAN
724       info->big_endian = !r->reverse_endian;
725 #else
726       info->big_endian = r->reverse_endian;
727 #endif
728
729       info->compressed = hdr.compress;
730
731       info->case_cnt = hdr.case_cnt;
732
733       for (cp = &prod_name[skip_amt]; cp < &prod_name[60]; cp++)
734         if (c_isgraph ((unsigned char) *cp))
735           break;
736       strcpy (info->product, cp);
737     }
738
739   return 1;
740
741 error:
742   return 0;
743 }
744
745 /* Reads most of the dictionary from file H; also fills in the
746    associated VAR_BY_IDX array. */
747 static int
748 read_variables (struct sfm_reader *r,
749                 struct dictionary *dict, struct variable ***var_by_idx)
750 {
751   int i;
752
753   struct sysfile_variable sv;           /* Disk buffer. */
754   int long_string_count = 0;    /* # of long string continuation
755                                    records still expected. */
756   int next_value = 0;           /* Index to next `value' structure. */
757
758   assert(r);
759
760   *var_by_idx = 0;
761
762   /* Pre-allocate variables. */
763   if (r->value_cnt != -1) 
764     {
765       *var_by_idx = xnmalloc (r->value_cnt, sizeof **var_by_idx);
766       r->vars = xnmalloc (r->value_cnt, sizeof *r->vars);
767     }
768
769
770   /* Read in the entry for each variable and use the info to
771      initialize the dictionary. */
772   for (i = 0; ; ++i)
773     {
774       struct variable *vv;
775       char name[SHORT_NAME_LEN + 1];
776       int nv;
777       int j;
778
779       if ( r->value_cnt != -1  && i >= r->value_cnt ) 
780         break;
781
782       assertive_buf_read (r, &sv, sizeof sv, 0);
783
784       if (r->reverse_endian)
785         {
786           bswap_int32 (&sv.rec_type);
787           bswap_int32 (&sv.type);
788           bswap_int32 (&sv.has_var_label);
789           bswap_int32 (&sv.n_missing_values);
790           bswap_int32 (&sv.print);
791           bswap_int32 (&sv.write);
792         }
793
794       /* We've come to the end of the variable entries */
795       if (sv.rec_type != 2)
796         {
797           buf_unread(r, sizeof sv);
798           r->value_cnt = i;
799           break;
800         }
801
802       if ( -1 == r->value_cnt ) 
803         {
804           *var_by_idx = xnrealloc (*var_by_idx, i + 1, sizeof **var_by_idx);
805           r->vars = xnrealloc (r->vars, i + 1, sizeof *r->vars);
806         }
807
808       /* If there was a long string previously, make sure that the
809          continuations are present; otherwise make sure there aren't
810          any. */
811       if (long_string_count)
812         {
813           if (sv.type != -1)
814             lose ((ME, _("%s: position %d: String variable does not have "
815                          "proper number of continuation records."),
816                    fh_get_file_name (r->fh), i));
817
818
819           r->vars[i].width = -1;
820           (*var_by_idx)[i] = NULL;
821           long_string_count--;
822           continue;
823         }
824       else if (sv.type == -1)
825         lose ((ME, _("%s: position %d: Superfluous long string continuation "
826                      "record."),
827                fh_get_file_name (r->fh), i));
828
829       /* Check fields for validity. */
830       if (sv.type < 0 || sv.type > 255)
831         lose ((ME, _("%s: position %d: Bad variable type code %d."),
832                fh_get_file_name (r->fh), i, sv.type));
833       if (sv.has_var_label != 0 && sv.has_var_label != 1)
834         lose ((ME, _("%s: position %d: Variable label indicator field is not "
835                "0 or 1."), fh_get_file_name (r->fh), i));
836       if (sv.n_missing_values < -3 || sv.n_missing_values > 3
837           || sv.n_missing_values == -1)
838         lose ((ME, _("%s: position %d: Missing value indicator field is not "
839                      "-3, -2, 0, 1, 2, or 3."), fh_get_file_name (r->fh), i));
840
841       /* Copy first character of variable name. */
842       if (sv.name[0] == '@' || sv.name[0] == '#')
843         lose ((ME, _("%s: position %d: Variable name begins with invalid "
844                      "character."),
845                fh_get_file_name (r->fh), i));
846
847       name[0] = sv.name[0];
848
849       /* Copy remaining characters of variable name. */
850       for (j = 1; j < SHORT_NAME_LEN; j++)
851         {
852           int c = (unsigned char) sv.name[j];
853
854           if (c == ' ') 
855             break;
856           else 
857             name[j] = c;
858         }
859       name[j] = 0;
860
861       if ( ! var_is_plausible_name(name, false) ) 
862         lose ((ME, _("%s: Invalid variable name `%s' within system file."),
863                fh_get_file_name (r->fh), name));
864
865       /* Create variable. */
866       vv = (*var_by_idx)[i] = dict_create_var (dict, name, sv.type);
867       if (vv == NULL) 
868         lose ((ME, _("%s: Duplicate variable name `%s' within system file."),
869                fh_get_file_name (r->fh), name));
870
871       var_set_short_name (vv, vv->name);
872
873       /* Case reading data. */
874       nv = sv.type == 0 ? 1 : DIV_RND_UP (sv.type, sizeof (flt64));
875       long_string_count = nv - 1;
876       next_value += nv;
877
878       /* Get variable label, if any. */
879       if (sv.has_var_label == 1)
880         {
881           /* Disk buffer. */
882           int32_t len;
883
884           /* Read length of label. */
885           assertive_buf_read (r, &len, sizeof len, 0);
886           if (r->reverse_endian)
887             bswap_int32 (&len);
888
889           /* Check len. */
890           if (len < 0 || len > 255)
891             lose ((ME, _("%s: Variable %s indicates variable label of invalid "
892                          "length %d."),
893                    fh_get_file_name (r->fh), vv->name, len));
894
895           if ( len != 0 ) 
896             {
897               /* Read label into variable structure. */
898               vv->label = buf_read (r, NULL, ROUND_UP (len, sizeof (int32_t)), len + 1);
899               if (vv->label == NULL)
900                 goto error;
901               vv->label[len] = '\0';
902             }
903         }
904
905       /* Set missing values. */
906       if (sv.n_missing_values != 0)
907         {
908           flt64 mv[3];
909           int mv_cnt = abs (sv.n_missing_values);
910
911           if (vv->width > MAX_SHORT_STRING)
912             lose ((ME, _("%s: Long string variable %s may not have missing "
913                          "values."),
914                    fh_get_file_name (r->fh), vv->name));
915
916           assertive_buf_read (r, mv, sizeof *mv * mv_cnt, 0);
917
918           if (r->reverse_endian && vv->type == NUMERIC)
919             for (j = 0; j < mv_cnt; j++)
920               bswap_flt64 (&mv[j]);
921
922           if (sv.n_missing_values > 0)
923             {
924               for (j = 0; j < sv.n_missing_values; j++)
925                 if (vv->type == NUMERIC)
926                   mv_add_num (&vv->miss, mv[j]);
927                 else
928                   mv_add_str (&vv->miss, (char *) &mv[j]);
929             }
930           else
931             {
932               if (vv->type == ALPHA)
933                 lose ((ME, _("%s: String variable %s may not have missing "
934                              "values specified as a range."),
935                        fh_get_file_name (r->fh), vv->name));
936
937               if (mv[0] == r->lowest)
938                 mv_add_num_range (&vv->miss, LOWEST, mv[1]);
939               else if (mv[1] == r->highest)
940                 mv_add_num_range (&vv->miss, mv[0], HIGHEST);
941               else
942                 mv_add_num_range (&vv->miss, mv[0], mv[1]);
943
944               if (sv.n_missing_values == -3)
945                 mv_add_num (&vv->miss, mv[2]);
946             }
947         }
948
949       if (!parse_format_spec (r, sv.print, &vv->print, vv)
950           || !parse_format_spec (r, sv.write, &vv->write, vv))
951         goto error;
952
953       r->vars[i].width = vv->width;
954       r->vars[i].fv = vv->fv;
955
956     }
957
958   /* Some consistency checks. */
959   if (long_string_count != 0)
960     lose ((ME, _("%s: Long string continuation records omitted at end of "
961                  "dictionary."),
962            fh_get_file_name (r->fh)));
963
964   if (next_value != r->value_cnt)
965     corrupt_msg(MW, _("%s: System file header indicates %d variable positions but "
966                  "%d were read from file."),
967            fh_get_file_name (r->fh), r->value_cnt, next_value);
968
969
970   return 1;
971
972 error:
973   return 0;
974 }
975
976 /* Translates the format spec from sysfile format to internal
977    format. */
978 static int
979 parse_format_spec (struct sfm_reader *r, int32_t s,
980                    struct fmt_spec *f, const struct variable *v)
981 {
982   f->type = translate_fmt ((s >> 16) & 0xff);
983   if (f->type == -1)
984     lose ((ME, _("%s: Bad format specifier byte (%d)."),
985            fh_get_file_name (r->fh), (s >> 16) & 0xff));
986   f->w = (s >> 8) & 0xff;
987   f->d = s & 0xff;
988
989   if ((v->type == ALPHA) ^ ((formats[f->type].cat & FCAT_STRING) != 0))
990     lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
991            fh_get_file_name (r->fh),
992            v->type == ALPHA ? _("String") : _("Numeric"),
993            v->name,
994            formats[f->type].cat & FCAT_STRING ? _("string") : _("numeric"),
995            formats[f->type].name));
996
997   if (!check_output_specifier (f, false)
998       || !check_specifier_width (f, v->width, false)) 
999     {
1000       msg (ME, _("%s variable %s has invalid format specifier %s."),
1001            v->type == NUMERIC ? _("Numeric") : _("String"),
1002            v->name, fmt_to_string (f));
1003       *f = v->type == NUMERIC ? f8_2 : make_output_format (FMT_A, v->width, 0);
1004     }
1005   return 1;
1006
1007 error:
1008   return 0;
1009 }
1010
1011 /* Reads value labels from sysfile H and inserts them into the
1012    associated dictionary. */
1013 int
1014 read_value_labels (struct sfm_reader *r,
1015                    struct dictionary *dict, struct variable **var_by_idx)
1016 {
1017   struct label 
1018     {
1019       char raw_value[8];        /* Value as uninterpreted bytes. */
1020       union value value;        /* Value. */
1021       char *label;              /* Null-terminated label string. */
1022     };
1023
1024   struct label *labels = NULL;
1025   int32_t n_labels;             /* Number of labels. */
1026
1027   struct variable **var = NULL; /* Associated variables. */
1028   int32_t n_vars;                       /* Number of associated variables. */
1029
1030   int i;
1031
1032   /* First step: read the contents of the type 3 record and record its
1033      contents.  Note that we can't do much with the data since we
1034      don't know yet whether it is of numeric or string type. */
1035
1036   /* Read number of labels. */
1037   assertive_buf_read (r, &n_labels, sizeof n_labels, 0);
1038   if (r->reverse_endian)
1039     bswap_int32 (&n_labels);
1040
1041   if ( n_labels >= ((int32_t) ~0) / sizeof *labels)
1042     {    
1043       corrupt_msg(MW, _("%s: Invalid number of labels: %d.  Ignoring labels."),
1044                   fh_get_file_name (r->fh), n_labels);
1045       n_labels = 0;
1046     }
1047
1048   /* Allocate memory. */
1049   labels = xcalloc (n_labels, sizeof *labels);
1050   for (i = 0; i < n_labels; i++)
1051     labels[i].label = NULL;
1052
1053   /* Read each value/label tuple into labels[]. */
1054   for (i = 0; i < n_labels; i++)
1055     {
1056       struct label *label = labels + i;
1057       unsigned char label_len;
1058       size_t padded_len;
1059
1060       /* Read value. */
1061       assertive_buf_read (r, label->raw_value, sizeof label->raw_value, 0);
1062
1063       /* Read label length. */
1064       assertive_buf_read (r, &label_len, sizeof label_len, 0);
1065       padded_len = ROUND_UP (label_len + 1, sizeof (flt64));
1066
1067       /* Read label, padding. */
1068       label->label = xmalloc (padded_len + 1);
1069       assertive_buf_read (r, label->label, padded_len - 1, 0);
1070       label->label[label_len] = 0;
1071     }
1072
1073   /* Second step: Read the type 4 record that has the list of
1074      variables to which the value labels are to be applied. */
1075
1076   /* Read record type of type 4 record. */
1077   {
1078     int32_t rec_type;
1079     
1080     assertive_buf_read (r, &rec_type, sizeof rec_type, 0);
1081     if (r->reverse_endian)
1082       bswap_int32 (&rec_type);
1083     
1084     if (rec_type != 4)
1085       lose ((ME, _("%s: Variable index record (type 4) does not immediately "
1086                    "follow value label record (type 3) as it should."),
1087              fh_get_file_name (r->fh)));
1088   }
1089
1090   /* Read number of variables associated with value label from type 4
1091      record. */
1092   assertive_buf_read (r, &n_vars, sizeof n_vars, 0);
1093   if (r->reverse_endian)
1094     bswap_int32 (&n_vars);
1095   if (n_vars < 1 || n_vars > dict_get_var_cnt (dict))
1096     lose ((ME, _("%s: Number of variables associated with a value label (%d) "
1097                  "is not between 1 and the number of variables (%d)."),
1098            fh_get_file_name (r->fh), n_vars, dict_get_var_cnt (dict)));
1099
1100   /* Read the list of variables. */
1101   var = xnmalloc (n_vars, sizeof *var);
1102   for (i = 0; i < n_vars; i++)
1103     {
1104       int32_t var_idx;
1105       struct variable *v;
1106
1107       /* Read variable index, check range. */
1108       assertive_buf_read (r, &var_idx, sizeof var_idx, 0);
1109       if (r->reverse_endian)
1110         bswap_int32 (&var_idx);
1111       if (var_idx < 1 || var_idx > r->value_cnt)
1112         lose ((ME, _("%s: Variable index associated with value label (%d) is "
1113                      "not between 1 and the number of values (%d)."),
1114                fh_get_file_name (r->fh), var_idx, r->value_cnt));
1115
1116       /* Make sure it's a real variable. */
1117       v = var_by_idx[var_idx - 1];
1118       if (v == NULL)
1119         lose ((ME, _("%s: Variable index associated with value label (%d) "
1120                      "refers to a continuation of a string variable, not to "
1121                      "an actual variable."),
1122                fh_get_file_name (r->fh), var_idx));
1123       if (v->type == ALPHA && v->width > MAX_SHORT_STRING)
1124         lose ((ME, _("%s: Value labels are not allowed on long string "
1125                      "variables (%s)."),
1126                fh_get_file_name (r->fh), v->name));
1127
1128       /* Add it to the list of variables. */
1129       var[i] = v;
1130     }
1131
1132   /* Type check the variables. */
1133   for (i = 1; i < n_vars; i++)
1134     if (var[i]->type != var[0]->type)
1135       lose ((ME, _("%s: Variables associated with value label are not all of "
1136                    "identical type.  Variable %s has %s type, but variable "
1137                    "%s has %s type."),
1138              fh_get_file_name (r->fh),
1139              var[0]->name, var[0]->type == ALPHA ? _("string") : _("numeric"),
1140              var[i]->name, var[i]->type == ALPHA ? _("string") : _("numeric")));
1141
1142   /* Fill in labels[].value, now that we know the desired type. */
1143   for (i = 0; i < n_labels; i++) 
1144     {
1145       struct label *label = labels + i;
1146       
1147       if (var[0]->type == ALPHA)
1148         {
1149           const int copy_len = min (sizeof label->raw_value,
1150                                     sizeof label->label);
1151           memcpy (label->value.s, label->raw_value, copy_len);
1152         } else {
1153           flt64 f;
1154           assert (sizeof f == sizeof label->raw_value);
1155           memcpy (&f, label->raw_value, sizeof f);
1156           if (r->reverse_endian)
1157             bswap_flt64 (&f);
1158           label->value.f = f;
1159         }
1160     }
1161   
1162   /* Assign the value_label's to each variable. */
1163   for (i = 0; i < n_vars; i++)
1164     {
1165       struct variable *v = var[i];
1166       int j;
1167
1168       /* Add each label to the variable. */
1169       for (j = 0; j < n_labels; j++)
1170         {
1171           struct label *label = labels + j;
1172           if (!val_labs_replace (v->val_labs, label->value, label->label))
1173             continue;
1174
1175           if (var[0]->type == NUMERIC)
1176             msg (MW, _("%s: File contains duplicate label for value %g for "
1177                        "variable %s."),
1178                  fh_get_file_name (r->fh), label->value.f, v->name);
1179           else
1180             msg (MW, _("%s: File contains duplicate label for value `%.*s' "
1181                        "for variable %s."),
1182                  fh_get_file_name (r->fh), v->width, label->value.s, v->name);
1183         }
1184     }
1185
1186   for (i = 0; i < n_labels; i++)
1187     free (labels[i].label);
1188   free (labels);
1189   free (var);
1190   return 1;
1191
1192 error:
1193   if (labels) 
1194     {
1195       for (i = 0; i < n_labels; i++)
1196         free (labels[i].label);
1197       free (labels); 
1198     }
1199   free (var);
1200   return 0;
1201 }
1202
1203 /* Reads BYTE_CNT bytes from the file represented by H.  If BUF is
1204    non-NULL, uses that as the buffer; otherwise allocates at least
1205    MIN_ALLOC bytes.  Returns a pointer to the buffer on success, NULL
1206    on failure. */
1207 static void *
1208 buf_read (struct sfm_reader *r, void *buf, size_t byte_cnt, size_t min_alloc)
1209 {
1210   assert (r);
1211
1212   if (buf == NULL && byte_cnt > 0 )
1213     buf = xmalloc (max (byte_cnt, min_alloc));
1214
1215   if ( byte_cnt == 0 )
1216     return buf;
1217
1218   
1219   if (1 != fread (buf, byte_cnt, 1, r->file))
1220     {
1221       if (ferror (r->file))
1222         msg (ME, _("%s: Reading system file: %s."),
1223              fh_get_file_name (r->fh), strerror (errno));
1224       else
1225         corrupt_msg (ME, _("%s: Unexpected end of file."),
1226                      fh_get_file_name (r->fh));
1227       r->ok = false;
1228       return NULL;
1229     }
1230   return buf;
1231 }
1232
1233 /* Winds the reader BYTE_CNT bytes back in the reader stream.   */
1234 void
1235 buf_unread(struct sfm_reader *r, size_t byte_cnt)
1236 {
1237   assert(byte_cnt > 0);
1238
1239   if ( 0 != fseek(r->file, -byte_cnt, SEEK_CUR))
1240     {
1241       msg (ME, _("%s: Seeking system file: %s."),
1242            fh_get_file_name (r->fh), strerror (errno));
1243     }
1244 }
1245
1246 /* Reads a document record, type 6, from system file R, and sets up
1247    the documents and n_documents fields in the associated
1248    dictionary. */
1249 static int
1250 read_documents (struct sfm_reader *r, struct dictionary *dict)
1251 {
1252   int32_t line_cnt;
1253   char *documents;
1254
1255   if (dict_get_documents (dict) != NULL)
1256     lose ((ME, _("%s: System file contains multiple "
1257                  "type 6 (document) records."),
1258            fh_get_file_name (r->fh)));
1259
1260   assertive_buf_read (r, &line_cnt, sizeof line_cnt, 0);
1261   if (line_cnt <= 0)
1262     lose ((ME, _("%s: Number of document lines (%ld) "
1263                  "must be greater than 0."),
1264            fh_get_file_name (r->fh), (long) line_cnt));
1265
1266   documents = buf_read (r, NULL, 80 * line_cnt, line_cnt * 80 + 1);
1267   /* FIXME?  Run through asciify. */
1268   if (documents == NULL)
1269     return 0;
1270   documents[80 * line_cnt] = '\0';
1271   dict_set_documents (dict, documents);
1272   free (documents);
1273   return 1;
1274
1275 error:
1276   return 0;
1277 }
1278 \f
1279 /* Data reader. */
1280
1281 /* Reads compressed data into H->BUF and sets other pointers
1282    appropriately.  Returns nonzero only if both no errors occur and
1283    data was read. */
1284 static int
1285 buffer_input (struct sfm_reader *r)
1286 {
1287   size_t amt;
1288
1289   if (!r->ok)
1290     return false;
1291   if (r->buf == NULL)
1292     r->buf = xnmalloc (128, sizeof *r->buf);
1293   amt = fread (r->buf, sizeof *r->buf, 128, r->file);
1294   if (ferror (r->file))
1295     {
1296       msg (ME, _("%s: Error reading file: %s."),
1297            fh_get_file_name (r->fh), strerror (errno));
1298       r->ok = false;
1299       return 0;
1300     }
1301   r->ptr = r->buf;
1302   r->end = &r->buf[amt];
1303   return amt;
1304 }
1305
1306 /* Reads a single case consisting of compressed data from system
1307    file H into the array BUF[] according to reader R, and
1308    returns nonzero only if successful. */
1309 /* Data in system files is compressed in this manner.  Data
1310    values are grouped into sets of eight ("octets").  Each value
1311    in an octet has one instruction byte that are output together.
1312    Each instruction byte gives a value for that byte or indicates
1313    that the value can be found following the instructions. */
1314 static int
1315 read_compressed_data (struct sfm_reader *r, flt64 *buf)
1316 {
1317   const unsigned char *p_end = r->x + sizeof (flt64);
1318   unsigned char *p = r->y;
1319
1320   const flt64 *buf_beg = buf;
1321   const flt64 *buf_end = &buf[r->value_cnt];
1322
1323   for (;;)
1324     {
1325       for (; p < p_end; p++){
1326         switch (*p)
1327           {
1328           case 0:
1329             /* Code 0 is ignored. */
1330             continue;
1331           case 252:
1332             /* Code 252 is end of file. */
1333             if (buf_beg == buf)
1334               return 0;
1335             lose ((ME, _("%s: Compressed data is corrupted.  Data ends "
1336                          "in partial case."),
1337                    fh_get_file_name (r->fh)));
1338           case 253:
1339             /* Code 253 indicates that the value is stored explicitly
1340                following the instruction bytes. */
1341             if (r->ptr == NULL || r->ptr >= r->end)
1342               if (!buffer_input (r))
1343                 lose ((ME, _("%s: Unexpected end of file."),
1344                        fh_get_file_name (r->fh)));
1345             memcpy (buf++, r->ptr++, sizeof *buf);
1346             if (buf >= buf_end)
1347               goto success;
1348             break;
1349           case 254:
1350             /* Code 254 indicates a string that is all blanks. */
1351             memset (buf++, ' ', sizeof *buf);
1352             if (buf >= buf_end)
1353               goto success;
1354             break;
1355           case 255:
1356             /* Code 255 indicates the system-missing value. */
1357             *buf = r->sysmis;
1358             if (r->reverse_endian)
1359               bswap_flt64 (buf);
1360             buf++;
1361             if (buf >= buf_end)
1362               goto success;
1363             break;
1364           default:
1365             /* Codes 1 through 251 inclusive are taken to indicate a
1366                value of (BYTE - BIAS), where BYTE is the byte's value
1367                and BIAS is the compression bias (generally 100.0). */
1368             *buf = *p - r->bias;
1369             if (r->reverse_endian)
1370               bswap_flt64 (buf);
1371             buf++;
1372             if (buf >= buf_end)
1373               goto success;
1374             break;
1375           }
1376       }
1377       /* We have reached the end of this instruction octet.  Read
1378          another. */
1379       if (r->ptr == NULL || r->ptr >= r->end) 
1380         {
1381           if (!buffer_input (r))
1382             {
1383               if (buf_beg != buf)
1384                 lose ((ME, _("%s: Unexpected end of file."),
1385                        fh_get_file_name (r->fh))); 
1386               else
1387                 return 0;
1388             }
1389         }
1390       memcpy (r->x, r->ptr++, sizeof *buf);
1391       p = r->x;
1392     }
1393
1394   abort ();
1395
1396 success:
1397   /* We have filled up an entire record.  Update state and return
1398      successfully. */
1399   r->y = ++p;
1400   return 1;
1401
1402 error:
1403   /* I/O error. */
1404   r->ok = false;
1405   return 0;
1406 }
1407
1408 /* Reads one case from READER's file into C.  Returns nonzero
1409    only if successful. */
1410 int
1411 sfm_read_case (struct sfm_reader *r, struct ccase *c)
1412 {
1413   if (!r->ok)
1414     return 0;
1415   
1416   if (!r->compressed && sizeof (flt64) == sizeof (double)) 
1417     {
1418       /* Fast path: external and internal representations are the
1419          same, except possibly for endianness or SYSMIS.  Read
1420          directly into the case's buffer, then fix up any minor
1421          details as needed. */
1422       if (!fread_ok (r, case_data_all_rw (c),
1423                      sizeof (union value) * r->value_cnt))
1424         return 0;
1425
1426       /* Fix up endianness if needed. */
1427       if (r->reverse_endian) 
1428         {
1429           int i;
1430           
1431           for (i = 0; i < r->value_cnt; i++) 
1432             if (r->vars[i].width == 0)
1433               bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
1434         }
1435
1436       /* Fix up SYSMIS values if needed.
1437          I don't think this will ever actually kick in, but it
1438          can't hurt. */
1439       if (r->sysmis != SYSMIS) 
1440         {
1441           int i;
1442           
1443           for (i = 0; i < r->value_cnt; i++) 
1444             if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
1445               case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
1446         }
1447     }
1448   else 
1449     {
1450       /* Slow path: internal and external representations differ.
1451          Read into a bounce buffer, then copy to C. */
1452       flt64 *bounce;
1453       flt64 *bounce_cur;
1454       size_t bounce_size;
1455       int read_ok;
1456       int i;
1457
1458       bounce_size = sizeof *bounce * r->value_cnt;
1459       bounce = bounce_cur = local_alloc (bounce_size);
1460
1461       if (!r->compressed)
1462         read_ok = fread_ok (r, bounce, bounce_size);
1463       else
1464         read_ok = read_compressed_data (r, bounce);
1465       if (!read_ok) 
1466         {
1467           local_free (bounce);
1468           return 0;
1469         }
1470
1471       for (i = 0; i < r->value_cnt; i++)
1472         {
1473           struct sfm_var *v = &r->vars[i];
1474
1475           if (v->width == 0)
1476             {
1477               flt64 f = *bounce_cur++;
1478               if (r->reverse_endian)
1479                 bswap_flt64 (&f);
1480               case_data_rw (c, v->fv)->f = f == r->sysmis ? SYSMIS : f;
1481             }
1482           else if (v->width != -1)
1483             {
1484               memcpy (case_data_rw (c, v->fv)->s, bounce_cur, v->width);
1485               bounce_cur += DIV_RND_UP (v->width, sizeof (flt64));
1486             }
1487         }
1488
1489       local_free (bounce);
1490     }
1491   return 1; 
1492 }
1493
1494 static int
1495 fread_ok (struct sfm_reader *r, void *buffer, size_t byte_cnt)
1496 {
1497   size_t read_bytes = fread (buffer, 1, byte_cnt, r->file);
1498
1499   if (read_bytes == byte_cnt)
1500     return 1;
1501   else
1502     {
1503       if (ferror (r->file)) 
1504         {
1505           msg (ME, _("%s: Reading system file: %s."),
1506                fh_get_file_name (r->fh), strerror (errno));
1507           r->ok = false; 
1508         }
1509       else if (read_bytes != 0) 
1510         {
1511           msg (ME, _("%s: Partial record at end of system file."),
1512                fh_get_file_name (r->fh));
1513           r->ok = false; 
1514         }
1515       return 0;
1516     }
1517 }
1518 \f
1519 /* Returns true if an I/O error has occurred on READER, false
1520    otherwise. */
1521 bool
1522 sfm_read_error (const struct sfm_reader *reader) 
1523 {
1524   return !reader->ok;
1525 }
1526
1527 /* Returns true if FILE is an SPSS system file,
1528    false otherwise. */
1529 bool
1530 sfm_detect (FILE *file) 
1531 {
1532   struct sysfile_header hdr;
1533
1534   if (fread (&hdr, sizeof hdr, 1, file) != 1)
1535     return false;
1536   if (strncmp ("$FL2", hdr.rec_type, 4))
1537     return false;
1538   return true; 
1539 }
1540