Fixed some issues with internationalisation
[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   __attribute__ ((format (printf, 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   if (fmt[0] < 0
621       || (size_t) fmt[0] >= sizeof translate_fmt / sizeof *translate_fmt)
622     lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
623
624   v->type = translate_fmt[fmt[0]];
625   v->w = fmt[1];
626   v->d = fmt[2];
627
628   /* FIXME?  Should verify the resulting specifier more thoroughly. */
629
630   if (v->type == -1)
631     lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
632   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))
633     lose ((h, _("%s variable %s has %s format specifier %s."),
634            vv->type == ALPHA ? _("String") : _("Numeric"),
635            vv->name,
636            formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),
637            formats[v->type].name));
638   return 1;
639
640  lossage:
641   return 0;
642 }
643
644 /* Translation table from SPSS character code to this computer's
645    native character code (which is probably ASCII). */
646 static const unsigned char spss2ascii[256] =
647   {
648     "                                                                "
649     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
650     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
651     "                                                                "
652   };
653
654 /* Translate string S into ASCII. */
655 static void
656 asciify (char *s)
657 {
658   for (; *s; s++)
659     *s = spss2ascii[(unsigned char) *s];
660 }
661
662 static int parse_value (struct file_handle *, union value *, struct variable *);
663
664 /* Read information on all the variables.  */
665 static int
666 read_variables (struct file_handle *h)
667 {
668   struct pfm_fhuser_ext *ext = h->ext;
669   char *weight_name = NULL;
670   int i;
671   
672   if (!match (68 /* 4 */))
673     lose ((h, _("Expected variable count record.")));
674   
675   ext->nvars = read_int (h);
676   if (ext->nvars <= 0 || ext->nvars == NOT_INT)
677     lose ((h, _("Invalid number of variables %d."), ext->nvars));
678   ext->vars = xmalloc (sizeof *ext->vars * ext->nvars);
679
680   /* Purpose of this value is unknown.  It is typically 161. */
681   {
682     int x = read_int (h);
683
684     if (x == NOT_INT)
685       goto lossage;
686     if (x != 161)
687       corrupt_msg (h, _("Unexpected flag value %d."), x);
688   }
689
690   ext->dict = dict_create ();
691
692   if (match (70 /* 6 */))
693     {
694       weight_name = read_string (h);
695       if (!weight_name)
696         goto lossage;
697
698       asciify (weight_name);
699       if (strlen (weight_name) > 8) 
700         {
701           corrupt_msg (h, _("Weight variable name (%s) truncated."),
702                        weight_name);
703           weight_name[8] = '\0';
704         }
705     }
706   
707   for (i = 0; i < ext->nvars; i++)
708     {
709       int width;
710       unsigned char *name;
711       int fmt[6];
712       struct variable *v;
713       int j;
714
715       if (!match (71 /* 7 */))
716         lose ((h, _("Expected variable record.")));
717
718       width = read_int (h);
719       if (width == NOT_INT)
720         goto lossage;
721       if (width < 0)
722         lose ((h, _("Invalid variable width %d."), width));
723       ext->vars[i] = width;
724       
725       name = read_string (h);
726       if (name == NULL)
727         goto lossage;
728       for (j = 0; j < 6; j++)
729         {
730           fmt[j] = read_int (h);
731           if (fmt[j] == NOT_INT)
732             goto lossage;
733         }
734
735       /* Verify first character of variable name.
736
737          Weirdly enough, there is no # character in the SPSS portable
738          character set, so we can't check for it. */
739       if (strlen (name) > 8)
740         lose ((h, _("position %d: Variable name has %u characters."),
741                i, strlen (name)));
742       if ((name[0] < 74 /* A */ || name[0] > 125 /* Z */)
743           && name[0] != 152 /* @ */)
744         lose ((h, _("position %d: Variable name begins with invalid "
745                "character."), i));
746       if (name[0] >= 100 /* a */ && name[0] <= 125 /* z */)
747         {
748           corrupt_msg (h, _("position %d: Variable name begins with "
749                             "lowercase letter %c."),
750                        i, name[0] - 100 + 'a');
751           name[0] -= 26 /* a - A */;
752         }
753
754       /* Verify remaining characters of variable name. */
755       for (j = 1; j < (int) strlen (name); j++)
756         {
757           int c = name[j];
758
759           if (c >= 100 /* a */ && c <= 125 /* z */)
760             {
761               corrupt_msg (h, _("position %d: Variable name character %d "
762                                 "is lowercase letter %c."),
763                            i, j + 1, c - 100 + 'a');
764               name[j] -= 26 /* z - Z */;
765             }
766           else if ((c >= 64 /* 0 */ && c <= 99 /* Z */)
767                    || c == 127 /* . */ || c == 152 /* @ */
768                    || c == 136 /* $ */ || c == 146 /* _ */)
769             name[j] = c;
770           else
771             lose ((h, _("position %d: character `\\%03o' is not "
772                         "valid in a variable name."), i, c));
773         }
774
775       asciify (name);
776       if (width < 0 || width > 255)
777         lose ((h, "Bad width %d for variable %s.", width, name));
778
779       v = dict_create_var (ext->dict, name, width);
780       v->get.fv = v->fv;
781       if (v == NULL)
782         lose ((h, _("Duplicate variable name %s."), name));
783       if (!convert_format (h, &fmt[0], &v->print, v))
784         goto lossage;
785       if (!convert_format (h, &fmt[3], &v->write, v))
786         goto lossage;
787
788       /* Range missing values. */
789       if (match (75 /* B */))
790         {
791           v->miss_type = MISSING_RANGE;
792           if (!parse_value (h, &v->missing[0], v)
793               || !parse_value (h, &v->missing[1], v))
794             goto lossage;
795         }
796       else if (match (74 /* A */))
797         {
798           v->miss_type = MISSING_HIGH;
799           if (!parse_value (h, &v->missing[0], v))
800             goto lossage;
801         }
802       else if (match (73 /* 9 */))
803         {
804           v->miss_type = MISSING_LOW;
805           if (!parse_value (h, &v->missing[0], v))
806             goto lossage;
807         }
808
809       /* Single missing values. */
810       while (match (72 /* 8 */))
811         {
812           static const int map_next[MISSING_COUNT] =
813             {
814               MISSING_1, MISSING_2, MISSING_3, -1,
815               MISSING_RANGE_1, MISSING_LOW_1, MISSING_HIGH_1,
816               -1, -1, -1,
817             };
818
819           static const int map_ofs[MISSING_COUNT] = 
820             {
821               -1, 0, 1, 2, -1, -1, -1, 2, 1, 1,
822             };
823
824           v->miss_type = map_next[v->miss_type];
825           if (v->miss_type == -1)
826             lose ((h, _("Bad missing values for %s."), v->name));
827           
828           assert (map_ofs[v->miss_type] != -1);
829           if (!parse_value (h, &v->missing[map_ofs[v->miss_type]], v))
830             goto lossage;
831         }
832
833       if (match (76 /* C */))
834         {
835           char *label = read_string (h);
836           
837           if (label == NULL)
838             goto lossage;
839
840           v->label = xstrdup (label);
841           asciify (v->label);
842         }
843     }
844
845   if (weight_name != NULL) 
846     {
847       struct variable *weight_var = dict_lookup_var (ext->dict, weight_name);
848       if (weight_var == NULL)
849         lose ((h, _("Weighting variable %s not present in dictionary."),
850                weight_name));
851       free (weight_name);
852
853       dict_set_weight (ext->dict, weight_var);
854     }
855
856   return 1;
857
858  lossage:
859   free (weight_name);
860   return 0;
861 }
862
863 /* Parse a value for variable VV into value V.  Returns success. */
864 static int
865 parse_value (struct file_handle *h, union value *v, struct variable *vv)
866 {
867   if (vv->type == ALPHA)
868     {
869       char *mv = read_string (h);
870       int j;
871       
872       if (mv == NULL)
873         return 0;
874
875       strncpy (v->s, mv, 8);
876       for (j = 0; j < 8; j++)
877         if (v->s[j])
878           v->s[j] = spss2ascii[v->s[j]];
879         else
880           /* Value labels are always padded with spaces. */
881           v->s[j] = ' ';
882     }
883   else
884     {
885       v->f = read_float (h);
886       if (v->f == second_lowest_value)
887         return 0;
888     }
889
890   return 1;
891 }
892
893 /* Parse a value label record and return success. */
894 static int
895 read_value_label (struct file_handle *h)
896 {
897   struct pfm_fhuser_ext *ext = h->ext;
898
899   /* Variables. */
900   int nv;
901   struct variable **v;
902
903   /* Labels. */
904   int n_labels;
905
906   int i;
907
908   nv = read_int (h);
909   if (nv == NOT_INT)
910     return 0;
911
912   v = xmalloc (sizeof *v * nv);
913   for (i = 0; i < nv; i++)
914     {
915       char *name = read_string (h);
916       if (name == NULL)
917         goto lossage;
918       asciify (name);
919
920       v[i] = dict_lookup_var (ext->dict, name);
921       if (v[i] == NULL)
922         lose ((h, _("Unknown variable %s while parsing value labels."), name));
923
924       if (v[0]->width != v[i]->width)
925         lose ((h, _("Cannot assign value labels to %s and %s, which "
926                     "have different variable types or widths."),
927                v[0]->name, v[i]->name));
928     }
929
930   n_labels = read_int (h);
931   if (n_labels == NOT_INT)
932     goto lossage;
933
934   for (i = 0; i < n_labels; i++)
935     {
936       union value val;
937       char *label;
938
939       int j;
940       
941       if (!parse_value (h, &val, v[0]))
942         goto lossage;
943       
944       label = read_string (h);
945       if (label == NULL)
946         goto lossage;
947       asciify (label);
948
949       /* Assign the value_label's to each variable. */
950       for (j = 0; j < nv; j++)
951         {
952           struct variable *var = v[j];
953
954           if (!val_labs_replace (var->val_labs, val, label))
955             continue;
956
957           if (var->type == NUMERIC)
958             lose ((h, _("Duplicate label for value %g for variable %s."),
959                    val.f, var->name));
960           else
961             lose ((h, _("Duplicate label for value `%.*s' for variable %s."),
962                    var->width, val.s, var->name));
963         }
964     }
965   free (v);
966   return 1;
967
968  lossage:
969   free (v);
970   return 0;
971 }
972
973 /* Reads one case from portable file H into the value array PERM
974    according to the instuctions given in associated dictionary DICT,
975    which must have the get.fv elements appropriately set.  Returns
976    nonzero only if successful. */
977 int
978 pfm_read_case (struct file_handle *h, union value *perm, struct dictionary *dict)
979 {
980   struct pfm_fhuser_ext *ext = h->ext;
981
982   union value *temp, *tp;
983   int i;
984
985   /* Check for end of file. */
986   if (ext->cc == 99 /* Z */)
987     return 0;
988   
989   /* The first concern is to obtain a full case relative to the data
990      file.  (Cases in the data file have no particular relationship to
991      cases in the active file.) */
992   tp = temp = local_alloc (sizeof *tp * ext->case_size);
993   for (tp = temp, i = 0; i < ext->nvars; i++)
994     if (ext->vars[i] == 0)
995       {
996         tp->f = read_float (h);
997         if (tp->f == second_lowest_value)
998           goto unexpected_eof;
999         tp++;
1000       }
1001     else
1002       {
1003         char *s = read_string (h);
1004         if (s == NULL)
1005           goto unexpected_eof;
1006         asciify (s);
1007           
1008         st_bare_pad_copy (tp->s, s, ext->vars[i]);
1009         tp += DIV_RND_UP (ext->vars[i], MAX_SHORT_STRING);
1010       }
1011
1012   /* Translate a case in data file format to a case in active file
1013      format. */
1014   for (i = 0; i < dict_get_var_cnt (dict); i++)
1015     {
1016       struct variable *v = dict_get_var (dict, i);
1017
1018       if (v->get.fv == -1)
1019         continue;
1020       
1021       if (v->type == NUMERIC)
1022         perm[v->fv].f = temp[v->get.fv].f;
1023       else
1024         memcpy (&perm[v->fv].s, &temp[v->get.fv], v->width);
1025     }
1026
1027   local_free (temp);
1028   return 1;
1029
1030  unexpected_eof:
1031   lose ((h, _("End of file midway through case.")));
1032
1033  lossage:
1034   local_free (temp);
1035   return 0;
1036 }
1037
1038 static struct fh_ext_class pfm_r_class =
1039 {
1040   5,
1041   N_("reading as a portable file"),
1042   pfm_close,
1043 };