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