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