Added new files resulting from directory restructuring.
[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 #include "sys-file-reader.h"
22 #include "sfm-private.h"
23 #include "message.h"
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <float.h>
28 #include <setjmp.h>
29 #include "alloc.h"
30 #include "case.h"
31 #include "dictionary.h"
32 #include "message.h"
33 #include "file-handle-def.h"
34 #include "filename.h"
35 #include "format.h"
36 #include "magic.h"
37 #include "misc.h"
38 #include "value-labels.h"
39 #include "str.h"
40 #include "variable.h"
41
42 #include "gettext.h"
43 #define _(msgid) gettext (msgid)
44
45 #include "debug-print.h"
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 *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.filename = 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_filename (r->fh), r->file) == EOF)
149         msg (ME, _("%s: Closing system file: %s."),
150              fh_get_filename (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,
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_filename (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_filename (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_filename (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_filename (fh)));
268       else if (weight_var->type == ALPHA)
269         lose ((ME, _("%s: Weighting variable may not be a string variable."),
270                fh_get_filename (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 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_filename (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 subtype P;
309                 int32 size P;
310                 int32 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_filename (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_filename (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 measure P;
363                         int32 width P;
364                         int32 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_filename (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_filename (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_filename (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_filename (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_filename (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 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_filename (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 data[8];
511   int file_bigendian;
512
513   int i;
514
515   if (size != sizeof (int32) || 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_filename (r->fh), size, count, sizeof (int32)));
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_filename (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_filename (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_filename (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_filename (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_filename (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_filename (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 (!isprint ((unsigned char) prod_name[i]))
622       prod_name[i] = ' ';
623   for (i = 59; i >= 0; i--)
624     if (!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_filename (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_filename (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_filename (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       if (!isspace ((unsigned char) hdr.file_label[i])
700           && hdr.file_label[i] != 0)
701         {
702           char *label = xmalloc (i + 2);
703           memcpy (label, hdr.file_label, i + 1);
704           label[i + 1] = 0;
705           dict_set_label (dict, label);
706           free (label);
707           break;
708         }
709   }
710
711   if (info)
712     {
713       char *cp;
714
715       memcpy (info->creation_date, hdr.creation_date, 9);
716       info->creation_date[9] = 0;
717
718       memcpy (info->creation_time, hdr.creation_time, 8);
719       info->creation_time[8] = 0;
720
721 #ifdef WORDS_BIGENDIAN
722       info->big_endian = !r->reverse_endian;
723 #else
724       info->big_endian = r->reverse_endian;
725 #endif
726
727       info->compressed = hdr.compress;
728
729       info->case_cnt = hdr.case_cnt;
730
731       for (cp = &prod_name[skip_amt]; cp < &prod_name[60]; cp++)
732         if (isgraph ((unsigned char) *cp))
733           break;
734       strcpy (info->product, cp);
735     }
736
737   return 1;
738
739 error:
740   return 0;
741 }
742
743 /* Reads most of the dictionary from file H; also fills in the
744    associated VAR_BY_IDX array. */
745 static int
746 read_variables (struct sfm_reader *r,
747                 struct dictionary *dict, struct variable ***var_by_idx)
748 {
749   int i;
750
751   struct sysfile_variable sv;           /* Disk buffer. */
752   int long_string_count = 0;    /* # of long string continuation
753                                    records still expected. */
754   int next_value = 0;           /* Index to next `value' structure. */
755
756   assert(r);
757
758   *var_by_idx = 0;
759
760   /* Pre-allocate variables. */
761   if (r->value_cnt != -1) 
762     {
763       *var_by_idx = xnmalloc (r->value_cnt, sizeof **var_by_idx);
764       r->vars = xnmalloc (r->value_cnt, sizeof *r->vars);
765     }
766
767
768   /* Read in the entry for each variable and use the info to
769      initialize the dictionary. */
770   for (i = 0; ; ++i)
771     {
772       struct variable *vv;
773       char name[SHORT_NAME_LEN + 1];
774       int nv;
775       int j;
776
777       if ( r->value_cnt != -1  && i >= r->value_cnt ) 
778         break;
779
780       assertive_buf_read (r, &sv, sizeof sv, 0);
781
782       if (r->reverse_endian)
783         {
784           bswap_int32 (&sv.rec_type);
785           bswap_int32 (&sv.type);
786           bswap_int32 (&sv.has_var_label);
787           bswap_int32 (&sv.n_missing_values);
788           bswap_int32 (&sv.print);
789           bswap_int32 (&sv.write);
790         }
791
792       /* We've come to the end of the variable entries */
793       if (sv.rec_type != 2)
794         {
795           buf_unread(r, sizeof sv);
796           r->value_cnt = i;
797           break;
798         }
799
800       if ( -1 == r->value_cnt ) 
801         {
802           *var_by_idx = xnrealloc (*var_by_idx, i + 1, sizeof **var_by_idx);
803           r->vars = xnrealloc (r->vars, i + 1, sizeof *r->vars);
804         }
805
806       /* If there was a long string previously, make sure that the
807          continuations are present; otherwise make sure there aren't
808          any. */
809       if (long_string_count)
810         {
811           if (sv.type != -1)
812             lose ((ME, _("%s: position %d: String variable does not have "
813                          "proper number of continuation records."),
814                    fh_get_filename (r->fh), i));
815
816
817           r->vars[i].width = -1;
818           (*var_by_idx)[i] = NULL;
819           long_string_count--;
820           continue;
821         }
822       else if (sv.type == -1)
823         lose ((ME, _("%s: position %d: Superfluous long string continuation "
824                      "record."),
825                fh_get_filename (r->fh), i));
826
827       /* Check fields for validity. */
828       if (sv.type < 0 || sv.type > 255)
829         lose ((ME, _("%s: position %d: Bad variable type code %d."),
830                fh_get_filename (r->fh), i, sv.type));
831       if (sv.has_var_label != 0 && sv.has_var_label != 1)
832         lose ((ME, _("%s: position %d: Variable label indicator field is not "
833                "0 or 1."), fh_get_filename (r->fh), i));
834       if (sv.n_missing_values < -3 || sv.n_missing_values > 3
835           || sv.n_missing_values == -1)
836         lose ((ME, _("%s: position %d: Missing value indicator field is not "
837                      "-3, -2, 0, 1, 2, or 3."), fh_get_filename (r->fh), i));
838
839       /* Copy first character of variable name. */
840       if (!isalpha ((unsigned char) sv.name[0])
841           && sv.name[0] != '@' && sv.name[0] != '#')
842         lose ((ME, _("%s: position %d: Variable name begins with invalid "
843                      "character."),
844                fh_get_filename (r->fh), i));
845       if (islower ((unsigned char) sv.name[0]))
846         msg (MW, _("%s: position %d: Variable name begins with lowercase letter "
847                    "%c."),
848              fh_get_filename (r->fh), i, sv.name[0]);
849       if (sv.name[0] == '#')
850         msg (MW, _("%s: position %d: Variable name begins with octothorpe "
851                    "(`#').  Scratch variables should not appear in system "
852                    "files."),
853              fh_get_filename (r->fh), i);
854       name[0] = toupper ((unsigned char) (sv.name[0]));
855
856       /* Copy remaining characters of variable name. */
857       for (j = 1; j < SHORT_NAME_LEN; j++)
858         {
859           int c = (unsigned char) sv.name[j];
860
861           if (isspace (c))
862             break;
863           else if (islower (c))
864             {
865               msg (MW, _("%s: position %d: Variable name character %d is "
866                          "lowercase letter %c."),
867                    fh_get_filename (r->fh), i, j + 1, sv.name[j]);
868               name[j] = toupper ((unsigned char) (c));
869             }
870           else if (isalnum (c) || c == '.' || c == '@'
871                    || c == '#' || c == '$' || c == '_')
872             name[j] = c;
873           else
874             lose ((ME, _("%s: position %d: character `\\%03o' (%c) is not valid in a "
875                          "variable name."),
876                    fh_get_filename (r->fh), i, c, c));
877         }
878       name[j] = 0;
879
880       if ( ! var_is_valid_name(name, false) ) 
881         lose ((ME, _("%s: Invalid variable name `%s' within system file."),
882                fh_get_filename (r->fh), name));
883
884       /* Create variable. */
885
886       vv = (*var_by_idx)[i] = dict_create_var (dict, name, sv.type);
887       if (vv == NULL) 
888         lose ((ME, _("%s: Duplicate variable name `%s' within system file."),
889                fh_get_filename (r->fh), name));
890
891       var_set_short_name (vv, vv->name);
892
893       /* Case reading data. */
894       nv = sv.type == 0 ? 1 : DIV_RND_UP (sv.type, sizeof (flt64));
895       long_string_count = nv - 1;
896       next_value += nv;
897
898       /* Get variable label, if any. */
899       if (sv.has_var_label == 1)
900         {
901           /* Disk buffer. */
902           int32 len;
903
904           /* Read length of label. */
905           assertive_buf_read (r, &len, sizeof len, 0);
906           if (r->reverse_endian)
907             bswap_int32 (&len);
908
909           /* Check len. */
910           if (len < 0 || len > 255)
911             lose ((ME, _("%s: Variable %s indicates variable label of invalid "
912                          "length %d."),
913                    fh_get_filename (r->fh), vv->name, len));
914
915           if ( len != 0 ) 
916             {
917               /* Read label into variable structure. */
918               vv->label = buf_read (r, NULL, ROUND_UP (len, sizeof (int32)), len + 1);
919               if (vv->label == NULL)
920                 goto error;
921               vv->label[len] = '\0';
922             }
923         }
924
925       /* Set missing values. */
926       if (sv.n_missing_values != 0)
927         {
928           flt64 mv[3];
929           int mv_cnt = abs (sv.n_missing_values);
930
931           if (vv->width > MAX_SHORT_STRING)
932             lose ((ME, _("%s: Long string variable %s may not have missing "
933                          "values."),
934                    fh_get_filename (r->fh), vv->name));
935
936           assertive_buf_read (r, mv, sizeof *mv * mv_cnt, 0);
937
938           if (r->reverse_endian && vv->type == NUMERIC)
939             for (j = 0; j < mv_cnt; j++)
940               bswap_flt64 (&mv[j]);
941
942           if (sv.n_missing_values > 0)
943             {
944               for (j = 0; j < sv.n_missing_values; j++)
945                 if (vv->type == NUMERIC)
946                   mv_add_num (&vv->miss, mv[j]);
947                 else
948                   mv_add_str (&vv->miss, (char *) &mv[j]);
949             }
950           else
951             {
952               if (vv->type == ALPHA)
953                 lose ((ME, _("%s: String variable %s may not have missing "
954                              "values specified as a range."),
955                        fh_get_filename (r->fh), vv->name));
956
957               if (mv[0] == r->lowest)
958                 mv_add_num_range (&vv->miss, LOWEST, mv[1]);
959               else if (mv[1] == r->highest)
960                 mv_add_num_range (&vv->miss, mv[0], HIGHEST);
961               else
962                 mv_add_num_range (&vv->miss, mv[0], mv[1]);
963
964               if (sv.n_missing_values == -3)
965                 mv_add_num (&vv->miss, mv[2]);
966             }
967         }
968
969       if (!parse_format_spec (r, sv.print, &vv->print, vv)
970           || !parse_format_spec (r, sv.write, &vv->write, vv))
971         goto error;
972
973       r->vars[i].width = vv->width;
974       r->vars[i].fv = vv->fv;
975
976     }
977
978   /* Some consistency checks. */
979   if (long_string_count != 0)
980     lose ((ME, _("%s: Long string continuation records omitted at end of "
981                  "dictionary."),
982            fh_get_filename (r->fh)));
983
984   if (next_value != r->value_cnt)
985     corrupt_msg(MW, _("%s: System file header indicates %d variable positions but "
986                  "%d were read from file."),
987            fh_get_filename (r->fh), r->value_cnt, next_value);
988
989
990   return 1;
991
992 error:
993   return 0;
994 }
995
996 /* Translates the format spec from sysfile format to internal
997    format. */
998 static int
999 parse_format_spec (struct sfm_reader *r, int32 s,
1000                    struct fmt_spec *f, const struct variable *v)
1001 {
1002   f->type = translate_fmt ((s >> 16) & 0xff);
1003   if (f->type == -1)
1004     lose ((ME, _("%s: Bad format specifier byte (%d)."),
1005            fh_get_filename (r->fh), (s >> 16) & 0xff));
1006   f->w = (s >> 8) & 0xff;
1007   f->d = s & 0xff;
1008
1009   if ((v->type == ALPHA) ^ ((formats[f->type].cat & FCAT_STRING) != 0))
1010     lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
1011            fh_get_filename (r->fh),
1012            v->type == ALPHA ? _("String") : _("Numeric"),
1013            v->name,
1014            formats[f->type].cat & FCAT_STRING ? _("string") : _("numeric"),
1015            formats[f->type].name));
1016
1017   if (!check_output_specifier (f, false)
1018       || !check_specifier_width (f, v->width, false)) 
1019     {
1020       msg (ME, _("%s variable %s has invalid format specifier %s."),
1021            v->type == NUMERIC ? _("Numeric") : _("String"),
1022            v->name, fmt_to_string (f));
1023       *f = v->type == NUMERIC ? f8_2 : make_output_format (FMT_A, v->width, 0);
1024     }
1025   return 1;
1026
1027 error:
1028   return 0;
1029 }
1030
1031 /* Reads value labels from sysfile H and inserts them into the
1032    associated dictionary. */
1033 int
1034 read_value_labels (struct sfm_reader *r,
1035                    struct dictionary *dict, struct variable **var_by_idx)
1036 {
1037   struct label 
1038     {
1039       char raw_value[8];        /* Value as uninterpreted bytes. */
1040       union value value;        /* Value. */
1041       char *label;              /* Null-terminated label string. */
1042     };
1043
1044   struct label *labels = NULL;
1045   int32 n_labels;               /* Number of labels. */
1046
1047   struct variable **var = NULL; /* Associated variables. */
1048   int32 n_vars;                 /* Number of associated variables. */
1049
1050   int i;
1051
1052   /* First step: read the contents of the type 3 record and record its
1053      contents.  Note that we can't do much with the data since we
1054      don't know yet whether it is of numeric or string type. */
1055
1056   /* Read number of labels. */
1057   assertive_buf_read (r, &n_labels, sizeof n_labels, 0);
1058   if (r->reverse_endian)
1059     bswap_int32 (&n_labels);
1060
1061   if ( n_labels >= ((int32) ~0) / sizeof *labels)
1062     {    
1063       corrupt_msg(MW, _("%s: Invalid number of labels: %d.  Ignoring labels."),
1064                   fh_get_filename (r->fh), n_labels);
1065       n_labels = 0;
1066     }
1067
1068   /* Allocate memory. */
1069   labels = xcalloc (n_labels, sizeof *labels);
1070   for (i = 0; i < n_labels; i++)
1071     labels[i].label = NULL;
1072
1073   /* Read each value/label tuple into labels[]. */
1074   for (i = 0; i < n_labels; i++)
1075     {
1076       struct label *label = labels + i;
1077       unsigned char label_len;
1078       size_t padded_len;
1079
1080       /* Read value. */
1081       assertive_buf_read (r, label->raw_value, sizeof label->raw_value, 0);
1082
1083       /* Read label length. */
1084       assertive_buf_read (r, &label_len, sizeof label_len, 0);
1085       padded_len = ROUND_UP (label_len + 1, sizeof (flt64));
1086
1087       /* Read label, padding. */
1088       label->label = xmalloc (padded_len + 1);
1089       assertive_buf_read (r, label->label, padded_len - 1, 0);
1090       label->label[label_len] = 0;
1091     }
1092
1093   /* Second step: Read the type 4 record that has the list of
1094      variables to which the value labels are to be applied. */
1095
1096   /* Read record type of type 4 record. */
1097   {
1098     int32 rec_type;
1099     
1100     assertive_buf_read (r, &rec_type, sizeof rec_type, 0);
1101     if (r->reverse_endian)
1102       bswap_int32 (&rec_type);
1103     
1104     if (rec_type != 4)
1105       lose ((ME, _("%s: Variable index record (type 4) does not immediately "
1106                    "follow value label record (type 3) as it should."),
1107              fh_get_filename (r->fh)));
1108   }
1109
1110   /* Read number of variables associated with value label from type 4
1111      record. */
1112   assertive_buf_read (r, &n_vars, sizeof n_vars, 0);
1113   if (r->reverse_endian)
1114     bswap_int32 (&n_vars);
1115   if (n_vars < 1 || n_vars > dict_get_var_cnt (dict))
1116     lose ((ME, _("%s: Number of variables associated with a value label (%d) "
1117                  "is not between 1 and the number of variables (%d)."),
1118            fh_get_filename (r->fh), n_vars, dict_get_var_cnt (dict)));
1119
1120   /* Read the list of variables. */
1121   var = xnmalloc (n_vars, sizeof *var);
1122   for (i = 0; i < n_vars; i++)
1123     {
1124       int32 var_idx;
1125       struct variable *v;
1126
1127       /* Read variable index, check range. */
1128       assertive_buf_read (r, &var_idx, sizeof var_idx, 0);
1129       if (r->reverse_endian)
1130         bswap_int32 (&var_idx);
1131       if (var_idx < 1 || var_idx > r->value_cnt)
1132         lose ((ME, _("%s: Variable index associated with value label (%d) is "
1133                      "not between 1 and the number of values (%d)."),
1134                fh_get_filename (r->fh), var_idx, r->value_cnt));
1135
1136       /* Make sure it's a real variable. */
1137       v = var_by_idx[var_idx - 1];
1138       if (v == NULL)
1139         lose ((ME, _("%s: Variable index associated with value label (%d) "
1140                      "refers to a continuation of a string variable, not to "
1141                      "an actual variable."),
1142                fh_get_filename (r->fh), var_idx));
1143       if (v->type == ALPHA && v->width > MAX_SHORT_STRING)
1144         lose ((ME, _("%s: Value labels are not allowed on long string "
1145                      "variables (%s)."),
1146                fh_get_filename (r->fh), v->name));
1147
1148       /* Add it to the list of variables. */
1149       var[i] = v;
1150     }
1151
1152   /* Type check the variables. */
1153   for (i = 1; i < n_vars; i++)
1154     if (var[i]->type != var[0]->type)
1155       lose ((ME, _("%s: Variables associated with value label are not all of "
1156                    "identical type.  Variable %s has %s type, but variable "
1157                    "%s has %s type."),
1158              fh_get_filename (r->fh),
1159              var[0]->name, var[0]->type == ALPHA ? _("string") : _("numeric"),
1160              var[i]->name, var[i]->type == ALPHA ? _("string") : _("numeric")));
1161
1162   /* Fill in labels[].value, now that we know the desired type. */
1163   for (i = 0; i < n_labels; i++) 
1164     {
1165       struct label *label = labels + i;
1166       
1167       if (var[0]->type == ALPHA)
1168         {
1169           const int copy_len = min (sizeof label->raw_value,
1170                                     sizeof label->label);
1171           memcpy (label->value.s, label->raw_value, copy_len);
1172         } else {
1173           flt64 f;
1174           assert (sizeof f == sizeof label->raw_value);
1175           memcpy (&f, label->raw_value, sizeof f);
1176           if (r->reverse_endian)
1177             bswap_flt64 (&f);
1178           label->value.f = f;
1179         }
1180     }
1181   
1182   /* Assign the value_label's to each variable. */
1183   for (i = 0; i < n_vars; i++)
1184     {
1185       struct variable *v = var[i];
1186       int j;
1187
1188       /* Add each label to the variable. */
1189       for (j = 0; j < n_labels; j++)
1190         {
1191           struct label *label = labels + j;
1192           if (!val_labs_replace (v->val_labs, label->value, label->label))
1193             continue;
1194
1195           if (var[0]->type == NUMERIC)
1196             msg (MW, _("%s: File contains duplicate label for value %g for "
1197                        "variable %s."),
1198                  fh_get_filename (r->fh), label->value.f, v->name);
1199           else
1200             msg (MW, _("%s: File contains duplicate label for value `%.*s' "
1201                        "for variable %s."),
1202                  fh_get_filename (r->fh), v->width, label->value.s, v->name);
1203         }
1204     }
1205
1206   for (i = 0; i < n_labels; i++)
1207     free (labels[i].label);
1208   free (labels);
1209   free (var);
1210   return 1;
1211
1212 error:
1213   if (labels) 
1214     {
1215       for (i = 0; i < n_labels; i++)
1216         free (labels[i].label);
1217       free (labels); 
1218     }
1219   free (var);
1220   return 0;
1221 }
1222
1223 /* Reads BYTE_CNT bytes from the file represented by H.  If BUF is
1224    non-NULL, uses that as the buffer; otherwise allocates at least
1225    MIN_ALLOC bytes.  Returns a pointer to the buffer on success, NULL
1226    on failure. */
1227 static void *
1228 buf_read (struct sfm_reader *r, void *buf, size_t byte_cnt, size_t min_alloc)
1229 {
1230   assert (r);
1231
1232   if (buf == NULL && byte_cnt > 0 )
1233     buf = xmalloc (max (byte_cnt, min_alloc));
1234
1235   if ( byte_cnt == 0 )
1236     return buf;
1237
1238   
1239   if (1 != fread (buf, byte_cnt, 1, r->file))
1240     {
1241       if (ferror (r->file))
1242         msg (ME, _("%s: Reading system file: %s."),
1243              fh_get_filename (r->fh), strerror (errno));
1244       else
1245         corrupt_msg (ME, _("%s: Unexpected end of file."),
1246                      fh_get_filename (r->fh));
1247       r->ok = false;
1248       return NULL;
1249     }
1250   return buf;
1251 }
1252
1253 /* Winds the reader BYTE_CNT bytes back in the reader stream.   */
1254 void
1255 buf_unread(struct sfm_reader *r, size_t byte_cnt)
1256 {
1257   assert(byte_cnt > 0);
1258
1259   if ( 0 != fseek(r->file, -byte_cnt, SEEK_CUR))
1260     {
1261       msg (ME, _("%s: Seeking system file: %s."),
1262            fh_get_filename (r->fh), strerror (errno));
1263     }
1264 }
1265
1266 /* Reads a document record, type 6, from system file R, and sets up
1267    the documents and n_documents fields in the associated
1268    dictionary. */
1269 static int
1270 read_documents (struct sfm_reader *r, struct dictionary *dict)
1271 {
1272   int32 line_cnt;
1273   char *documents;
1274
1275   if (dict_get_documents (dict) != NULL)
1276     lose ((ME, _("%s: System file contains multiple "
1277                  "type 6 (document) records."),
1278            fh_get_filename (r->fh)));
1279
1280   assertive_buf_read (r, &line_cnt, sizeof line_cnt, 0);
1281   if (line_cnt <= 0)
1282     lose ((ME, _("%s: Number of document lines (%ld) "
1283                  "must be greater than 0."),
1284            fh_get_filename (r->fh), (long) line_cnt));
1285
1286   documents = buf_read (r, NULL, 80 * line_cnt, line_cnt * 80 + 1);
1287   /* FIXME?  Run through asciify. */
1288   if (documents == NULL)
1289     return 0;
1290   documents[80 * line_cnt] = '\0';
1291   dict_set_documents (dict, documents);
1292   free (documents);
1293   return 1;
1294
1295 error:
1296   return 0;
1297 }
1298 \f
1299 /* Data reader. */
1300
1301 /* Reads compressed data into H->BUF and sets other pointers
1302    appropriately.  Returns nonzero only if both no errors occur and
1303    data was read. */
1304 static int
1305 buffer_input (struct sfm_reader *r)
1306 {
1307   size_t amt;
1308
1309   if (!r->ok)
1310     return false;
1311   if (r->buf == NULL)
1312     r->buf = xnmalloc (128, sizeof *r->buf);
1313   amt = fread (r->buf, sizeof *r->buf, 128, r->file);
1314   if (ferror (r->file))
1315     {
1316       msg (ME, _("%s: Error reading file: %s."),
1317            fh_get_filename (r->fh), strerror (errno));
1318       r->ok = false;
1319       return 0;
1320     }
1321   r->ptr = r->buf;
1322   r->end = &r->buf[amt];
1323   return amt;
1324 }
1325
1326 /* Reads a single case consisting of compressed data from system
1327    file H into the array BUF[] according to reader R, and
1328    returns nonzero only if successful. */
1329 /* Data in system files is compressed in this manner.  Data
1330    values are grouped into sets of eight ("octets").  Each value
1331    in an octet has one instruction byte that are output together.
1332    Each instruction byte gives a value for that byte or indicates
1333    that the value can be found following the instructions. */
1334 static int
1335 read_compressed_data (struct sfm_reader *r, flt64 *buf)
1336 {
1337   const unsigned char *p_end = r->x + sizeof (flt64);
1338   unsigned char *p = r->y;
1339
1340   const flt64 *buf_beg = buf;
1341   const flt64 *buf_end = &buf[r->value_cnt];
1342
1343   for (;;)
1344     {
1345       for (; p < p_end; p++){
1346         switch (*p)
1347           {
1348           case 0:
1349             /* Code 0 is ignored. */
1350             continue;
1351           case 252:
1352             /* Code 252 is end of file. */
1353             if (buf_beg == buf)
1354               return 0;
1355             lose ((ME, _("%s: Compressed data is corrupted.  Data ends "
1356                          "in partial case."),
1357                    fh_get_filename (r->fh)));
1358           case 253:
1359             /* Code 253 indicates that the value is stored explicitly
1360                following the instruction bytes. */
1361             if (r->ptr == NULL || r->ptr >= r->end)
1362               if (!buffer_input (r))
1363                 lose ((ME, _("%s: Unexpected end of file."),
1364                        fh_get_filename (r->fh)));
1365             memcpy (buf++, r->ptr++, sizeof *buf);
1366             if (buf >= buf_end)
1367               goto success;
1368             break;
1369           case 254:
1370             /* Code 254 indicates a string that is all blanks. */
1371             memset (buf++, ' ', sizeof *buf);
1372             if (buf >= buf_end)
1373               goto success;
1374             break;
1375           case 255:
1376             /* Code 255 indicates the system-missing value. */
1377             *buf = r->sysmis;
1378             if (r->reverse_endian)
1379               bswap_flt64 (buf);
1380             buf++;
1381             if (buf >= buf_end)
1382               goto success;
1383             break;
1384           default:
1385             /* Codes 1 through 251 inclusive are taken to indicate a
1386                value of (BYTE - BIAS), where BYTE is the byte's value
1387                and BIAS is the compression bias (generally 100.0). */
1388             *buf = *p - r->bias;
1389             if (r->reverse_endian)
1390               bswap_flt64 (buf);
1391             buf++;
1392             if (buf >= buf_end)
1393               goto success;
1394             break;
1395           }
1396       }
1397       /* We have reached the end of this instruction octet.  Read
1398          another. */
1399       if (r->ptr == NULL || r->ptr >= r->end) 
1400         {
1401           if (!buffer_input (r))
1402             if (buf_beg != buf)
1403               lose ((ME, _("%s: Unexpected end of file."),
1404                      fh_get_filename (r->fh))); 
1405         }
1406       memcpy (r->x, r->ptr++, sizeof *buf);
1407       p = r->x;
1408     }
1409
1410   abort ();
1411
1412 success:
1413   /* We have filled up an entire record.  Update state and return
1414      successfully. */
1415   r->y = ++p;
1416   return 1;
1417
1418 error:
1419   /* I/O error. */
1420   r->ok = false;
1421   return 0;
1422 }
1423
1424 /* Reads one case from READER's file into C.  Returns nonzero
1425    only if successful. */
1426 int
1427 sfm_read_case (struct sfm_reader *r, struct ccase *c)
1428 {
1429   if (!r->ok)
1430     return 0;
1431   
1432   if (!r->compressed && sizeof (flt64) == sizeof (double)) 
1433     {
1434       /* Fast path: external and internal representations are the
1435          same, except possibly for endianness or SYSMIS.  Read
1436          directly into the case's buffer, then fix up any minor
1437          details as needed. */
1438       if (!fread_ok (r, case_data_all_rw (c),
1439                      sizeof (union value) * r->value_cnt))
1440         return 0;
1441
1442       /* Fix up endianness if needed. */
1443       if (r->reverse_endian) 
1444         {
1445           int i;
1446           
1447           for (i = 0; i < r->value_cnt; i++) 
1448             if (r->vars[i].width == 0)
1449               bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
1450         }
1451
1452       /* Fix up SYSMIS values if needed.
1453          I don't think this will ever actually kick in, but it
1454          can't hurt. */
1455       if (r->sysmis != SYSMIS) 
1456         {
1457           int i;
1458           
1459           for (i = 0; i < r->value_cnt; i++) 
1460             if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
1461               case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
1462         }
1463     }
1464   else 
1465     {
1466       /* Slow path: internal and external representations differ.
1467          Read into a bounce buffer, then copy to C. */
1468       flt64 *bounce;
1469       flt64 *bounce_cur;
1470       size_t bounce_size;
1471       int read_ok;
1472       int i;
1473
1474       bounce_size = sizeof *bounce * r->value_cnt;
1475       bounce = bounce_cur = local_alloc (bounce_size);
1476
1477       if (!r->compressed)
1478         read_ok = fread_ok (r, bounce, bounce_size);
1479       else
1480         read_ok = read_compressed_data (r, bounce);
1481       if (!read_ok) 
1482         {
1483           local_free (bounce);
1484           return 0;
1485         }
1486
1487       for (i = 0; i < r->value_cnt; i++)
1488         {
1489           struct sfm_var *v = &r->vars[i];
1490
1491           if (v->width == 0)
1492             {
1493               flt64 f = *bounce_cur++;
1494               if (r->reverse_endian)
1495                 bswap_flt64 (&f);
1496               case_data_rw (c, v->fv)->f = f == r->sysmis ? SYSMIS : f;
1497             }
1498           else if (v->width != -1)
1499             {
1500               memcpy (case_data_rw (c, v->fv)->s, bounce_cur, v->width);
1501               bounce_cur += DIV_RND_UP (v->width, sizeof (flt64));
1502             }
1503         }
1504
1505       local_free (bounce);
1506     }
1507   return 1; 
1508 }
1509
1510 static int
1511 fread_ok (struct sfm_reader *r, void *buffer, size_t byte_cnt)
1512 {
1513   size_t read_bytes = fread (buffer, 1, byte_cnt, r->file);
1514
1515   if (read_bytes == byte_cnt)
1516     return 1;
1517   else
1518     {
1519       if (ferror (r->file)) 
1520         {
1521           msg (ME, _("%s: Reading system file: %s."),
1522                fh_get_filename (r->fh), strerror (errno));
1523           r->ok = false; 
1524         }
1525       else if (read_bytes != 0) 
1526         {
1527           msg (ME, _("%s: Partial record at end of system file."),
1528                fh_get_filename (r->fh));
1529           r->ok = false; 
1530         }
1531       return 0;
1532     }
1533 }
1534 \f
1535 /* Returns true if an I/O error has occurred on READER, false
1536    otherwise. */
1537 bool
1538 sfm_read_error (const struct sfm_reader *reader) 
1539 {
1540   return !reader->ok;
1541 }
1542
1543 /* Returns true if FILE is an SPSS system file,
1544    false otherwise. */
1545 bool
1546 sfm_detect (FILE *file) 
1547 {
1548   struct sysfile_header hdr;
1549
1550   if (fread (&hdr, sizeof hdr, 1, file) != 1)
1551     return false;
1552   if (strncmp ("$FL2", hdr.rec_type, 4))
1553     return false;
1554   return true; 
1555 }