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