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