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