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