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