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