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