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