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