Patch #6258.
[pspp-builds.git] / src / data / por-file-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include "por-file-reader.h"
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <math.h>
23 #include <setjmp.h>
24 #include <stdarg.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <data/casereader-provider.h>
30 #include <data/casereader.h>
31 #include <data/dictionary.h>
32 #include <data/file-handle-def.h>
33 #include <data/file-name.h>
34 #include <data/format.h>
35 #include <data/missing-values.h>
36 #include <data/value-labels.h>
37 #include <data/variable.h>
38 #include <libpspp/compiler.h>
39 #include <libpspp/hash.h>
40 #include <libpspp/message.h>
41 #include <libpspp/misc.h>
42 #include <libpspp/pool.h>
43 #include <libpspp/str.h>
44
45 #include "xalloc.h"
46
47 #include "gettext.h"
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) (msgid)
50
51 /* portable_to_local[PORTABLE] translates the given portable
52    character into the local character set. */
53 static const char portable_to_local[256] =
54   {
55     "                                                                "
56     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
57     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
58     "                                                                "
59   };
60
61 /* Portable file reader. */
62 struct pfm_reader
63   {
64     struct pool *pool;          /* All the portable file state. */
65
66     jmp_buf bail_out;           /* longjmp() target for error handling. */
67
68     struct file_handle *fh;     /* File handle. */
69     struct fh_lock *lock;       /* Read lock for file. */
70     FILE *file;                 /* File stream. */
71     int line_length;            /* Number of characters so far on this line. */
72     char cc;                    /* Current character. */
73     char *trans;                /* 256-byte character set translation table. */
74     int var_cnt;                /* Number of variables. */
75     int weight_index;           /* 0-based index of weight variable, or -1. */
76     int *widths;                /* Variable widths, 0 for numeric. */
77     size_t value_cnt;           /* Number of `value's per case. */
78     bool ok;                    /* Set false on I/O error. */
79   };
80
81 static struct casereader_class por_file_casereader_class;
82
83 static void
84 error (struct pfm_reader *r, const char *msg,...)
85      PRINTF_FORMAT (2, 3)
86      NO_RETURN;
87
88 /* Displays MSG as an error message and aborts reading the
89    portable file via longjmp(). */
90 static void
91 error (struct pfm_reader *r, const char *msg, ...)
92 {
93   struct msg m;
94   struct string text;
95   va_list args;
96
97   ds_init_empty (&text);
98   ds_put_format (&text, _("portable file %s corrupt at offset 0x%lx: "),
99                  fh_get_file_name (r->fh), ftell (r->file));
100   va_start (args, msg);
101   ds_put_vformat (&text, msg, args);
102   va_end (args);
103
104   m.category = MSG_GENERAL;
105   m.severity = MSG_ERROR;
106   m.where.file_name = NULL;
107   m.where.line_number = 0;
108   m.text = ds_cstr (&text);
109
110   msg_emit (&m);
111
112   r->ok = false;
113
114   longjmp (r->bail_out, 1);
115 }
116
117 /* Displays MSG as an warning for the current position in
118    portable file reader R. */
119 static void
120 warning (struct pfm_reader *r, const char *msg, ...)
121 {
122   struct msg m;
123   struct string text;
124   va_list args;
125
126   ds_init_empty (&text);
127   ds_put_format (&text, _("reading portable file %s at offset 0x%lx: "),
128                  fh_get_file_name (r->fh), ftell (r->file));
129   va_start (args, msg);
130   ds_put_vformat (&text, msg, args);
131   va_end (args);
132
133   m.category = MSG_GENERAL;
134   m.severity = MSG_WARNING;
135   m.where.file_name = NULL;
136   m.where.line_number = 0;
137   m.text = ds_cstr (&text);
138
139   msg_emit (&m);
140 }
141
142 /* Close and destroy R.
143    Returns false if an error was detected on R, true otherwise. */
144 static bool
145 close_reader (struct pfm_reader *r)
146 {
147   bool ok;
148   if (r == NULL)
149     return true;
150
151   if (r->file)
152     {
153       if (fn_close (fh_get_file_name (r->fh), r->file) == EOF)
154         {
155           msg (ME, _("Error closing portable file \"%s\": %s."),
156                fh_get_file_name (r->fh), strerror (errno));
157           r->ok = false;
158         }
159       r->file = NULL;
160     }
161
162   fh_unlock (r->lock);
163   fh_unref (r->fh);
164
165   ok = r->ok;
166   pool_destroy (r->pool);
167
168   return ok;
169 }
170
171 /* Closes portable file reader R, after we're done with it. */
172 static void
173 por_file_casereader_destroy (struct casereader *reader, void *r_)
174 {
175   struct pfm_reader *r = r_;
176   if (!close_reader (r))
177     casereader_force_error (reader);
178 }
179
180 /* Read a single character into cur_char.  */
181 static void
182 advance (struct pfm_reader *r)
183 {
184   int c;
185
186   /* Read the next character from the file.
187      Ignore carriage returns entirely.
188      Mostly ignore new-lines, but if a new-line occurs before the
189      line has reached 80 bytes in length, then treat the
190      "missing" bytes as spaces. */
191   for (;;)
192     {
193       while ((c = getc (r->file)) == '\r')
194         continue;
195       if (c != '\n')
196         break;
197
198       if (r->line_length < 80)
199         {
200           c = ' ';
201           ungetc ('\n', r->file);
202           break;
203         }
204       r->line_length = 0;
205     }
206   if (c == EOF)
207     error (r, _("unexpected end of file"));
208
209   if (r->trans != NULL)
210     c = r->trans[c];
211   r->cc = c;
212   r->line_length++;
213 }
214
215 /* Skip a single character if present, and return whether it was
216    skipped. */
217 static inline bool
218 match (struct pfm_reader *r, int c)
219 {
220   if (r->cc == c)
221     {
222       advance (r);
223       return true;
224     }
225   else
226     return false;
227 }
228
229 static void read_header (struct pfm_reader *);
230 static void read_version_data (struct pfm_reader *, struct pfm_read_info *);
231 static void read_variables (struct pfm_reader *, struct dictionary *);
232 static void read_value_label (struct pfm_reader *, struct dictionary *);
233 static void read_documents (struct pfm_reader *, struct dictionary *);
234
235 /* Reads the dictionary from file with handle H, and returns it in a
236    dictionary structure.  This dictionary may be modified in order to
237    rename, reorder, and delete variables, etc. */
238 struct casereader *
239 pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
240                  struct pfm_read_info *info)
241 {
242   struct pool *volatile pool = NULL;
243   struct pfm_reader *volatile r = NULL;
244
245   *dict = dict_create ();
246
247   /* Create and initialize reader. */
248   pool = pool_create ();
249   r = pool_alloc (pool, sizeof *r);
250   r->pool = pool;
251   r->fh = fh_ref (fh);
252   r->lock = NULL;
253   r->file = NULL;
254   r->line_length = 0;
255   r->weight_index = -1;
256   r->trans = NULL;
257   r->var_cnt = 0;
258   r->widths = NULL;
259   r->value_cnt = 0;
260   r->ok = true;
261   if (setjmp (r->bail_out))
262     goto error;
263
264   /* Lock file. */
265   /* TRANSLATORS: this fragment will be interpolated into
266      messages in fh_lock() that identify types of files. */
267   r->lock = fh_lock (fh, FH_REF_FILE, N_("portable file"), FH_ACC_READ, false);
268   if (r->lock == NULL)
269     goto error;
270
271   /* Open file. */
272   r->file = fn_open (fh_get_file_name (r->fh), "rb");
273   if (r->file == NULL)
274     {
275       msg (ME, _("An error occurred while opening \"%s\" for reading "
276                  "as a portable file: %s."),
277            fh_get_file_name (r->fh), strerror (errno));
278       goto error;
279     }
280
281   /* Read header, version, date info, product id, variables. */
282   read_header (r);
283   read_version_data (r, info);
284   read_variables (r, *dict);
285
286   /* Read value labels. */
287   while (match (r, 'D'))
288     read_value_label (r, *dict);
289
290   /* Read documents. */
291   if (match (r, 'E'))
292     read_documents (r, *dict);
293
294   /* Check that we've made it to the data. */
295   if (!match (r, 'F'))
296     error (r, _("Data record expected."));
297
298   r->value_cnt = dict_get_next_value_idx (*dict);
299   return casereader_create_sequential (NULL, r->value_cnt, CASENUMBER_MAX,
300                                        &por_file_casereader_class, r);
301
302  error:
303   close_reader (r);
304   dict_destroy (*dict);
305   *dict = NULL;
306   return NULL;
307 }
308 \f
309 /* Returns the value of base-30 digit C,
310    or -1 if C is not a base-30 digit. */
311 static int
312 base_30_value (unsigned char c)
313 {
314   static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST";
315   const char *p = strchr (base_30_digits, c);
316   return p != NULL ? p - base_30_digits : -1;
317 }
318
319 /* Read a floating point value and return its value. */
320 static double
321 read_float (struct pfm_reader *r)
322 {
323   double num = 0.;
324   int exponent = 0;
325   bool got_dot = false;         /* Seen a decimal point? */
326   bool got_digit = false;       /* Seen any digits? */
327   bool negative = false;        /* Number is negative? */
328
329   /* Skip leading spaces. */
330   while (match (r, ' '))
331     continue;
332
333   /* `*' indicates system-missing. */
334   if (match (r, '*'))
335     {
336       advance (r);      /* Probably a dot (.) but doesn't appear to matter. */
337       return SYSMIS;
338     }
339
340   negative = match (r, '-');
341   for (;;)
342     {
343       int digit = base_30_value (r->cc);
344       if (digit != -1)
345         {
346           got_digit = true;
347
348           /* Make sure that multiplication by 30 will not overflow.  */
349           if (num > DBL_MAX * (1. / 30.))
350             /* The value of the digit doesn't matter, since we have already
351                gotten as many digits as can be represented in a `double'.
352                This doesn't necessarily mean the result will overflow.
353                The exponent may reduce it to within range.
354
355                We just need to record that there was another
356                digit so that we can multiply by 10 later.  */
357             ++exponent;
358           else
359             num = (num * 30.0) + digit;
360
361           /* Keep track of the number of digits after the decimal point.
362              If we just divided by 30 here, we would lose precision.  */
363           if (got_dot)
364             --exponent;
365         }
366       else if (!got_dot && r->cc == '.')
367         /* Record that we have found the decimal point.  */
368         got_dot = 1;
369       else
370         /* Any other character terminates the number.  */
371         break;
372
373       advance (r);
374     }
375
376   /* Check that we had some digits. */
377   if (!got_digit)
378     error (r, _("Number expected."));
379
380   /* Get exponent if any. */
381   if (r->cc == '+' || r->cc == '-')
382     {
383       long int exp = 0;
384       bool negative_exponent = r->cc == '-';
385       int digit;
386
387       for (advance (r); (digit = base_30_value (r->cc)) != -1; advance (r))
388         {
389           if (exp > LONG_MAX / 30)
390             {
391               exp = LONG_MAX;
392               break;
393             }
394           exp = exp * 30 + digit;
395         }
396
397       /* We don't check whether there were actually any digits, but we
398          probably should. */
399       if (negative_exponent)
400         exp = -exp;
401       exponent += exp;
402     }
403
404   /* Numbers must end with `/'. */
405   if (!match (r, '/'))
406     error (r, _("Missing numeric terminator."));
407
408   /* Multiply `num' by 30 to the `exponent' power, checking for
409      overflow.  */
410   if (exponent < 0)
411     num *= pow (30.0, (double) exponent);
412   else if (exponent > 0)
413     {
414       if (num > DBL_MAX * pow (30.0, (double) -exponent))
415         num = DBL_MAX;
416       else
417         num *= pow (30.0, (double) exponent);
418     }
419
420   return negative ? -num : num;
421 }
422
423 /* Read an integer and return its value. */
424 static int
425 read_int (struct pfm_reader *r)
426 {
427   double f = read_float (r);
428   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
429     error (r, _("Invalid integer."));
430   return f;
431 }
432
433 /* Reads a string into BUF, which must have room for 256
434    characters. */
435 static void
436 read_string (struct pfm_reader *r, char *buf)
437 {
438   int n = read_int (r);
439   if (n < 0 || n > 255)
440     error (r, _("Bad string length %d."), n);
441
442   while (n-- > 0)
443     {
444       *buf++ = r->cc;
445       advance (r);
446     }
447   *buf = '\0';
448 }
449
450 /* Reads a string and returns a copy of it allocated from R's
451    pool. */
452 static char *
453 read_pool_string (struct pfm_reader *r)
454 {
455   char string[256];
456   read_string (r, string);
457   return pool_strdup (r->pool, string);
458 }
459 \f
460 /* Reads the 464-byte file header. */
461 static void
462 read_header (struct pfm_reader *r)
463 {
464   char *trans;
465   int i;
466
467   /* Read and ignore vanity splash strings. */
468   for (i = 0; i < 200; i++)
469     advance (r);
470
471   /* Skip the first 64 characters of the translation table.
472      We don't care about these.  They are probably all set to
473      '0', marking them as untranslatable, and that would screw
474      up our actual translation of the real '0'. */
475   for (i = 0; i < 64; i++)
476     advance (r);
477
478   /* Read the rest of the translation table. */
479   trans = pool_malloc (r->pool, 256);
480   memset (trans, 0, 256);
481   for (; i < 256; i++)
482     {
483       unsigned char c;
484
485       advance (r);
486
487       c = r->cc;
488       if (trans[c] == 0)
489         trans[c] = portable_to_local[i];
490     }
491
492   /* Set up the translation table, then read the first
493      translated character. */
494   r->trans = trans;
495   advance (r);
496
497   /* Skip and verify signature. */
498   for (i = 0; i < 8; i++)
499     if (!match (r, "SPSSPORT"[i]))
500       {
501         msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh));
502         longjmp (r->bail_out, 1);
503       }
504 }
505
506 /* Reads the version and date info record, as well as product and
507    subproduct identification records if present. */
508 static void
509 read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
510 {
511   static char empty_string[] = "";
512   char *date, *time, *product, *author, *subproduct;
513   int i;
514
515   /* Read file. */
516   if (!match (r, 'A'))
517     error (r, _("Unrecognized version code `%c'."), r->cc);
518   date = read_pool_string (r);
519   time = read_pool_string (r);
520   product = match (r, '1') ? read_pool_string (r) : empty_string;
521   author = match (r, '2') ? read_pool_string (r) : empty_string;
522   subproduct = match (r, '3') ? read_pool_string (r) : empty_string;
523
524   /* Validate file. */
525   if (strlen (date) != 8)
526     error (r, _("Bad date string length %zu."), strlen (date));
527   if (strlen (time) != 6)
528     error (r, _("Bad time string length %zu."), strlen (time));
529
530   /* Save file info. */
531   if (info != NULL)
532     {
533       /* Date. */
534       for (i = 0; i < 8; i++)
535         {
536           static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
537           info->creation_date[map[i]] = date[i];
538         }
539       info->creation_date[2] = info->creation_date[5] = ' ';
540       info->creation_date[10] = 0;
541
542       /* Time. */
543       for (i = 0; i < 6; i++)
544         {
545           static const int map[] = {0, 1, 3, 4, 6, 7};
546           info->creation_time[map[i]] = time[i];
547         }
548       info->creation_time[2] = info->creation_time[5] = ' ';
549       info->creation_time[8] = 0;
550
551       /* Product. */
552       str_copy_trunc (info->product, sizeof info->product, product);
553       str_copy_trunc (info->subproduct, sizeof info->subproduct, subproduct);
554     }
555 }
556
557 /* Translates a format specification read from portable file R as
558    the three integers INTS into a normal format specifier FORMAT,
559    checking that the format is appropriate for variable V. */
560 static struct fmt_spec
561 convert_format (struct pfm_reader *r, const int portable_format[3],
562                 struct variable *v, bool *report_error)
563 {
564   struct fmt_spec format;
565   bool ok;
566
567   if (!fmt_from_io (portable_format[0], &format.type))
568     {
569       if (*report_error)
570         warning (r, _("%s: Bad format specifier byte (%d).  Variable "
571                       "will be assigned a default format."),
572                  var_get_name (v), portable_format[0]);
573       goto assign_default;
574     }
575
576   format.w = portable_format[1];
577   format.d = portable_format[2];
578
579   msg_disable ();
580   ok = (fmt_check_output (&format)
581         && fmt_check_width_compat (&format, var_get_width (v)));
582   msg_enable ();
583
584   if (!ok)
585     {
586       if (*report_error)
587         {
588           char fmt_string[FMT_STRING_LEN_MAX + 1];
589           fmt_to_string (&format, fmt_string);
590           if (var_is_numeric (v))
591             warning (r, _("Numeric variable %s has invalid format "
592                           "specifier %s."),
593                      var_get_name (v), fmt_string);
594           else
595             warning (r, _("String variable %s with width %d has "
596                           "invalid format specifier %s."),
597                      var_get_name (v), var_get_width (v), fmt_string);
598         }
599       goto assign_default;
600     }
601
602   return format;
603
604 assign_default:
605   *report_error = false;
606   return fmt_default_for_width (var_get_width (v));
607 }
608
609 static union value parse_value (struct pfm_reader *, struct variable *);
610
611 /* Read information on all the variables.  */
612 static void
613 read_variables (struct pfm_reader *r, struct dictionary *dict)
614 {
615   char *weight_name = NULL;
616   int i;
617
618   if (!match (r, '4'))
619     error (r, _("Expected variable count record."));
620
621   r->var_cnt = read_int (r);
622   if (r->var_cnt <= 0)
623     error (r, _("Invalid number of variables %d."), r->var_cnt);
624   r->widths = pool_nalloc (r->pool, r->var_cnt, sizeof *r->widths);
625
626   /* Purpose of this value is unknown.  It is typically 161. */
627   read_int (r);
628
629   if (match (r, '6'))
630     {
631       weight_name = read_pool_string (r);
632       if (strlen (weight_name) > SHORT_NAME_LEN)
633         error (r, _("Weight variable name (%s) truncated."), weight_name);
634     }
635
636   for (i = 0; i < r->var_cnt; i++)
637     {
638       int width;
639       char name[256];
640       int fmt[6];
641       struct variable *v;
642       struct missing_values miss;
643       struct fmt_spec print, write;
644       bool report_error = true;
645       int j;
646
647       if (!match (r, '7'))
648         error (r, _("Expected variable record."));
649
650       width = read_int (r);
651       if (width < 0)
652         error (r, _("Invalid variable width %d."), width);
653       r->widths[i] = width;
654
655       read_string (r, name);
656       for (j = 0; j < 6; j++)
657         fmt[j] = read_int (r);
658
659       if (!var_is_valid_name (name, false) || *name == '#' || *name == '$')
660         error (r, _("Invalid variable name `%s' in position %d."), name, i);
661       str_uppercase (name);
662
663       if (width < 0 || width > 255)
664         error (r, _("Bad width %d for variable %s."), width, name);
665
666       v = dict_create_var (dict, name, width);
667       if (v == NULL)
668         {
669           int i;
670           for (i = 1; i < 100000; i++)
671             {
672               char try_name[LONG_NAME_LEN + 1];
673               sprintf (try_name, "%.*s_%d", LONG_NAME_LEN - 6, name, i);
674               v = dict_create_var (dict, try_name, width);
675               if (v != NULL)
676                 break;
677             }
678           if (v == NULL)
679             error (r, _("Duplicate variable name %s in position %d."), name, i);
680           warning (r, _("Duplicate variable name %s in position %d renamed "
681                         "to %s."), name, i, var_get_name (v));
682         }
683
684       print = convert_format (r, &fmt[0], v, &report_error);
685       write = convert_format (r, &fmt[3], v, &report_error);
686       var_set_print_format (v, &print);
687       var_set_write_format (v, &write);
688
689       /* Range missing values. */
690       mv_init (&miss, var_get_width (v));
691       if (match (r, 'B'))
692         {
693           double x = read_float (r);
694           double y = read_float (r);
695           mv_add_num_range (&miss, x, y);
696         }
697       else if (match (r, 'A'))
698         mv_add_num_range (&miss, read_float (r), HIGHEST);
699       else if (match (r, '9'))
700         mv_add_num_range (&miss, LOWEST, read_float (r));
701
702       /* Single missing values. */
703       while (match (r, '8'))
704         {
705           union value value = parse_value (r, v);
706           mv_add_value (&miss, &value);
707         }
708
709       var_set_missing_values (v, &miss);
710
711       if (match (r, 'C'))
712         {
713           char label[256];
714           read_string (r, label);
715           var_set_label (v, label);
716         }
717     }
718
719   if (weight_name != NULL)
720     {
721       struct variable *weight_var = dict_lookup_var (dict, weight_name);
722       if (weight_var == NULL)
723         error (r, _("Weighting variable %s not present in dictionary."),
724                weight_name);
725
726       dict_set_weight (dict, weight_var);
727     }
728 }
729
730 /* Parse a value for variable VV into value V. */
731 static union value
732 parse_value (struct pfm_reader *r, struct variable *vv)
733 {
734   union value v;
735
736   if (var_is_alpha (vv))
737     {
738       char string[256];
739       read_string (r, string);
740       buf_copy_str_rpad (v.s, 8, string);
741     }
742   else
743     v.f = read_float (r);
744
745   return v;
746 }
747
748 /* Parse a value label record and return success. */
749 static void
750 read_value_label (struct pfm_reader *r, struct dictionary *dict)
751 {
752   /* Variables. */
753   int nv;
754   struct variable **v;
755
756   /* Labels. */
757   int n_labels;
758
759   int i;
760
761   nv = read_int (r);
762   v = pool_nalloc (r->pool, nv, sizeof *v);
763   for (i = 0; i < nv; i++)
764     {
765       char name[256];
766       read_string (r, name);
767
768       v[i] = dict_lookup_var (dict, name);
769       if (v[i] == NULL)
770         error (r, _("Unknown variable %s while parsing value labels."), name);
771
772       if (var_get_type (v[0]) != var_get_type (v[i]))
773         error (r, _("Cannot assign value labels to %s and %s, which "
774                     "have different variable types."),
775                var_get_name (v[0]), var_get_name (v[i]));
776     }
777
778   n_labels = read_int (r);
779   for (i = 0; i < n_labels; i++)
780     {
781       union value val;
782       char label[256];
783       int j;
784
785       val = parse_value (r, v[0]);
786       read_string (r, label);
787
788       /* Assign the value label to each variable. */
789       for (j = 0; j < nv; j++)
790         {
791           struct variable *var = v[j];
792
793           if (!var_is_long_string (var))
794             var_replace_value_label (var, &val, label);
795         }
796     }
797 }
798
799 /* Reads a set of documents from portable file R into DICT. */
800 static void
801 read_documents (struct pfm_reader *r, struct dictionary *dict)
802 {
803   int line_cnt;
804   int i;
805
806   line_cnt = read_int (r);
807   for (i = 0; i < line_cnt; i++)
808     {
809       char line[256];
810       read_string (r, line);
811       dict_add_document_line (dict, line);
812     }
813 }
814
815 /* Reads one case from portable file R into C. */
816 static bool
817 por_file_casereader_read (struct casereader *reader, void *r_, struct ccase *c)
818 {
819   struct pfm_reader *r = r_;
820   size_t i;
821   size_t idx;
822
823   case_create (c, casereader_get_value_cnt (reader));
824   setjmp (r->bail_out);
825   if (!r->ok)
826     {
827       casereader_force_error (reader);
828       case_destroy (c);
829       return false;
830     }
831
832   /* Check for end of file. */
833   if (r->cc == 'Z')
834     {
835       case_destroy (c);
836       return false;
837     }
838
839   idx = 0;
840   for (i = 0; i < r->var_cnt; i++)
841     {
842       int width = r->widths[i];
843
844       if (width == 0)
845         {
846           case_data_rw_idx (c, idx)->f = read_float (r);
847           idx++;
848         }
849       else
850         {
851           char string[256];
852           read_string (r, string);
853           buf_copy_str_rpad (case_data_rw_idx (c, idx)->s, width, string);
854           idx += DIV_RND_UP (width, MAX_SHORT_STRING);
855         }
856     }
857
858   return true;
859 }
860
861 /* Returns true if FILE is an SPSS portable file,
862    false otherwise. */
863 bool
864 pfm_detect (FILE *file)
865 {
866   unsigned char header[464];
867   char trans[256];
868   int cooked_cnt, raw_cnt;
869   int i;
870
871   cooked_cnt = raw_cnt = 0;
872   while (cooked_cnt < sizeof header)
873     {
874       int c = getc (file);
875       if (c == EOF || raw_cnt++ > 512)
876         return false;
877       else if (c != '\n' && c != '\r')
878         header[cooked_cnt++] = c;
879     }
880
881   memset (trans, 0, 256);
882   for (i = 64; i < 256; i++)
883     {
884       unsigned char c = header[i + 200];
885       if (trans[c] == 0)
886         trans[c] = portable_to_local[i];
887     }
888
889   for (i = 0; i < 8; i++)
890     if (trans[header[i + 456]] != "SPSSPORT"[i])
891       return false;
892
893   return true;
894 }
895
896 static struct casereader_class por_file_casereader_class =
897   {
898     por_file_casereader_read,
899     por_file_casereader_destroy,
900     NULL,
901     NULL,
902   };