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