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