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