ab33e700f724ab2c5c6562593be0351a673a3e17
[pspp-builds.git] / src / pfm-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 "pfm.h"
22 #include "error.h"
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <math.h>
29 #include "alloc.h"
30 #include "file-handle.h"
31 #include "format.h"
32 #include "getline.h"
33 #include "hash.h"
34 #include "magic.h"
35 #include "misc.h"
36 #include "str.h"
37 #include "value-labels.h"
38 #include "var.h"
39
40 #include "debug-print.h"
41
42 /* pfm's file_handle extension. */
43 struct pfm_fhuser_ext
44   {
45     FILE *file;                 /* Actual file. */
46
47     struct dictionary *dict;    /* File's dictionary. */
48     int weight_index;           /* 0-based index of weight variable, or -1. */
49
50     unsigned char *trans;       /* 256-byte character set translation table. */
51
52     int nvars;                  /* Number of variables. */
53     int *vars;                  /* Variable widths, 0 for numeric. */
54     int case_size;              /* Number of `value's per case. */
55
56     unsigned char buf[83];      /* Input buffer. */
57     unsigned char *bp;          /* Buffer pointer. */
58     int cc;                     /* Current character. */
59   };
60
61 static struct fh_ext_class pfm_r_class;
62
63 static int 
64 corrupt_msg (struct file_handle *h, const char *format,...)
65      PRINTF_FORMAT (2, 3);
66
67 /* Displays a corruption error. */
68 static int
69 corrupt_msg (struct file_handle *h, const char *format, ...)
70 {
71   struct pfm_fhuser_ext *ext = h->ext;
72   char buf[1024];
73   
74   {
75     va_list args;
76
77     va_start (args, format);
78     vsnprintf (buf, 1024, format, args);
79     va_end (args);
80   }
81   
82   {
83     char *title;
84     struct error e;
85     const char *filename;
86
87     e.class = ME;
88     getl_location (&e.where.filename, &e.where.line_number);
89     filename = handle_get_filename (h);
90     e.title = title = local_alloc (strlen (filename) + 80);
91     sprintf (title, _("portable file %s corrupt at offset %ld: "),
92              filename, ftell (ext->file) - (82 - (long) (ext->bp - ext->buf)));
93     e.text = buf;
94
95     err_vmsg (&e);
96
97     local_free (title);
98   }
99   
100   return 0;
101 }
102
103 /* Closes a portable file after we're done with it. */
104 static void
105 pfm_close (struct file_handle *h)
106 {
107   struct pfm_fhuser_ext *ext = h->ext;
108
109   if (EOF == fclose (ext->file))
110     msg (ME, _("%s: Closing portable file: %s."),
111          handle_get_filename (h), strerror (errno));
112   free (ext->vars);
113   free (ext->trans);
114   free (h->ext);
115 }
116
117 /* Displays the message X with corrupt_msg, then jumps to the lossage
118    label. */
119 #define lose(X)                                 \
120         do                                      \
121           {                                     \
122             corrupt_msg X;                      \
123             goto lossage;                       \
124           }                                     \
125         while (0)
126
127 /* Read an 80-character line into handle H's buffer.  Return
128    success. */
129 static int
130 fill_buf (struct file_handle *h)
131 {
132   struct pfm_fhuser_ext *ext = h->ext;
133
134   if (80 != fread (ext->buf, 1, 80, ext->file))
135     lose ((h, _("Unexpected end of file.")));
136
137   /* PORTME: line ends. */
138   {
139     int c;
140     
141     c = getc (ext->file);
142     if (c != '\n' && c != '\r')
143       lose ((h, _("Bad line end.")));
144
145     c = getc (ext->file);
146     if (c != '\n' && c != '\r')
147       ungetc (c, ext->file);
148   }
149   
150   if (ext->trans)
151     {
152       int i;
153       
154       for (i = 0; i < 80; i++)
155         ext->buf[i] = ext->trans[ext->buf[i]];
156     }
157
158   ext->bp = ext->buf;
159
160   return 1;
161
162  lossage:
163   return 0;
164 }
165
166 /* Read a single character into cur_char.  Return success; */
167 static int
168 read_char (struct file_handle *h)
169 {
170   struct pfm_fhuser_ext *ext = h->ext;
171
172   if (ext->bp >= &ext->buf[80] && !fill_buf (h))
173     return 0;
174   ext->cc = *ext->bp++;
175   return 1;
176 }
177
178 /* Advance a single character. */
179 #define advance() if (!read_char (h)) goto lossage
180
181 /* Skip a single character if present, and return whether it was
182    skipped. */
183 static inline int
184 skip_char (struct file_handle *h, int c)
185 {
186   struct pfm_fhuser_ext *ext = h->ext;
187   
188   if (ext->cc == c)
189     {
190       advance ();
191       return 1;
192     }
193  lossage:
194   return 0;
195 }
196
197 /* Skip a single character if present, and return whether it was
198    skipped. */
199 #define match(C) skip_char (h, C)
200
201 static int read_header (struct file_handle *h);
202 static int read_version_data (struct file_handle *h, struct pfm_read_info *inf);
203 static int read_variables (struct file_handle *h);
204 static int read_value_label (struct file_handle *h);
205 void dump_dictionary (struct dictionary *dict);
206
207 /* Reads the dictionary from file with handle H, and returns it in a
208    dictionary structure.  This dictionary may be modified in order to
209    rename, reorder, and delete variables, etc. */
210 struct dictionary *
211 pfm_read_dictionary (struct file_handle *h, struct pfm_read_info *inf)
212 {
213   /* The file handle extension record. */
214   struct pfm_fhuser_ext *ext;
215
216   /* Check whether the file is already open. */
217   if (h->class == &pfm_r_class)
218     {
219       ext = h->ext;
220       return ext->dict;
221     }
222   else if (h->class != NULL)
223     {
224       msg (ME, _("Cannot read file %s as portable file: already opened "
225                  "for %s."),
226            handle_get_name (h), h->class->name);
227       return NULL;
228     }
229
230   msg (VM (1), _("%s: Opening portable-file handle %s for reading."),
231        handle_get_filename (h), handle_get_name (h));
232
233   /* Open the physical disk file. */
234   ext = xmalloc (sizeof (struct pfm_fhuser_ext));
235   ext->file = fopen (handle_get_filename (h), "rb");
236   if (ext->file == NULL)
237     {
238       msg (ME, _("An error occurred while opening \"%s\" for reading "
239                  "as a portable file: %s."),
240            handle_get_filename (h), strerror (errno));
241       err_cond_fail ();
242       free (ext);
243       return NULL;
244     }
245
246   /* Initialize the sfm_fhuser_ext structure. */
247   h->class = &pfm_r_class;
248   h->ext = ext;
249   ext->dict = NULL;
250   ext->trans = NULL;
251   if (!fill_buf (h))
252     goto lossage;
253   advance ();
254
255   /* Read the header. */
256   if (!read_header (h))
257     goto lossage;
258   
259   /* Read version, date info, product identification. */
260   if (!read_version_data (h, inf))
261     goto lossage;
262
263   /* Read variables. */
264   if (!read_variables (h))
265     goto lossage;
266
267   /* Value labels. */
268   while (match (77 /* D */))
269     if (!read_value_label (h))
270       goto lossage;
271
272   if (!match (79 /* F */))
273     lose ((h, _("Data record expected.")));
274
275   msg (VM (2), _("Read portable-file dictionary successfully."));
276
277 #if DEBUGGING
278   dump_dictionary (ext->dict);
279 #endif
280   return ext->dict;
281
282  lossage:
283   /* Come here on unsuccessful completion. */
284   msg (VM (1), _("Error reading portable-file dictionary."));
285   
286   fclose (ext->file);
287   if (ext && ext->dict)
288     dict_destroy (ext->dict);
289   free (ext);
290   h->class = NULL;
291   h->ext = NULL;
292   return NULL;
293 }
294 \f
295 /* Read a floating point value and return its value, or
296    second_lowest_value on error. */
297 static double
298 read_float (struct file_handle *h)
299 {
300   struct pfm_fhuser_ext *ext = h->ext;                        
301   double num = 0.;
302   int got_dot = 0;
303   int got_digit = 0;
304   int exponent = 0;
305   int neg = 0;
306
307   /* Skip leading spaces. */
308   while (match (126 /* space */))
309     ;
310
311   if (match (137 /* * */))
312     {
313       advance ();       /* Probably a dot (.) but doesn't appear to matter. */
314       return SYSMIS;
315     }
316   else if (match (141 /* - */))
317     neg = 1;
318
319   for (;;)
320     {
321       if (ext->cc >= 64 /* 0 */ && ext->cc <= 93 /* T */)
322         {
323           got_digit++;
324
325           /* Make sure that multiplication by 30 will not overflow.  */
326           if (num > DBL_MAX * (1. / 30.))
327             /* The value of the digit doesn't matter, since we have already
328                gotten as many digits as can be represented in a `double'.
329                This doesn't necessarily mean the result will overflow.
330                The exponent may reduce it to within range.
331
332                We just need to record that there was another
333                digit so that we can multiply by 10 later.  */
334             ++exponent;
335           else
336             num = (num * 30.0) + (ext->cc - 64);
337
338           /* Keep track of the number of digits after the decimal point.
339              If we just divided by 30 here, we would lose precision.  */
340           if (got_dot)
341             --exponent;
342         }
343       else if (!got_dot && ext->cc == 127 /* . */)
344         /* Record that we have found the decimal point.  */
345         got_dot = 1;
346       else
347         /* Any other character terminates the number.  */
348         break;
349
350       advance ();
351     }
352
353   if (!got_digit)
354     lose ((h, "Number expected."));
355       
356   if (ext->cc == 130 /* + */ || ext->cc == 141 /* - */)
357     {
358       /* Get the exponent.  */
359       long int exp = 0;
360       int neg_exp = ext->cc == 141 /* - */;
361
362       for (;;)
363         {
364           advance ();
365
366           if (ext->cc < 64 /* 0 */ || ext->cc > 93 /* T */)
367             break;
368
369           if (exp > LONG_MAX / 30)
370             goto overflow;
371           exp = exp * 30 + (ext->cc - 64);
372         }
373
374       /* We don't check whether there were actually any digits, but we
375          probably should. */
376       if (neg_exp)
377         exp = -exp;
378       exponent += exp;
379     }
380   
381   if (!match (142 /* / */))
382     lose ((h, _("Missing numeric terminator.")));
383
384   /* Multiply NUM by 30 to the EXPONENT power, checking for overflow.  */
385
386   if (exponent < 0)
387     num *= pow (30.0, (double) exponent);
388   else if (exponent > 0)
389     {
390       if (num > DBL_MAX * pow (30.0, (double) -exponent))
391         goto overflow;
392       num *= pow (30.0, (double) exponent);
393     }
394
395   if (neg)
396     return -num;
397   else
398     return num;
399
400  overflow:
401   if (neg)
402     return -DBL_MAX / 10.;
403   else
404     return DBL_MAX / 10;
405
406  lossage:
407   return second_lowest_value;
408 }
409   
410 /* Read an integer and return its value, or NOT_INT on failure. */
411 static int
412 read_int (struct file_handle *h)
413 {
414   double f = read_float (h);
415
416   if (f == second_lowest_value)
417     goto lossage;
418   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
419     lose ((h, _("Bad integer format.")));
420   return f;
421
422  lossage:
423   return NOT_INT;
424 }
425
426 /* Reads a string and returns its value in a static buffer, or NULL on
427    failure.  The buffer can be deallocated by calling with a NULL
428    argument. */
429 static unsigned char *
430 read_string (struct file_handle *h)
431 {
432   struct pfm_fhuser_ext *ext = h->ext;
433   static char *buf;
434   int n;
435   
436   if (h == NULL)
437     {
438       free (buf);
439       buf = NULL;
440       return NULL;
441     }
442   else if (buf == NULL)
443     buf = xmalloc (256);
444
445   n = read_int (h);
446   if (n == NOT_INT)
447     return NULL;
448   if (n < 0 || n > 255)
449     lose ((h, _("Bad string length %d."), n));
450   
451   {
452     int i;
453
454     for (i = 0; i < n; i++)
455       {
456         buf[i] = ext->cc;
457         advance ();
458       }
459   }
460   
461   buf[n] = 0;
462   return buf;
463
464  lossage:
465   return NULL;
466 }
467 \f
468 /* Reads the 464-byte file header. */
469 int
470 read_header (struct file_handle *h)
471 {
472   struct pfm_fhuser_ext *ext = h->ext;
473
474   /* For now at least, just ignore the vanity splash strings. */
475   {
476     int i;
477
478     for (i = 0; i < 200; i++)
479       advance ();
480   }
481   
482   {
483     unsigned char src[256];
484     int trans_temp[256];
485     int i;
486
487     for (i = 0; i < 256; i++)
488       {
489         src[i] = (unsigned char) ext->cc;
490         advance ();
491       }
492
493     for (i = 0; i < 256; i++)
494       trans_temp[i] = -1;
495
496     /* 0 is used to mark untranslatable characters, so we have to mark
497        it specially. */
498     trans_temp[src[64]] = 64;
499     for (i = 0; i < 256; i++)
500       if (trans_temp[src[i]] == -1)
501         trans_temp[src[i]] = i;
502     
503     ext->trans = xmalloc (256);
504     for (i = 0; i < 256; i++)
505       ext->trans[i] = trans_temp[i] == -1 ? 0 : trans_temp[i];
506
507     /* Translate the input buffer. */
508     for (i = 0; i < 80; i++)
509       ext->buf[i] = ext->trans[ext->buf[i]];
510     ext->cc = ext->trans[ext->cc];
511   }
512   
513   {
514     unsigned char sig[8] = {92, 89, 92, 92, 89, 88, 91, 93};
515     int i;
516
517     for (i = 0; i < 8; i++)
518       if (!match (sig[i]))
519         lose ((h, "Missing SPSSPORT signature."));
520   }
521
522   return 1;
523
524  lossage:
525   return 0;
526 }
527
528 /* Reads the version and date info record, as well as product and
529    subproduct identification records if present. */
530 int
531 read_version_data (struct file_handle *h, struct pfm_read_info *inf)
532 {
533   struct pfm_fhuser_ext *ext = h->ext;
534
535   /* Version. */
536   if (!match (74 /* A */))
537     lose ((h, "Unrecognized version code %d.", ext->cc));
538
539   /* Date. */
540   {
541     static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
542     char *date = read_string (h);
543     int i;
544     
545     if (!date)
546       return 0;
547     if (strlen (date) != 8)
548       lose ((h, _("Bad date string length %d."), strlen (date)));
549     for (i = 0; i < 8; i++)
550       {
551         if (date[i] < 64 /* 0 */ || date[i] > 73 /* 9 */)
552           lose ((h, _("Bad character in date.")));
553         if (inf)
554           inf->creation_date[map[i]] = date[i] - 64 /* 0 */ + '0';
555       }
556     if (inf)
557       {
558         inf->creation_date[2] = inf->creation_date[5] = ' ';
559         inf->creation_date[10] = 0;
560       }
561   }
562   
563   /* Time. */
564   {
565     static const int map[] = {0, 1, 3, 4, 6, 7};
566     char *time = read_string (h);
567     int i;
568
569     if (!time)
570       return 0;
571     if (strlen (time) != 6)
572       lose ((h, _("Bad time string length %d."), strlen (time)));
573     for (i = 0; i < 6; i++)
574       {
575         if (time[i] < 64 /* 0 */ || time[i] > 73 /* 9 */)
576           lose ((h, _("Bad character in time.")));
577         if (inf)
578           inf->creation_time[map[i]] = time[i] - 64 /* 0 */ + '0';
579       }
580     if (inf)
581       {
582         inf->creation_time[2] = inf->creation_time[5] = ' ';
583         inf->creation_time[8] = 0;
584       }
585   }
586
587   /* Product. */
588   if (match (65 /* 1 */))
589     {
590       char *product;
591       
592       product = read_string (h);
593       if (product == NULL)
594         return 0;
595       if (inf)
596         strncpy (inf->product, product, 61);
597     }
598   else if (inf)
599     inf->product[0] = 0;
600
601   /* Subproduct. */
602   if (match (67 /* 3 */))
603     {
604       char *subproduct;
605
606       subproduct = read_string (h);
607       if (subproduct == NULL)
608         return 0;
609       if (inf)
610         strncpy (inf->subproduct, subproduct, 61);
611     }
612   else if (inf)
613     inf->subproduct[0] = 0;
614   return 1;
615   
616  lossage:
617   return 0;
618 }
619
620 static int
621 convert_format (struct file_handle *h, int fmt[3], struct fmt_spec *v,
622                 struct variable *vv)
623 {
624   v->type = translate_fmt (fmt[0]);
625   if (v->type == -1)
626     lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
627   v->w = fmt[1];
628   v->d = fmt[2];
629
630   /* FIXME?  Should verify the resulting specifier more thoroughly. */
631
632   if (v->type == -1)
633     lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
634   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))
635     lose ((h, _("%s variable %s has %s format specifier %s."),
636            vv->type == ALPHA ? _("String") : _("Numeric"),
637            vv->name,
638            formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),
639            formats[v->type].name));
640   return 1;
641
642  lossage:
643   return 0;
644 }
645
646 /* Translation table from SPSS character code to this computer's
647    native character code (which is probably ASCII). */
648 static const unsigned char spss2ascii[256] =
649   {
650     "                                                                "
651     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
652     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
653     "                                                                "
654   };
655
656 /* Translate string S into ASCII. */
657 static void
658 asciify (char *s)
659 {
660   for (; *s; s++)
661     *s = spss2ascii[(unsigned char) *s];
662 }
663
664 static int parse_value (struct file_handle *, union value *, struct variable *);
665
666 /* Read information on all the variables.  */
667 static int
668 read_variables (struct file_handle *h)
669 {
670   struct pfm_fhuser_ext *ext = h->ext;
671   char *weight_name = NULL;
672   int i;
673   
674   if (!match (68 /* 4 */))
675     lose ((h, _("Expected variable count record.")));
676   
677   ext->nvars = read_int (h);
678   if (ext->nvars <= 0 || ext->nvars == NOT_INT)
679     lose ((h, _("Invalid number of variables %d."), ext->nvars));
680   ext->vars = xmalloc (sizeof *ext->vars * ext->nvars);
681
682   /* Purpose of this value is unknown.  It is typically 161. */
683   {
684     int x = read_int (h);
685
686     if (x == NOT_INT)
687       goto lossage;
688     if (x != 161)
689       corrupt_msg (h, _("Unexpected flag value %d."), x);
690   }
691
692   ext->dict = dict_create ();
693
694   if (match (70 /* 6 */))
695     {
696       weight_name = read_string (h);
697       if (!weight_name)
698         goto lossage;
699
700       asciify (weight_name);
701       if (strlen (weight_name) > 8) 
702         {
703           corrupt_msg (h, _("Weight variable name (%s) truncated."),
704                        weight_name);
705           weight_name[8] = '\0';
706         }
707     }
708   
709   for (i = 0; i < ext->nvars; i++)
710     {
711       int width;
712       unsigned char *name;
713       int fmt[6];
714       struct variable *v;
715       int j;
716
717       if (!match (71 /* 7 */))
718         lose ((h, _("Expected variable record.")));
719
720       width = read_int (h);
721       if (width == NOT_INT)
722         goto lossage;
723       if (width < 0)
724         lose ((h, _("Invalid variable width %d."), width));
725       ext->vars[i] = width;
726       
727       name = read_string (h);
728       if (name == NULL)
729         goto lossage;
730       for (j = 0; j < 6; j++)
731         {
732           fmt[j] = read_int (h);
733           if (fmt[j] == NOT_INT)
734             goto lossage;
735         }
736
737       /* Verify first character of variable name.
738
739          Weirdly enough, there is no # character in the SPSS portable
740          character set, so we can't check for it. */
741       if (strlen (name) > 8)
742         lose ((h, _("position %d: Variable name has %u characters."),
743                i, strlen (name)));
744       if ((name[0] < 74 /* A */ || name[0] > 125 /* Z */)
745           && name[0] != 152 /* @ */)
746         lose ((h, _("position %d: Variable name begins with invalid "
747                "character."), i));
748       if (name[0] >= 100 /* a */ && name[0] <= 125 /* z */)
749         {
750           corrupt_msg (h, _("position %d: Variable name begins with "
751                             "lowercase letter %c."),
752                        i, name[0] - 100 + 'a');
753           name[0] -= 26 /* a - A */;
754         }
755
756       /* Verify remaining characters of variable name. */
757       for (j = 1; j < (int) strlen (name); j++)
758         {
759           int c = name[j];
760
761           if (c >= 100 /* a */ && c <= 125 /* z */)
762             {
763               corrupt_msg (h, _("position %d: Variable name character %d "
764                                 "is lowercase letter %c."),
765                            i, j + 1, c - 100 + 'a');
766               name[j] -= 26 /* z - Z */;
767             }
768           else if ((c >= 64 /* 0 */ && c <= 99 /* Z */)
769                    || c == 127 /* . */ || c == 152 /* @ */
770                    || c == 136 /* $ */ || c == 146 /* _ */)
771             name[j] = c;
772           else
773             lose ((h, _("position %d: character `\\%03o' is not "
774                         "valid in a variable name."), i, c));
775         }
776
777       asciify (name);
778       if (width < 0 || width > 255)
779         lose ((h, "Bad width %d for variable %s.", width, name));
780
781       v = dict_create_var (ext->dict, name, width);
782       v->get.fv = v->fv;
783       if (v == NULL)
784         lose ((h, _("Duplicate variable name %s."), name));
785       if (!convert_format (h, &fmt[0], &v->print, v))
786         goto lossage;
787       if (!convert_format (h, &fmt[3], &v->write, v))
788         goto lossage;
789
790       /* Range missing values. */
791       if (match (75 /* B */))
792         {
793           v->miss_type = MISSING_RANGE;
794           if (!parse_value (h, &v->missing[0], v)
795               || !parse_value (h, &v->missing[1], v))
796             goto lossage;
797         }
798       else if (match (74 /* A */))
799         {
800           v->miss_type = MISSING_HIGH;
801           if (!parse_value (h, &v->missing[0], v))
802             goto lossage;
803         }
804       else if (match (73 /* 9 */))
805         {
806           v->miss_type = MISSING_LOW;
807           if (!parse_value (h, &v->missing[0], v))
808             goto lossage;
809         }
810
811       /* Single missing values. */
812       while (match (72 /* 8 */))
813         {
814           static const int map_next[MISSING_COUNT] =
815             {
816               MISSING_1, MISSING_2, MISSING_3, -1,
817               MISSING_RANGE_1, MISSING_LOW_1, MISSING_HIGH_1,
818               -1, -1, -1,
819             };
820
821           static const int map_ofs[MISSING_COUNT] = 
822             {
823               -1, 0, 1, 2, -1, -1, -1, 2, 1, 1,
824             };
825
826           v->miss_type = map_next[v->miss_type];
827           if (v->miss_type == -1)
828             lose ((h, _("Bad missing values for %s."), v->name));
829           
830           assert (map_ofs[v->miss_type] != -1);
831           if (!parse_value (h, &v->missing[map_ofs[v->miss_type]], v))
832             goto lossage;
833         }
834
835       if (match (76 /* C */))
836         {
837           char *label = read_string (h);
838           
839           if (label == NULL)
840             goto lossage;
841
842           v->label = xstrdup (label);
843           asciify (v->label);
844         }
845     }
846
847   if (weight_name != NULL) 
848     {
849       struct variable *weight_var = dict_lookup_var (ext->dict, weight_name);
850       if (weight_var == NULL)
851         lose ((h, _("Weighting variable %s not present in dictionary."),
852                weight_name));
853       free (weight_name);
854
855       dict_set_weight (ext->dict, weight_var);
856     }
857
858   return 1;
859
860  lossage:
861   free (weight_name);
862   return 0;
863 }
864
865 /* Parse a value for variable VV into value V.  Returns success. */
866 static int
867 parse_value (struct file_handle *h, union value *v, struct variable *vv)
868 {
869   if (vv->type == ALPHA)
870     {
871       char *mv = read_string (h);
872       int j;
873       
874       if (mv == NULL)
875         return 0;
876
877       strncpy (v->s, mv, 8);
878       for (j = 0; j < 8; j++)
879         if (v->s[j])
880           v->s[j] = spss2ascii[v->s[j]];
881         else
882           /* Value labels are always padded with spaces. */
883           v->s[j] = ' ';
884     }
885   else
886     {
887       v->f = read_float (h);
888       if (v->f == second_lowest_value)
889         return 0;
890     }
891
892   return 1;
893 }
894
895 /* Parse a value label record and return success. */
896 static int
897 read_value_label (struct file_handle *h)
898 {
899   struct pfm_fhuser_ext *ext = h->ext;
900
901   /* Variables. */
902   int nv;
903   struct variable **v;
904
905   /* Labels. */
906   int n_labels;
907
908   int i;
909
910   nv = read_int (h);
911   if (nv == NOT_INT)
912     return 0;
913
914   v = xmalloc (sizeof *v * nv);
915   for (i = 0; i < nv; i++)
916     {
917       char *name = read_string (h);
918       if (name == NULL)
919         goto lossage;
920       asciify (name);
921
922       v[i] = dict_lookup_var (ext->dict, name);
923       if (v[i] == NULL)
924         lose ((h, _("Unknown variable %s while parsing value labels."), name));
925
926       if (v[0]->width != v[i]->width)
927         lose ((h, _("Cannot assign value labels to %s and %s, which "
928                     "have different variable types or widths."),
929                v[0]->name, v[i]->name));
930     }
931
932   n_labels = read_int (h);
933   if (n_labels == NOT_INT)
934     goto lossage;
935
936   for (i = 0; i < n_labels; i++)
937     {
938       union value val;
939       char *label;
940
941       int j;
942       
943       if (!parse_value (h, &val, v[0]))
944         goto lossage;
945       
946       label = read_string (h);
947       if (label == NULL)
948         goto lossage;
949       asciify (label);
950
951       /* Assign the value_label's to each variable. */
952       for (j = 0; j < nv; j++)
953         {
954           struct variable *var = v[j];
955
956           if (!val_labs_replace (var->val_labs, val, label))
957             continue;
958
959           if (var->type == NUMERIC)
960             lose ((h, _("Duplicate label for value %g for variable %s."),
961                    val.f, var->name));
962           else
963             lose ((h, _("Duplicate label for value `%.*s' for variable %s."),
964                    var->width, val.s, var->name));
965         }
966     }
967   free (v);
968   return 1;
969
970  lossage:
971   free (v);
972   return 0;
973 }
974
975 /* Reads one case from portable file H into the value array PERM
976    according to the instuctions given in associated dictionary DICT,
977    which must have the get.fv elements appropriately set.  Returns
978    nonzero only if successful. */
979 int
980 pfm_read_case (struct file_handle *h, union value *perm, struct dictionary *dict)
981 {
982   struct pfm_fhuser_ext *ext = h->ext;
983
984   union value *temp, *tp;
985   int i;
986
987   /* Check for end of file. */
988   if (ext->cc == 99 /* Z */)
989     return 0;
990   
991   /* The first concern is to obtain a full case relative to the data
992      file.  (Cases in the data file have no particular relationship to
993      cases in the active file.) */
994   tp = temp = local_alloc (sizeof *tp * ext->case_size);
995   for (tp = temp, i = 0; i < ext->nvars; i++)
996     if (ext->vars[i] == 0)
997       {
998         tp->f = read_float (h);
999         if (tp->f == second_lowest_value)
1000           goto unexpected_eof;
1001         tp++;
1002       }
1003     else
1004       {
1005         char *s = read_string (h);
1006         if (s == NULL)
1007           goto unexpected_eof;
1008         asciify (s);
1009           
1010         st_bare_pad_copy (tp->s, s, ext->vars[i]);
1011         tp += DIV_RND_UP (ext->vars[i], MAX_SHORT_STRING);
1012       }
1013
1014   /* Translate a case in data file format to a case in active file
1015      format. */
1016   for (i = 0; i < dict_get_var_cnt (dict); i++)
1017     {
1018       struct variable *v = dict_get_var (dict, i);
1019
1020       if (v->get.fv == -1)
1021         continue;
1022       
1023       if (v->type == NUMERIC)
1024         perm[v->fv].f = temp[v->get.fv].f;
1025       else
1026         memcpy (&perm[v->fv].s, &temp[v->get.fv], v->width);
1027     }
1028
1029   local_free (temp);
1030   return 1;
1031
1032  unexpected_eof:
1033   lose ((h, _("End of file midway through case.")));
1034
1035  lossage:
1036   local_free (temp);
1037   return 0;
1038 }
1039
1040 static struct fh_ext_class pfm_r_class =
1041 {
1042   5,
1043   N_("reading as a portable file"),
1044   pfm_close,
1045 };