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