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