986dede94fd2947a62d8a944d1777cf319c688b3
[pspp] / src / sfm-read.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 "sfm-read.h"
22 #include "sfmP.h"
23 #include "error.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 "error.h"
33 #include "file-handle.h"
34 #include "filename.h"
35 #include "format.h"
36 #include "getl.h"
37 #include "hash.h"
38 #include "magic.h"
39 #include "misc.h"
40 #include "value-labels.h"
41 #include "str.h"
42 #include "var.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46
47 #include "debug-print.h"
48
49 /* System file reader. */
50 struct sfm_reader
51   {
52     struct file_handle *fh;     /* File handle. */
53     FILE *file;                 /* File stream. */
54
55     int reverse_endian;         /* 1=file has endianness opposite us. */
56     int fix_specials;           /* 1=SYSMIS/HIGHEST/LOWEST differs from us. */
57     int value_cnt;              /* Number of `union values's per case. */
58     long case_cnt;              /* Number of cases, -1 if unknown. */
59     int compressed;             /* 1=compressed, 0=not compressed. */
60     double bias;                /* Compression bias, usually 100.0. */
61     int weight_idx;             /* 0-based index of weighting variable, or -1. */
62
63     /* Variables. */
64     struct sfm_var *vars;       /* Variables. */
65
66     /* File's special constants. */
67     flt64 sysmis;
68     flt64 highest;
69     flt64 lowest;
70
71     /* Decompression buffer. */
72     flt64 *buf;                 /* Buffer data. */
73     flt64 *ptr;                 /* Current location in buffer. */
74     flt64 *end;                 /* End of buffer data. */
75
76     /* Compression instruction octet. */
77     unsigned char x[8];         /* Current instruction octet. */
78     unsigned char *y;           /* Location in current instruction octet. */
79   };
80
81 /* A variable in a system file. */
82 struct sfm_var 
83   {
84     int width;                  /* 0=numeric, otherwise string width. */
85     int fv;                     /* Index into case. */
86   };
87 \f
88 /* Utilities. */
89
90 /* Swap bytes *A and *B. */
91 static inline void
92 bswap (char *a, char *b) 
93 {
94   char t = *a;
95   *a = *b;
96   *b = t;
97 }
98
99 /* Reverse the byte order of 32-bit integer *X. */
100 static inline void
101 bswap_int32 (int32 *x_)
102 {
103   char *x = (char *) x_;
104   bswap (x + 0, x + 3);
105   bswap (x + 1, x + 2);
106 }
107
108 /* Reverse the byte order of 64-bit floating point *X. */
109 static inline void
110 bswap_flt64 (flt64 *x_)
111 {
112   char *x = (char *) x_;
113   bswap (x + 0, x + 7);
114   bswap (x + 1, x + 6);
115   bswap (x + 2, x + 5);
116   bswap (x + 3, x + 4);
117 }
118
119 static void
120 corrupt_msg (int class, const char *format,...)
121      PRINTF_FORMAT (2, 3);
122
123 /* Displays a corrupt sysfile error. */
124 static void
125 corrupt_msg (int class, const char *format,...)
126 {
127   struct error e;
128   va_list args;
129
130   e.class = class;
131   getl_location (&e.where.filename, &e.where.line_number);
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 *, 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
227   r->vars = NULL;
228
229   r->sysmis = -FLT64_MAX;
230   r->highest = FLT64_MAX;
231   r->lowest = second_lowest_flt64;
232
233   r->buf = r->ptr = r->end = NULL;
234   r->y = r->x + sizeof r->x;
235
236   /* Check that file open succeeded. */
237   if (r->file == NULL)
238     {
239       msg (ME, _("An error occurred while opening \"%s\" for reading "
240                  "as a system file: %s."),
241            fh_get_filename (r->fh), strerror (errno));
242       err_cond_fail ();
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                         {
433                           lose ((ME, _("%s: Duplicate long variable name `%s' "
434                                        "within system file."),
435                                  fh_get_filename (r->fh), long_name));
436                           break;
437                         }
438
439                       /* Set long name.
440                          Renaming a variable may clear the short
441                          name, but we want to retain it, so
442                          re-set it explicitly. */
443                       dict_rename_var (*dict, v, long_name);
444                       var_set_short_name (v, short_name);
445
446                       /* For compatability, make sure dictionary
447                          is in long variable name map order.  In
448                          the common case, this has no effect,
449                          because the dictionary and the long
450                          variable name map are already in the
451                          same order. */
452                       dict_reorder_var (*dict, v, idx);
453                     }
454
455                   /* Free data. */
456                   free (buf);
457                 }
458                 break;
459
460               default:
461                 msg (MW, _("%s: Unrecognized record type 7, subtype %d "
462                            "encountered in system file."),
463                      fh_get_filename (r->fh), data.subtype);
464                 skip = 1;
465               }
466
467             if (skip)
468               {
469                 void *x = buf_read (r, NULL, data.size * data.count, 0);
470                 if (x == NULL)
471                   goto error;
472                 free (x);
473               }
474           }
475           break;
476
477         case 999:
478           {
479             int32 filler;
480
481             assertive_buf_read (r, &filler, sizeof filler, 0);
482             goto success;
483           }
484
485         default:
486           corrupt_msg(MW, _("%s: Unrecognized record type %d."),
487                  fh_get_filename (r->fh), rec_type);
488         }
489     }
490
491 success:
492   /* Come here on successful completion. */
493   free (var_by_idx);
494   return r;
495
496 error:
497   /* Come here on unsuccessful completion. */
498   sfm_close_reader (r);
499   free (var_by_idx);
500   if (*dict != NULL) 
501     {
502       dict_destroy (*dict);
503       *dict = NULL; 
504     }
505   return NULL;
506 }
507
508 /* Read record type 7, subtype 3. */
509 static int
510 read_machine_int32_info (struct sfm_reader *r, int size, int count)
511 {
512   int32 data[8];
513   int file_bigendian;
514
515   int i;
516
517   if (size != sizeof (int32) || count != 8)
518     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
519                  "subtype 3.    Expected size %d, count 8."),
520            fh_get_filename (r->fh), size, count, sizeof (int32)));
521
522   assertive_buf_read (r, data, sizeof data, 0);
523   if (r->reverse_endian)
524     for (i = 0; i < 8; i++)
525       bswap_int32 (&data[i]);
526
527 #ifdef FPREP_IEEE754
528   if (data[4] != 1)
529     lose ((ME, _("%s: Floating-point representation in system file is not "
530                  "IEEE-754.  PSPP cannot convert between floating-point "
531                  "formats."),
532            fh_get_filename (r->fh)));
533 #else
534 #error Add support for your floating-point format.
535 #endif
536
537 #ifdef WORDS_BIGENDIAN
538   file_bigendian = 1;
539 #else
540   file_bigendian = 0;
541 #endif
542   if (r->reverse_endian)
543     file_bigendian ^= 1;
544   if (file_bigendian ^ (data[6] == 1))
545     lose ((ME, _("%s: File-indicated endianness (%s) does not match "
546                  "endianness intuited from file header (%s)."),
547            fh_get_filename (r->fh),
548            file_bigendian ? _("big-endian") : _("little-endian"),
549            data[6] == 1 ? _("big-endian") : (data[6] == 2 ? _("little-endian")
550                                           : _("unknown"))));
551
552   /* PORTME: Character representation code. */
553   if (data[7] != 2 && data[7] != 3) 
554     lose ((ME, _("%s: File-indicated character representation code (%s) is "
555                  "not ASCII."),
556            fh_get_filename (r->fh),
557            (data[7] == 1 ? "EBCDIC"
558             : (data[7] == 4 ? _("DEC Kanji") : _("Unknown")))));
559
560   return 1;
561
562 error:
563   return 0;
564 }
565
566 /* Read record type 7, subtype 4. */
567 static int
568 read_machine_flt64_info (struct sfm_reader *r, int size, int count)
569 {
570   flt64 data[3];
571   int i;
572
573   if (size != sizeof (flt64) || count != 3)
574     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
575                  "subtype 4.    Expected size %d, count 8."),
576            fh_get_filename (r->fh), size, count, sizeof (flt64)));
577
578   assertive_buf_read (r, data, sizeof data, 0);
579   if (r->reverse_endian)
580     for (i = 0; i < 3; i++)
581       bswap_flt64 (&data[i]);
582
583   if (data[0] != SYSMIS || data[1] != FLT64_MAX
584       || data[2] != second_lowest_flt64)
585     {
586       r->sysmis = data[0];
587       r->highest = data[1];
588       r->lowest = data[2];
589       msg (MW, _("%s: File-indicated value is different from internal value "
590                  "for at least one of the three system values.  SYSMIS: "
591                  "indicated %g, expected %g; HIGHEST: %g, %g; LOWEST: "
592                  "%g, %g."),
593            fh_get_filename (r->fh), (double) data[0], (double) SYSMIS,
594            (double) data[1], (double) FLT64_MAX,
595            (double) data[2], (double) second_lowest_flt64);
596     }
597   
598   return 1;
599
600 error:
601   return 0;
602 }
603
604 static int
605 read_header (struct sfm_reader *r,
606              struct dictionary *dict, struct sfm_read_info *info)
607 {
608   struct sysfile_header hdr;            /* Disk buffer. */
609   char prod_name[sizeof hdr.prod_name + 1];     /* Buffer for product name. */
610   int skip_amt = 0;                     /* Amount of product name to omit. */
611   int i;
612
613   /* Read header, check magic. */
614   assertive_buf_read (r, &hdr, sizeof hdr, 0);
615   if (strncmp ("$FL2", hdr.rec_type, 4) != 0)
616     lose ((ME, _("%s: Bad magic.  Proper system files begin with "
617                  "the four characters `$FL2'. This file will not be read."),
618            fh_get_filename (r->fh)));
619
620   /* Check eye-catcher string. */
621   memcpy (prod_name, hdr.prod_name, sizeof hdr.prod_name);
622   for (i = 0; i < 60; i++)
623     if (!isprint ((unsigned char) prod_name[i]))
624       prod_name[i] = ' ';
625   for (i = 59; i >= 0; i--)
626     if (!isgraph ((unsigned char) prod_name[i]))
627       {
628         prod_name[i] = '\0';
629         break;
630       }
631   prod_name[60] = '\0';
632   
633   {
634 #define N_PREFIXES 2
635     static const char *prefix[N_PREFIXES] =
636       {
637         "@(#) SPSS DATA FILE",
638         "SPSS SYSTEM FILE.",
639       };
640
641     int i;
642
643     for (i = 0; i < N_PREFIXES; i++)
644       if (!strncmp (prefix[i], hdr.prod_name, strlen (prefix[i])))
645         {
646           skip_amt = strlen (prefix[i]);
647           break;
648         }
649   }
650   
651   /* Check endianness. */
652   if (hdr.layout_code == 2)
653     r->reverse_endian = 0;
654   else
655     {
656       bswap_int32 (&hdr.layout_code);
657       if (hdr.layout_code != 2)
658         lose ((ME, _("%s: File layout code has unexpected value %d.  Value "
659                      "should be 2, in big-endian or little-endian format."),
660                fh_get_filename (r->fh), hdr.layout_code));
661
662       r->reverse_endian = 1;
663       bswap_int32 (&hdr.case_size);
664       bswap_int32 (&hdr.compress);
665       bswap_int32 (&hdr.weight_idx);
666       bswap_int32 (&hdr.case_cnt);
667       bswap_flt64 (&hdr.bias);
668     }
669
670
671   /* Copy basic info and verify correctness. */
672   r->value_cnt = hdr.case_size;
673
674   /* If value count is rediculous, then force it to -1 (a sentinel value) */
675   if ( r->value_cnt < 0 || 
676        r->value_cnt > (INT_MAX / (int) sizeof (union value) / 2))
677     r->value_cnt = -1;
678
679   r->compressed = hdr.compress;
680
681   r->weight_idx = hdr.weight_idx - 1;
682
683   r->case_cnt = hdr.case_cnt;
684   if (r->case_cnt < -1 || r->case_cnt > INT_MAX / 2)
685     lose ((ME,
686            _("%s: Number of cases in file (%ld) is not between -1 and %d."),
687            fh_get_filename (r->fh), (long) r->case_cnt, INT_MAX / 2));
688
689   r->bias = hdr.bias;
690   if (r->bias != 100.0)
691     corrupt_msg (MW, _("%s: Compression bias (%g) is not the usual "
692                        "value of 100."),
693                  fh_get_filename (r->fh), r->bias);
694
695   /* Make a file label only on the condition that the given label is
696      not all spaces or nulls. */
697   {
698     int i;
699
700     for (i = sizeof hdr.file_label - 1; i >= 0; i--)
701       if (!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   if (info)
714     {
715       char *cp;
716
717       memcpy (info->creation_date, hdr.creation_date, 9);
718       info->creation_date[9] = 0;
719
720       memcpy (info->creation_time, hdr.creation_time, 8);
721       info->creation_time[8] = 0;
722
723 #ifdef WORDS_BIGENDIAN
724       info->big_endian = !r->reverse_endian;
725 #else
726       info->big_endian = r->reverse_endian;
727 #endif
728
729       info->compressed = hdr.compress;
730
731       info->case_cnt = hdr.case_cnt;
732
733       for (cp = &prod_name[skip_amt]; cp < &prod_name[60]; cp++)
734         if (isgraph ((unsigned char) *cp))
735           break;
736       strcpy (info->product, cp);
737     }
738
739   return 1;
740
741 error:
742   return 0;
743 }
744
745 /* Reads most of the dictionary from file H; also fills in the
746    associated VAR_BY_IDX array. */
747 static int
748 read_variables (struct sfm_reader *r,
749                 struct dictionary *dict, struct variable ***var_by_idx)
750 {
751   int i;
752
753   struct sysfile_variable sv;           /* Disk buffer. */
754   int long_string_count = 0;    /* # of long string continuation
755                                    records still expected. */
756   int next_value = 0;           /* Index to next `value' structure. */
757
758   assert(r);
759
760   *var_by_idx = 0;
761
762   /* Pre-allocate variables. */
763   if (r->value_cnt != -1) 
764     {
765       *var_by_idx = xnmalloc (r->value_cnt, sizeof **var_by_idx);
766       r->vars = xnmalloc (r->value_cnt, sizeof *r->vars);
767     }
768
769
770   /* Read in the entry for each variable and use the info to
771      initialize the dictionary. */
772   for (i = 0; ; ++i)
773     {
774       struct variable *vv;
775       char name[SHORT_NAME_LEN + 1];
776       int nv;
777       int j;
778
779       if ( r->value_cnt != -1  && i >= r->value_cnt ) 
780         break;
781
782       assertive_buf_read (r, &sv, sizeof sv, 0);
783
784       if (r->reverse_endian)
785         {
786           bswap_int32 (&sv.rec_type);
787           bswap_int32 (&sv.type);
788           bswap_int32 (&sv.has_var_label);
789           bswap_int32 (&sv.n_missing_values);
790           bswap_int32 (&sv.print);
791           bswap_int32 (&sv.write);
792         }
793
794       /* We've come to the end of the variable entries */
795       if (sv.rec_type != 2)
796         {
797           buf_unread(r, sizeof sv);
798           r->value_cnt = i;
799           break;
800         }
801
802       if ( -1 == r->value_cnt ) 
803         {
804           *var_by_idx = xnrealloc (*var_by_idx, i + 1, sizeof **var_by_idx);
805           r->vars = xnrealloc (r->vars, i + 1, sizeof *r->vars);
806         }
807
808       /* If there was a long string previously, make sure that the
809          continuations are present; otherwise make sure there aren't
810          any. */
811       if (long_string_count)
812         {
813           if (sv.type != -1)
814             lose ((ME, _("%s: position %d: String variable does not have "
815                          "proper number of continuation records."),
816                    fh_get_filename (r->fh), i));
817
818
819           r->vars[i].width = -1;
820           (*var_by_idx)[i] = NULL;
821           long_string_count--;
822           continue;
823         }
824       else if (sv.type == -1)
825         lose ((ME, _("%s: position %d: Superfluous long string continuation "
826                      "record."),
827                fh_get_filename (r->fh), i));
828
829       /* Check fields for validity. */
830       if (sv.type < 0 || sv.type > 255)
831         lose ((ME, _("%s: position %d: Bad variable type code %d."),
832                fh_get_filename (r->fh), i, sv.type));
833       if (sv.has_var_label != 0 && sv.has_var_label != 1)
834         lose ((ME, _("%s: position %d: Variable label indicator field is not "
835                "0 or 1."), fh_get_filename (r->fh), i));
836       if (sv.n_missing_values < -3 || sv.n_missing_values > 3
837           || sv.n_missing_values == -1)
838         lose ((ME, _("%s: position %d: Missing value indicator field is not "
839                      "-3, -2, 0, 1, 2, or 3."), fh_get_filename (r->fh), i));
840
841       /* Copy first character of variable name. */
842       if (!isalpha ((unsigned char) sv.name[0])
843           && sv.name[0] != '@' && sv.name[0] != '#')
844         lose ((ME, _("%s: position %d: Variable name begins with invalid "
845                      "character."),
846                fh_get_filename (r->fh), i));
847       if (islower ((unsigned char) sv.name[0]))
848         msg (MW, _("%s: position %d: Variable name begins with lowercase letter "
849                    "%c."),
850              fh_get_filename (r->fh), i, sv.name[0]);
851       if (sv.name[0] == '#')
852         msg (MW, _("%s: position %d: Variable name begins with octothorpe "
853                    "(`#').  Scratch variables should not appear in system "
854                    "files."),
855              fh_get_filename (r->fh), i);
856       name[0] = toupper ((unsigned char) (sv.name[0]));
857
858       /* Copy remaining characters of variable name. */
859       for (j = 1; j < SHORT_NAME_LEN; j++)
860         {
861           int c = (unsigned char) sv.name[j];
862
863           if (isspace (c))
864             break;
865           else if (islower (c))
866             {
867               msg (MW, _("%s: position %d: Variable name character %d is "
868                          "lowercase letter %c."),
869                    fh_get_filename (r->fh), i, j + 1, sv.name[j]);
870               name[j] = toupper ((unsigned char) (c));
871             }
872           else if (isalnum (c) || c == '.' || c == '@'
873                    || c == '#' || c == '$' || c == '_')
874             name[j] = c;
875           else
876             lose ((ME, _("%s: position %d: character `\\%03o' (%c) is not valid in a "
877                          "variable name."),
878                    fh_get_filename (r->fh), i, c, c));
879         }
880       name[j] = 0;
881
882       if ( ! var_is_valid_name(name, false) ) 
883         lose ((ME, _("%s: Invalid variable name `%s' within system file."),
884                fh_get_filename (r->fh), name));
885
886       /* Create variable. */
887
888       vv = (*var_by_idx)[i] = dict_create_var (dict, name, sv.type);
889       if (vv == NULL) 
890         lose ((ME, _("%s: Duplicate variable name `%s' within system file."),
891                fh_get_filename (r->fh), name));
892
893       var_set_short_name (vv, vv->name);
894
895       /* Case reading data. */
896       nv = sv.type == 0 ? 1 : DIV_RND_UP (sv.type, sizeof (flt64));
897       long_string_count = nv - 1;
898       next_value += nv;
899
900       /* Get variable label, if any. */
901       if (sv.has_var_label == 1)
902         {
903           /* Disk buffer. */
904           int32 len;
905
906           /* Read length of label. */
907           assertive_buf_read (r, &len, sizeof len, 0);
908           if (r->reverse_endian)
909             bswap_int32 (&len);
910
911           /* Check len. */
912           if (len < 0 || len > 255)
913             lose ((ME, _("%s: Variable %s indicates variable label of invalid "
914                          "length %d."),
915                    fh_get_filename (r->fh), vv->name, len));
916
917           if ( len != 0 ) 
918             {
919               /* Read label into variable structure. */
920               vv->label = buf_read (r, NULL, ROUND_UP (len, sizeof (int32)), len + 1);
921               if (vv->label == NULL)
922                 goto error;
923               vv->label[len] = '\0';
924             }
925         }
926
927       /* Set missing values. */
928       if (sv.n_missing_values != 0)
929         {
930           flt64 mv[3];
931           int mv_cnt = abs (sv.n_missing_values);
932
933           if (vv->width > MAX_SHORT_STRING)
934             lose ((ME, _("%s: Long string variable %s may not have missing "
935                          "values."),
936                    fh_get_filename (r->fh), vv->name));
937
938           assertive_buf_read (r, mv, sizeof *mv * mv_cnt, 0);
939
940           if (r->reverse_endian && vv->type == NUMERIC)
941             for (j = 0; j < mv_cnt; j++)
942               bswap_flt64 (&mv[j]);
943
944           if (sv.n_missing_values > 0)
945             {
946               for (j = 0; j < sv.n_missing_values; j++)
947                 if (vv->type == NUMERIC)
948                   mv_add_num (&vv->miss, mv[j]);
949                 else
950                   mv_add_str (&vv->miss, (char *) &mv[j]);
951             }
952           else
953             {
954               if (vv->type == ALPHA)
955                 lose ((ME, _("%s: String variable %s may not have missing "
956                              "values specified as a range."),
957                        fh_get_filename (r->fh), vv->name));
958
959               if (mv[0] == r->lowest)
960                 mv_add_num_range (&vv->miss, LOWEST, mv[1]);
961               else if (mv[1] == r->highest)
962                 mv_add_num_range (&vv->miss, mv[0], HIGHEST);
963               else
964                 mv_add_num_range (&vv->miss, mv[0], mv[1]);
965
966               if (sv.n_missing_values == -3)
967                 mv_add_num (&vv->miss, mv[2]);
968             }
969         }
970
971       if (!parse_format_spec (r, sv.print, &vv->print, vv)
972           || !parse_format_spec (r, sv.write, &vv->write, vv))
973         goto error;
974
975       r->vars[i].width = vv->width;
976       r->vars[i].fv = vv->fv;
977
978     }
979
980   /* Some consistency checks. */
981   if (long_string_count != 0)
982     lose ((ME, _("%s: Long string continuation records omitted at end of "
983                  "dictionary."),
984            fh_get_filename (r->fh)));
985
986   if (next_value != r->value_cnt)
987     corrupt_msg(MW, _("%s: System file header indicates %d variable positions but "
988                  "%d were read from file."),
989            fh_get_filename (r->fh), r->value_cnt, next_value);
990
991
992   return 1;
993
994 error:
995   return 0;
996 }
997
998 /* Translates the format spec from sysfile format to internal
999    format. */
1000 static int
1001 parse_format_spec (struct sfm_reader *r, int32 s,
1002                    struct fmt_spec *f, struct variable *v)
1003 {
1004   f->type = translate_fmt ((s >> 16) & 0xff);
1005   if (f->type == -1)
1006     lose ((ME, _("%s: Bad format specifier byte (%d)."),
1007            fh_get_filename (r->fh), (s >> 16) & 0xff));
1008   f->w = (s >> 8) & 0xff;
1009   f->d = s & 0xff;
1010
1011   if ((v->type == ALPHA) ^ ((formats[f->type].cat & FCAT_STRING) != 0))
1012     lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
1013            fh_get_filename (r->fh),
1014            v->type == ALPHA ? _("String") : _("Numeric"),
1015            v->name,
1016            formats[f->type].cat & FCAT_STRING ? _("string") : _("numeric"),
1017            formats[f->type].name));
1018
1019   if (!check_output_specifier (f, false)
1020       || !check_specifier_width (f, v->width, false)) 
1021     {
1022       msg (ME, _("%s variable %s has invalid format specifier %s."),
1023            v->type == NUMERIC ? _("Numeric") : _("String"),
1024            v->name, fmt_to_string (f));
1025       *f = v->type == NUMERIC ? f8_2 : make_output_format (FMT_A, v->width, 0);
1026     }
1027   return 1;
1028
1029 error:
1030   return 0;
1031 }
1032
1033 /* Reads value labels from sysfile H and inserts them into the
1034    associated dictionary. */
1035 int
1036 read_value_labels (struct sfm_reader *r,
1037                    struct dictionary *dict, struct variable **var_by_idx)
1038 {
1039   struct label 
1040     {
1041       char raw_value[8];        /* Value as uninterpreted bytes. */
1042       union value value;        /* Value. */
1043       char *label;              /* Null-terminated label string. */
1044     };
1045
1046   struct label *labels = NULL;
1047   int32 n_labels;               /* Number of labels. */
1048
1049   struct variable **var = NULL; /* Associated variables. */
1050   int32 n_vars;                 /* Number of associated variables. */
1051
1052   int i;
1053
1054   /* First step: read the contents of the type 3 record and record its
1055      contents.  Note that we can't do much with the data since we
1056      don't know yet whether it is of numeric or string type. */
1057
1058   /* Read number of labels. */
1059   assertive_buf_read (r, &n_labels, sizeof n_labels, 0);
1060   if (r->reverse_endian)
1061     bswap_int32 (&n_labels);
1062
1063   if ( n_labels >= ((int32) ~0) / sizeof *labels)
1064     {    
1065       corrupt_msg(MW, _("%s: Invalid number of labels: %d.  Ignoring labels."),
1066                   fh_get_filename (r->fh), n_labels);
1067       n_labels = 0;
1068     }
1069
1070   /* Allocate memory. */
1071   labels = xcalloc (n_labels, sizeof *labels);
1072   for (i = 0; i < n_labels; i++)
1073     labels[i].label = NULL;
1074
1075   /* Read each value/label tuple into labels[]. */
1076   for (i = 0; i < n_labels; i++)
1077     {
1078       struct label *label = labels + i;
1079       unsigned char label_len;
1080       size_t padded_len;
1081
1082       /* Read value. */
1083       assertive_buf_read (r, label->raw_value, sizeof label->raw_value, 0);
1084
1085       /* Read label length. */
1086       assertive_buf_read (r, &label_len, sizeof label_len, 0);
1087       padded_len = ROUND_UP (label_len + 1, sizeof (flt64));
1088
1089       /* Read label, padding. */
1090       label->label = xmalloc (padded_len + 1);
1091       assertive_buf_read (r, label->label, padded_len - 1, 0);
1092       label->label[label_len] = 0;
1093     }
1094
1095   /* Second step: Read the type 4 record that has the list of
1096      variables to which the value labels are to be applied. */
1097
1098   /* Read record type of type 4 record. */
1099   {
1100     int32 rec_type;
1101     
1102     assertive_buf_read (r, &rec_type, sizeof rec_type, 0);
1103     if (r->reverse_endian)
1104       bswap_int32 (&rec_type);
1105     
1106     if (rec_type != 4)
1107       lose ((ME, _("%s: Variable index record (type 4) does not immediately "
1108                    "follow value label record (type 3) as it should."),
1109              fh_get_filename (r->fh)));
1110   }
1111
1112   /* Read number of variables associated with value label from type 4
1113      record. */
1114   assertive_buf_read (r, &n_vars, sizeof n_vars, 0);
1115   if (r->reverse_endian)
1116     bswap_int32 (&n_vars);
1117   if (n_vars < 1 || n_vars > dict_get_var_cnt (dict))
1118     lose ((ME, _("%s: Number of variables associated with a value label (%d) "
1119                  "is not between 1 and the number of variables (%d)."),
1120            fh_get_filename (r->fh), n_vars, dict_get_var_cnt (dict)));
1121
1122   /* Read the list of variables. */
1123   var = xnmalloc (n_vars, sizeof *var);
1124   for (i = 0; i < n_vars; i++)
1125     {
1126       int32 var_idx;
1127       struct variable *v;
1128
1129       /* Read variable index, check range. */
1130       assertive_buf_read (r, &var_idx, sizeof var_idx, 0);
1131       if (r->reverse_endian)
1132         bswap_int32 (&var_idx);
1133       if (var_idx < 1 || var_idx > r->value_cnt)
1134         lose ((ME, _("%s: Variable index associated with value label (%d) is "
1135                      "not between 1 and the number of values (%d)."),
1136                fh_get_filename (r->fh), var_idx, r->value_cnt));
1137
1138       /* Make sure it's a real variable. */
1139       v = var_by_idx[var_idx - 1];
1140       if (v == NULL)
1141         lose ((ME, _("%s: Variable index associated with value label (%d) "
1142                      "refers to a continuation of a string variable, not to "
1143                      "an actual variable."),
1144                fh_get_filename (r->fh), var_idx));
1145       if (v->type == ALPHA && v->width > MAX_SHORT_STRING)
1146         lose ((ME, _("%s: Value labels are not allowed on long string "
1147                      "variables (%s)."),
1148                fh_get_filename (r->fh), v->name));
1149
1150       /* Add it to the list of variables. */
1151       var[i] = v;
1152     }
1153
1154   /* Type check the variables. */
1155   for (i = 1; i < n_vars; i++)
1156     if (var[i]->type != var[0]->type)
1157       lose ((ME, _("%s: Variables associated with value label are not all of "
1158                    "identical type.  Variable %s has %s type, but variable "
1159                    "%s has %s type."),
1160              fh_get_filename (r->fh),
1161              var[0]->name, var[0]->type == ALPHA ? _("string") : _("numeric"),
1162              var[i]->name, var[i]->type == ALPHA ? _("string") : _("numeric")));
1163
1164   /* Fill in labels[].value, now that we know the desired type. */
1165   for (i = 0; i < n_labels; i++) 
1166     {
1167       struct label *label = labels + i;
1168       
1169       if (var[0]->type == ALPHA)
1170         {
1171           const int copy_len = min (sizeof label->raw_value,
1172                                     sizeof label->label);
1173           memcpy (label->value.s, label->raw_value, copy_len);
1174         } else {
1175           flt64 f;
1176           assert (sizeof f == sizeof label->raw_value);
1177           memcpy (&f, label->raw_value, sizeof f);
1178           if (r->reverse_endian)
1179             bswap_flt64 (&f);
1180           label->value.f = f;
1181         }
1182     }
1183   
1184   /* Assign the value_label's to each variable. */
1185   for (i = 0; i < n_vars; i++)
1186     {
1187       struct variable *v = var[i];
1188       int j;
1189
1190       /* Add each label to the variable. */
1191       for (j = 0; j < n_labels; j++)
1192         {
1193           struct label *label = labels + j;
1194           if (!val_labs_replace (v->val_labs, label->value, label->label))
1195             continue;
1196
1197           if (var[0]->type == NUMERIC)
1198             msg (MW, _("%s: File contains duplicate label for value %g for "
1199                        "variable %s."),
1200                  fh_get_filename (r->fh), label->value.f, v->name);
1201           else
1202             msg (MW, _("%s: File contains duplicate label for value `%.*s' "
1203                        "for variable %s."),
1204                  fh_get_filename (r->fh), v->width, label->value.s, v->name);
1205         }
1206     }
1207
1208   for (i = 0; i < n_labels; i++)
1209     free (labels[i].label);
1210   free (labels);
1211   free (var);
1212   return 1;
1213
1214 error:
1215   if (labels) 
1216     {
1217       for (i = 0; i < n_labels; i++)
1218         free (labels[i].label);
1219       free (labels); 
1220     }
1221   free (var);
1222   return 0;
1223 }
1224
1225 /* Reads BYTE_CNT bytes from the file represented by H.  If BUF is
1226    non-NULL, uses that as the buffer; otherwise allocates at least
1227    MIN_ALLOC bytes.  Returns a pointer to the buffer on success, NULL
1228    on failure. */
1229 static void *
1230 buf_read (struct sfm_reader *r, void *buf, size_t byte_cnt, size_t min_alloc)
1231 {
1232   assert (r);
1233
1234   if (buf == NULL && byte_cnt > 0 )
1235     buf = xmalloc (max (byte_cnt, min_alloc));
1236
1237   if ( byte_cnt == 0 )
1238     return buf;
1239
1240   
1241   if (1 != fread (buf, byte_cnt, 1, r->file))
1242     {
1243       if (ferror (r->file))
1244         msg (ME, _("%s: Reading system file: %s."),
1245              fh_get_filename (r->fh), strerror (errno));
1246       else
1247         corrupt_msg (ME, _("%s: Unexpected end of file."),
1248                      fh_get_filename (r->fh));
1249       return NULL;
1250     }
1251   return buf;
1252 }
1253
1254 /* Winds the reader BYTE_CNT bytes back in the reader stream.   */
1255 void
1256 buf_unread(struct sfm_reader *r, size_t byte_cnt)
1257 {
1258   assert(byte_cnt > 0);
1259
1260   if ( 0 != fseek(r->file, -byte_cnt, SEEK_CUR))
1261     {
1262       msg (ME, _("%s: Seeking system file: %s."),
1263            fh_get_filename (r->fh), strerror (errno));
1264     }
1265 }
1266
1267 /* Reads a document record, type 6, from system file R, and sets up
1268    the documents and n_documents fields in the associated
1269    dictionary. */
1270 static int
1271 read_documents (struct sfm_reader *r, struct dictionary *dict)
1272 {
1273   int32 line_cnt;
1274   char *documents;
1275
1276   if (dict_get_documents (dict) != NULL)
1277     lose ((ME, _("%s: System file contains multiple "
1278                  "type 6 (document) records."),
1279            fh_get_filename (r->fh)));
1280
1281   assertive_buf_read (r, &line_cnt, sizeof line_cnt, 0);
1282   if (line_cnt <= 0)
1283     lose ((ME, _("%s: Number of document lines (%ld) "
1284                  "must be greater than 0."),
1285            fh_get_filename (r->fh), (long) line_cnt));
1286
1287   documents = buf_read (r, NULL, 80 * line_cnt, line_cnt * 80 + 1);
1288   /* FIXME?  Run through asciify. */
1289   if (documents == NULL)
1290     return 0;
1291   documents[80 * line_cnt] = '\0';
1292   dict_set_documents (dict, documents);
1293   free (documents);
1294   return 1;
1295
1296 error:
1297   return 0;
1298 }
1299 \f
1300 /* Data reader. */
1301
1302 /* Reads compressed data into H->BUF and sets other pointers
1303    appropriately.  Returns nonzero only if both no errors occur and
1304    data was read. */
1305 static int
1306 buffer_input (struct sfm_reader *r)
1307 {
1308   size_t amt;
1309
1310   if (r->buf == NULL)
1311     r->buf = xnmalloc (128, sizeof *r->buf);
1312   amt = fread (r->buf, sizeof *r->buf, 128, r->file);
1313   if (ferror (r->file))
1314     {
1315       msg (ME, _("%s: Error reading file: %s."),
1316            fh_get_filename (r->fh), strerror (errno));
1317       return 0;
1318     }
1319   r->ptr = r->buf;
1320   r->end = &r->buf[amt];
1321   return amt;
1322 }
1323
1324 /* Reads a single case consisting of compressed data from system
1325    file H into the array BUF[] according to reader R, and
1326    returns nonzero only if successful. */
1327 /* Data in system files is compressed in this manner.  Data
1328    values are grouped into sets of eight ("octets").  Each value
1329    in an octet has one instruction byte that are output together.
1330    Each instruction byte gives a value for that byte or indicates
1331    that the value can be found following the instructions. */
1332 static int
1333 read_compressed_data (struct sfm_reader *r, flt64 *buf)
1334 {
1335   const unsigned char *p_end = r->x + sizeof (flt64);
1336   unsigned char *p = r->y;
1337
1338   const flt64 *buf_beg = buf;
1339   const flt64 *buf_end = &buf[r->value_cnt];
1340
1341   for (;;)
1342     {
1343       for (; p < p_end; p++){
1344         switch (*p)
1345           {
1346           case 0:
1347             /* Code 0 is ignored. */
1348             continue;
1349           case 252:
1350             /* Code 252 is end of file. */
1351             if (buf_beg != buf)
1352               lose ((ME, _("%s: Compressed data is corrupted.  Data ends "
1353                      "in partial case."),
1354                      fh_get_filename (r->fh)));
1355             goto error;
1356           case 253:
1357             /* Code 253 indicates that the value is stored explicitly
1358                following the instruction bytes. */
1359             if (r->ptr == NULL || r->ptr >= r->end)
1360               if (!buffer_input (r))
1361                 {
1362                   lose ((ME, _("%s: Unexpected end of file."),
1363                          fh_get_filename (r->fh)));
1364                   goto error;
1365                 }
1366             memcpy (buf++, r->ptr++, sizeof *buf);
1367             if (buf >= buf_end)
1368               goto success;
1369             break;
1370           case 254:
1371             /* Code 254 indicates a string that is all blanks. */
1372             memset (buf++, ' ', sizeof *buf);
1373             if (buf >= buf_end)
1374               goto success;
1375             break;
1376           case 255:
1377             /* Code 255 indicates the system-missing value. */
1378             *buf = r->sysmis;
1379             if (r->reverse_endian)
1380               bswap_flt64 (buf);
1381             buf++;
1382             if (buf >= buf_end)
1383               goto success;
1384             break;
1385           default:
1386             /* Codes 1 through 251 inclusive are taken to indicate a
1387                value of (BYTE - BIAS), where BYTE is the byte's value
1388                and BIAS is the compression bias (generally 100.0). */
1389             *buf = *p - r->bias;
1390             if (r->reverse_endian)
1391               bswap_flt64 (buf);
1392             buf++;
1393             if (buf >= buf_end)
1394               goto success;
1395             break;
1396           }
1397       }
1398       /* We have reached the end of this instruction octet.  Read
1399          another. */
1400       if (r->ptr == NULL || r->ptr >= r->end)
1401         if (!buffer_input (r))
1402           {
1403             if (buf_beg != buf)
1404               lose ((ME, _("%s: Unexpected end of file."),
1405                      fh_get_filename (r->fh)));
1406             goto error;
1407           }
1408       memcpy (r->x, r->ptr++, sizeof *buf);
1409       p = r->x;
1410     }
1411
1412   /* Not reached. */
1413   assert (0);
1414
1415 success:
1416   /* We have filled up an entire record.  Update state and return
1417      successfully. */
1418   r->y = ++p;
1419   return 1;
1420
1421 error:
1422   /* We have been unsuccessful at filling a record, either through i/o
1423      error or through an end-of-file indication.  Update state and
1424      return unsuccessfully. */
1425   return 0;
1426 }
1427
1428 /* Reads one case from READER's file into C.  Returns nonzero
1429    only if successful. */
1430 int
1431 sfm_read_case (struct sfm_reader *r, struct ccase *c)
1432 {
1433   if (!r->compressed && sizeof (flt64) == sizeof (double)) 
1434     {
1435       /* Fast path: external and internal representations are the
1436          same, except possibly for endianness or SYSMIS.  Read
1437          directly into the case's buffer, then fix up any minor
1438          details as needed. */
1439       if (!fread_ok (r, case_data_all_rw (c),
1440                      sizeof (union value) * r->value_cnt))
1441         return 0;
1442
1443       /* Fix up endianness if needed. */
1444       if (r->reverse_endian) 
1445         {
1446           int i;
1447           
1448           for (i = 0; i < r->value_cnt; i++) 
1449             if (r->vars[i].width == 0)
1450               bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
1451         }
1452
1453       /* Fix up SYSMIS values if needed.
1454          I don't think this will ever actually kick in, but it
1455          can't hurt. */
1456       if (r->sysmis != SYSMIS) 
1457         {
1458           int i;
1459           
1460           for (i = 0; i < r->value_cnt; i++) 
1461             if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
1462               case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
1463         }
1464     }
1465   else 
1466     {
1467       /* Slow path: internal and external representations differ.
1468          Read into a bounce buffer, then copy to C. */
1469       flt64 *bounce;
1470       flt64 *bounce_cur;
1471       size_t bounce_size;
1472       int read_ok;
1473       int i;
1474
1475       bounce_size = sizeof *bounce * r->value_cnt;
1476       bounce = bounce_cur = local_alloc (bounce_size);
1477
1478       if (!r->compressed)
1479         read_ok = fread_ok (r, bounce, bounce_size);
1480       else
1481         read_ok = read_compressed_data (r, bounce);
1482       if (!read_ok) 
1483         {
1484           local_free (bounce);
1485           return 0;
1486         }
1487
1488       for (i = 0; i < r->value_cnt; i++)
1489         {
1490           struct sfm_var *v = &r->vars[i];
1491
1492           if (v->width == 0)
1493             {
1494               flt64 f = *bounce_cur++;
1495               if (r->reverse_endian)
1496                 bswap_flt64 (&f);
1497               case_data_rw (c, v->fv)->f = f == r->sysmis ? SYSMIS : f;
1498             }
1499           else if (v->width != -1)
1500             {
1501               memcpy (case_data_rw (c, v->fv)->s, bounce_cur, v->width);
1502               bounce_cur += DIV_RND_UP (v->width, sizeof (flt64));
1503             }
1504         }
1505
1506       local_free (bounce);
1507     }
1508   return 1; 
1509 }
1510
1511 static int
1512 fread_ok (struct sfm_reader *r, void *buffer, size_t byte_cnt)
1513 {
1514   size_t read_bytes = fread (buffer, 1, byte_cnt, r->file);
1515
1516   if (read_bytes == byte_cnt)
1517     return 1;
1518   else
1519     {
1520       if (ferror (r->file))
1521         msg (ME, _("%s: Reading system file: %s."),
1522              fh_get_filename (r->fh), strerror (errno));
1523       else if (read_bytes != 0)
1524         msg (ME, _("%s: Partial record at end of system file."),
1525              fh_get_filename (r->fh));
1526       return 0;
1527     }
1528 }
1529 \f
1530 /* Returns true if FILE is an SPSS system file,
1531    false otherwise. */
1532 bool
1533 sfm_detect (FILE *file) 
1534 {
1535   struct sysfile_header hdr;
1536
1537   if (fread (&hdr, sizeof hdr, 1, file) != 1)
1538     return false;
1539   if (strncmp ("$FL2", hdr.rec_type, 4))
1540     return false;
1541   return true; 
1542 }