/[pspp]/pspp/src/pfm-read.c
ViewVC logotype

Diff of /pspp/src/pfm-read.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.16 by blp, Tue Mar 1 08:16:15 2005 UTC revision 1.17 by blp, Sat Mar 12 01:08:33 2005 UTC
# Line 28  Line 28 
28  #include <ctype.h>  #include <ctype.h>
29  #include <errno.h>  #include <errno.h>
30  #include <math.h>  #include <math.h>
31    #include <setjmp.h>
32  #include "alloc.h"  #include "alloc.h"
33    #include "bool.h"
34  #include "case.h"  #include "case.h"
35  #include "dictionary.h"  #include "dictionary.h"
36  #include "file-handle.h"  #include "file-handle.h"
# Line 37  Line 39 
39  #include "hash.h"  #include "hash.h"
40  #include "magic.h"  #include "magic.h"
41  #include "misc.h"  #include "misc.h"
42    #include "pool.h"
43  #include "str.h"  #include "str.h"
44  #include "value-labels.h"  #include "value-labels.h"
45  #include "var.h"  #include "var.h"
# Line 46  Line 49 
49  /* Portable file reader. */  /* Portable file reader. */
50  struct pfm_reader  struct pfm_reader
51    {    {
52      struct file_handle *fh;     /* File handle. */      struct pool *pool;          /* All the portable file state. */
     FILE *file;                 /* File stream. */  
53    
54      int weight_index;           /* 0-based index of weight variable, or -1. */      jmp_buf bail_out;           /* longjmp() target for error handling. */
55    
56        struct file_handle *fh;     /* File handle. */
57        FILE *file;                 /* File stream. */
58        char cc;                    /* Current character. */
59      unsigned char *trans;       /* 256-byte character set translation table. */      unsigned char *trans;       /* 256-byte character set translation table. */
60    
61      int var_cnt;                /* Number of variables. */      int var_cnt;                /* Number of variables. */
62        int weight_index;           /* 0-based index of weight variable, or -1. */
63      int *widths;                /* Variable widths, 0 for numeric. */      int *widths;                /* Variable widths, 0 for numeric. */
64      int value_cnt;              /* Number of `value's per case. */      int value_cnt;              /* Number of `value's per case. */
   
     unsigned char buf[83];      /* Input buffer. */  
     unsigned char *bp;          /* Buffer pointer. */  
     int cc;                     /* Current character. */  
65    };    };
66    
67  static int  static void
68  corrupt_msg (struct pfm_reader *r, const char *format,...)  error (struct pfm_reader *r, const char *msg,...)
69       PRINTF_FORMAT (2, 3);       PRINTF_FORMAT (2, 3);
70    
71  /* Displays a corruption error. */  /* Displays MSG as an error message and aborts reading the
72  static int     portable file via longjmp(). */
73  corrupt_msg (struct pfm_reader *r, const char *format, ...)  static void
74    error (struct pfm_reader *r, const char *msg, ...)
75  {  {
   char *title;  
76    struct error e;    struct error e;
77    const char *filename;    const char *filename;
78      char *title;
79    va_list args;    va_list args;
80    
81    e.class = ME;    e.class = ME;
82    getl_location (&e.where.filename, &e.where.line_number);    getl_location (&e.where.filename, &e.where.line_number);
83    filename = handle_get_filename (r->fh);    filename = handle_get_filename (r->fh);
84    e.title = title = local_alloc (strlen (filename) + 80);    e.title = title = pool_alloc (r->pool, strlen (filename) + 80);
85    sprintf (title, _("portable file %s corrupt at offset %ld: "),    sprintf (e.title, _("portable file %s corrupt at offset %ld: "),
86             filename, ftell (r->file) - (82 - (long) (r->bp - r->buf)));             filename, ftell (r->file));
87    
88    va_start (args, format);    va_start (args, msg);
89    err_vmsg (&e, format, args);    err_vmsg (&e, msg, args);
90    va_end (args);    va_end (args);
91    
92    local_free (title);    longjmp (r->bail_out, 1);
   
   return 0;  
93  }  }
94    
95  static unsigned char * read_string (struct pfm_reader *r);  /* Closes portable file reader R, after we're done with it. */
   
 /* Closes a portable file after we're done with it. */  
96  void  void
97  pfm_close_reader (struct pfm_reader *r)  pfm_close_reader (struct pfm_reader *r)
98  {  {
99    if (r == NULL)    if (r != NULL)
100      return;      pool_destroy (r->pool);
   
   read_string (NULL);  
   
   if (r->fh != NULL)  
     fh_close (r->fh, "portable file", "rs");  
   if (fclose (r->file) == EOF)  
     msg (ME, _("%s: Closing portable file: %s."),  
          handle_get_filename (r->fh), strerror (errno));  
   free (r->trans);  
   free (r->widths);  
   free (r);  
 }  
   
 /* Displays the message X with corrupt_msg, then jumps to the error  
    label. */  
 #define lose(X)                                 \  
         do {                                    \  
             corrupt_msg X;                      \  
             goto error;                       \  
         } while (0)  
   
 /* Read an 80-character line into handle H's buffer.  Return  
    success. */  
 static int  
 fill_buf (struct pfm_reader *r)  
 {  
   if (80 != fread (r->buf, 1, 80, r->file))  
     lose ((r, _("Unexpected end of file.")));  
   
   /* PORTME: line ends. */  
   {  
     int c;  
       
     c = getc (r->file);  
     if (c != '\n' && c != '\r')  
       lose ((r, _("Bad line end.")));  
   
     c = getc (r->file);  
     if (c != '\n' && c != '\r')  
       ungetc (c, r->file);  
   }  
     
   if (r->trans)  
     {  
       int i;  
         
       for (i = 0; i < 80; i++)  
         r->buf[i] = r->trans[r->buf[i]];  
     }  
   
   r->bp = r->buf;  
   
   return 1;  
   
  error:  
   return 0;  
101  }  }
102    
103  /* Read a single character into cur_char.  Return success; */  /* Read a single character into cur_char.  */
104  static int  static void
105  read_char (struct pfm_reader *r)  advance (struct pfm_reader *r)
106  {  {
107    if (r->bp >= &r->buf[80] && !fill_buf (r))    int c;
     return 0;  
   r->cc = *r->bp++;  
   return 1;  
 }  
108    
109  /* Advance a single character. */    while ((c = getc (r->file)) == '\r' || c == '\n')
110  #define advance()                               \      continue;
111          do {                                    \    if (c == EOF)
112            if (!read_char (r))                   \      error (r, _("unexpected end of file"));
113              goto error;                       \  
114          } while (0)    if (r->trans != NULL)
115        c = r->trans[c];
116      r->cc = c;
117    }
118    
119  /* Skip a single character if present, and return whether it was  /* Skip a single character if present, and return whether it was
120     skipped. */     skipped. */
121  static inline int  static inline bool
122  skip_char (struct pfm_reader *r, int c)  match (struct pfm_reader *r, int c)
123  {  {
124    if (r->cc == c)    if (r->cc == c)
125      {      {
126        advance ();        advance (r);
127        return 1;        return true;
128      }      }
129   error:    else
130    return 0;      return false;
131  }  }
132    
133  /* Skip a single character if present, and return whether it was  static void read_header (struct pfm_reader *);
134     skipped. */  static void read_version_data (struct pfm_reader *, struct pfm_read_info *);
135  #define match(C) skip_char (r, C)  static void read_variables (struct pfm_reader *, struct dictionary *);
136    static void read_value_label (struct pfm_reader *, struct dictionary *);
 static int read_header (struct pfm_reader *);  
 static int read_version_data (struct pfm_reader *, struct pfm_read_info *);  
 static int read_variables (struct pfm_reader *, struct dictionary *);  
 static int read_value_label (struct pfm_reader *, struct dictionary *);  
137  void dump_dictionary (struct dictionary *);  void dump_dictionary (struct dictionary *);
138    
139  /* Reads the dictionary from file with handle H, and returns it in a  /* Reads the dictionary from file with handle H, and returns it in a
# Line 205  struct pfm_reader * Line 143  struct pfm_reader *
143  pfm_open_reader (struct file_handle *fh, struct dictionary **dict,  pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
144                   struct pfm_read_info *info)                   struct pfm_read_info *info)
145  {  {
146    struct pfm_reader *r = NULL;    struct pool *volatile pool = NULL;
147      struct pfm_reader *volatile r = NULL;
148    
149    *dict = dict_create ();    *dict = dict_create ();
150    if (!fh_open (fh, "portable file", "rs"))    if (!fh_open (fh, "portable file", "rs"))
151      goto error;      goto error;
152    
153    /* Create and initialize reader. */    /* Create and initialize reader. */
154    r = xmalloc (sizeof *r);    pool = pool_create ();
155      r = pool_alloc (pool, sizeof *r);
156      r->pool = pool;
157      if (setjmp (r->bail_out))
158        goto error;
159    r->fh = fh;    r->fh = fh;
160    r->file = fopen (handle_get_filename (r->fh), "rb");    r->file = pool_fopen (r->pool, handle_get_filename (r->fh), "rb");
161    r->weight_index = -1;    r->weight_index = -1;
162    r->trans = NULL;    r->trans = NULL;
163    r->var_cnt = 0;    r->var_cnt = 0;
164    r->widths = NULL;    r->widths = NULL;
165    r->value_cnt = 0;    r->value_cnt = 0;
   r->bp = NULL;  
166    
167    /* Check that file open succeeded, prime reading. */    /* Check that file open succeeded, prime reading. */
168    if (r->file == NULL)    if (r->file == NULL)
# Line 231  pfm_open_reader (struct file_handle *fh, Line 173  pfm_open_reader (struct file_handle *fh,
173        err_cond_fail ();        err_cond_fail ();
174        goto error;        goto error;
175      }      }
176    if (!fill_buf (r))    
     goto error;  
   advance ();  
   
177    /* Read header, version, date info, product id, variables. */    /* Read header, version, date info, product id, variables. */
178    if (!read_header (r)    read_header (r);
179        || !read_version_data (r, info)    read_version_data (r, info);
180        || !read_variables (r, *dict))    read_variables (r, *dict);
     goto error;  
181    
182    /* Read value labels. */    /* Read value labels. */
183    while (match (77 /* D */))    while (match (r, 'D'))
184      if (!read_value_label (r, *dict))      read_value_label (r, *dict);
       goto error;  
185    
186    /* Check that we've made it to the data. */    /* Check that we've made it to the data. */
187    if (!match (79 /* F */))    if (!match (r, 'F'))
188      lose ((r, _("Data record expected.")));      error (r, _("Data record expected."));
189    
190    return r;    return r;
191    
# Line 259  pfm_open_reader (struct file_handle *fh, Line 196  pfm_open_reader (struct file_handle *fh,
196    return NULL;    return NULL;
197  }  }
198    
199  /* Read a floating point value and return its value, or  /* Returns the value of base-30 digit C,
200     second_lowest_value on error. */     or -1 if C is not a base-30 digit. */
201    static int
202    base_30_value (unsigned char c)
203    {
204      static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST";
205      const char *p = strchr (base_30_digits, c);
206      return p != NULL ? p - base_30_digits : -1;
207    }
208    
209    /* Read a floating point value and return its value. */
210  static double  static double
211  read_float (struct pfm_reader *r)  read_float (struct pfm_reader *r)
212  {  {
213    double num = 0.;    double num = 0.;
   int got_dot = 0;  
   int got_digit = 0;  
214    int exponent = 0;    int exponent = 0;
215    int neg = 0;    bool got_dot = false;         /* Seen a decimal point? */
216      bool got_digit = false;       /* Seen any digits? */
217      bool negative = false;        /* Number is negative? */
218    
219    /* Skip leading spaces. */    /* Skip leading spaces. */
220    while (match (126 /* space */))    while (match (r, ' '))
221      ;      continue;
222    
223    if (match (137 /* * */))    /* `*' indicates system-missing. */
224      if (match (r, '*'))
225      {      {
226        advance ();       /* Probably a dot (.) but doesn't appear to matter. */        advance (r);      /* Probably a dot (.) but doesn't appear to matter. */
227        return SYSMIS;        return SYSMIS;
228      }      }
   else if (match (141 /* - */))  
     neg = 1;  
229    
230      negative = match (r, '-');
231    for (;;)    for (;;)
232      {      {
233        if (r->cc >= 64 /* 0 */ && r->cc <= 93 /* T */)        int digit = base_30_value (r->cc);
234          if (digit != -1)
235          {          {
236            got_digit++;            got_digit = true;
237    
238            /* Make sure that multiplication by 30 will not overflow.  */            /* Make sure that multiplication by 30 will not overflow.  */
239            if (num > DBL_MAX * (1. / 30.))            if (num > DBL_MAX * (1. / 30.))
# Line 299  read_float (struct pfm_reader *r) Line 246  read_float (struct pfm_reader *r)
246                 digit so that we can multiply by 10 later.  */                 digit so that we can multiply by 10 later.  */
247              ++exponent;              ++exponent;
248            else            else
249              num = (num * 30.0) + (r->cc - 64);              num = (num * 30.0) + digit;
250    
251            /* Keep track of the number of digits after the decimal point.            /* Keep track of the number of digits after the decimal point.
252               If we just divided by 30 here, we would lose precision.  */               If we just divided by 30 here, we would lose precision.  */
253            if (got_dot)            if (got_dot)
254              --exponent;              --exponent;
255          }          }
256        else if (!got_dot && r->cc == 127 /* . */)        else if (!got_dot && r->cc == '.')
257          /* Record that we have found the decimal point.  */          /* Record that we have found the decimal point.  */
258          got_dot = 1;          got_dot = 1;
259        else        else
260          /* Any other character terminates the number.  */          /* Any other character terminates the number.  */
261          break;          break;
262    
263        advance ();        advance (r);
264      }      }
265    
266      /* Check that we had some digits. */
267    if (!got_digit)    if (!got_digit)
268      lose ((r, "Number expected."));      error (r, "Number expected.");
269          
270    if (r->cc == 130 /* + */ || r->cc == 141 /* - */)    /* Get exponent if any. */
271      if (r->cc == '+' || r->cc == '-')
272      {      {
       /* Get the exponent.  */  
273        long int exp = 0;        long int exp = 0;
274        int neg_exp = r->cc == 141 /* - */;        bool negative_exponent = r->cc == '-';
275          int digit;
276    
277        for (;;)        for (advance (r); (digit = base_30_value (r->cc)) != -1; advance (r))
278          {          {
           advance ();  
   
           if (r->cc < 64 /* 0 */ || r->cc > 93 /* T */)  
             break;  
   
279            if (exp > LONG_MAX / 30)            if (exp > LONG_MAX / 30)
280              goto overflow;              {
281            exp = exp * 30 + (r->cc - 64);                exp = LONG_MAX;
282                  break;
283                }
284              exp = exp * 30 + digit;
285          }          }
286    
287        /* We don't check whether there were actually any digits, but we        /* We don't check whether there were actually any digits, but we
288           probably should. */           probably should. */
289        if (neg_exp)        if (negative_exponent)
290          exp = -exp;          exp = -exp;
291        exponent += exp;        exponent += exp;
292      }      }
     
   if (!match (142 /* / */))  
     lose ((r, _("Missing numeric terminator.")));  
293    
294    /* Multiply NUM by 30 to the EXPONENT power, checking for overflow.  */    /* Numbers must end with `/'. */
295      if (!match (r, '/'))
296        error (r, _("Missing numeric terminator."));
297    
298      /* Multiply `num' by 30 to the `exponent' power, checking for
299         overflow.  */
300    if (exponent < 0)    if (exponent < 0)
301      num *= pow (30.0, (double) exponent);      num *= pow (30.0, (double) exponent);
302    else if (exponent > 0)    else if (exponent > 0)
303      {      {
304        if (num > DBL_MAX * pow (30.0, (double) -exponent))        if (num > DBL_MAX * pow (30.0, (double) -exponent))
305          goto overflow;          num = DBL_MAX;
306        num *= pow (30.0, (double) exponent);        else
307            num *= pow (30.0, (double) exponent);
308      }      }
309    
310    if (neg)    return negative ? -num : num;
     return -num;  
   else  
     return num;  
   
  overflow:  
   if (neg)  
     return -DBL_MAX / 10.;  
   else  
     return DBL_MAX / 10;  
   
  error:  
   return second_lowest_value;  
311  }  }
312        
313  /* Read an integer and return its value, or NOT_INT on failure. */  /* Read an integer and return its value. */
314  static int  static int
315  read_int (struct pfm_reader *r)  read_int (struct pfm_reader *r)
316  {  {
317    double f = read_float (r);    double f = read_float (r);
   
   if (f == second_lowest_value)  
     goto error;  
318    if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)    if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
319      lose ((r, _("Bad integer format.")));      error (r, _("Invalid integer."));
320    return f;    return f;
   
  error:  
   return NOT_INT;  
321  }  }
322    
323  /* Reads a string and returns its value in a static buffer, or NULL on  /* Reads a string into BUF, which must have room for 256
324     failure.  The buffer can be deallocated by calling with a NULL     characters. */
325     argument. */  static void
326  static unsigned char *  read_string (struct pfm_reader *r, char *buf)
 read_string (struct pfm_reader *r)  
327  {  {
328    static char *buf;    int n = read_int (r);
   int n;  
     
   if (r == NULL)  
     {  
       free (buf);  
       buf = NULL;  
       return NULL;  
     }  
   else if (buf == NULL)  
     buf = xmalloc (256);  
   
   n = read_int (r);  
   if (n == NOT_INT)  
     return NULL;  
329    if (n < 0 || n > 255)    if (n < 0 || n > 255)
330      lose ((r, _("Bad string length %d."), n));      error (r, _("Bad string length %d."), n);
331        
332    {    while (n-- > 0)
333      int i;      {
334          *buf++ = r->cc;
335      for (i = 0; i < n; i++)        advance (r);
336        {      }
337          buf[i] = r->cc;    *buf = '\0';
338          advance ();  }
       }  
   }  
     
   buf[n] = 0;  
   return buf;  
339    
340   error:  /* Reads a string and returns a copy of it allocated from R's
341    return NULL;     pool. */
342    static unsigned char *
343    read_pool_string (struct pfm_reader *r)
344    {
345      char string[256];
346      read_string (r, string);
347      return pool_strdup (r->pool, string);
348  }  }
349    
350  /* Reads the 464-byte file header. */  /* Reads the 464-byte file header. */
351  int  static void
352  read_header (struct pfm_reader *r)  read_header (struct pfm_reader *r)
353  {  {
354    /* For now at least, just ignore the vanity splash strings. */    /* portable_to_local[PORTABLE] translates the given portable
355    {       character into the local character set. */
356      int i;    static const unsigned char portable_to_local[256] =
357        {
358      for (i = 0; i < 200; i++)        "                                                                "
359        advance ();        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
360    }        "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
361            "                                                                "
362    {      };
     unsigned char src[256];  
     int trans_temp[256];  
     int i;  
   
     for (i = 0; i < 256; i++)  
       {  
         src[i] = (unsigned char) r->cc;  
         advance ();  
       }  
   
     for (i = 0; i < 256; i++)  
       trans_temp[i] = -1;  
   
     /* 0 is used to mark untranslatable characters, so we have to mark  
        it specially. */  
     trans_temp[src[64]] = 64;  
     for (i = 0; i < 256; i++)  
       if (trans_temp[src[i]] == -1)  
         trans_temp[src[i]] = i;  
       
     r->trans = xmalloc (256);  
     for (i = 0; i < 256; i++)  
       r->trans[i] = trans_temp[i] == -1 ? 0 : trans_temp[i];  
   
     /* Translate the input buffer. */  
     for (i = 0; i < 80; i++)  
       r->buf[i] = r->trans[r->buf[i]];  
     r->cc = r->trans[r->cc];  
   }  
     
   {  
     unsigned char sig[8] = {92, 89, 92, 92, 89, 88, 91, 93};  
     int i;  
   
     for (i = 0; i < 8; i++)  
       if (!match (sig[i]))  
         lose ((r, "Missing SPSSPORT signature."));  
   }  
363    
364    return 1;    unsigned char *trans;
365      int i;
366    
367   error:    /* Read and ignore vanity splash strings. */
368    return 0;    for (i = 0; i < 200; i++)
369        advance (r);
370      
371      /* Skip the first 64 characters of the translation table.
372         We don't care about these.  They are probably all set to
373         '0', marking them as untranslatable, and that would screw
374         up our actual translation of the real '0'. */
375      for (i = 0; i < 64; i++)
376        advance (r);
377    
378      /* Read the rest of the translation table. */
379      trans = pool_malloc (r->pool, 256);
380      memset (trans, 0, 256);
381      for (; i < 256; i++)
382        {
383          unsigned char c;
384    
385          advance (r);
386    
387          c = r->cc;
388          if (trans[c] == 0)
389            trans[c] = portable_to_local[i];
390        }
391    
392      /* Set up the translation table, then read the first
393         translated character. */
394      r->trans = trans;
395      advance (r);
396    
397      /* Skip and verify signature. */
398      for (i = 0; i < 8; i++)
399        if (!match (r, "SPSSPORT"[i]))
400          error (r, _("Missing SPSSPORT signature."));
401  }  }
402    
403  /* Reads the version and date info record, as well as product and  /* Reads the version and date info record, as well as product and
404     subproduct identification records if present. */     subproduct identification records if present. */
405  int  static void
406  read_version_data (struct pfm_reader *r, struct pfm_read_info *info)  read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
407  {  {
408    /* Version. */    char *date, *time, *product, *subproduct;
409    if (!match (74 /* A */))    int i;
     lose ((r, "Unrecognized version code %d.", r->cc));  
410    
411    /* Date. */    /* Read file. */
412    {    if (!match (r, 'A'))
413      static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};      error (r, "Unrecognized version code `%c'.", r->cc);
414      char *date = read_string (r);    date = read_pool_string (r);
415      int i;    time = read_pool_string (r);
416          product = match (r, '1') ? read_pool_string (r) : (unsigned char *) "";
417      if (!date)    subproduct
418        return 0;      = match (r, '3') ? read_pool_string (r) : (unsigned char *) "";
419      if (strlen (date) != 8)  
420        lose ((r, _("Bad date string length %d."), strlen (date)));    /* Validate file. */
421      for (i = 0; i < 8; i++)    if (strlen (date) != 8)
422        {      error (r, _("Bad date string length %d."), strlen (date));
423          if (date[i] < 64 /* 0 */ || date[i] > 73 /* 9 */)    if (strlen (time) != 6)
424            lose ((r, _("Bad character in date.")));      error (r, _("Bad time string length %d."), strlen (time));
         if (info)  
           info->creation_date[map[i]] = date[i] - 64 /* 0 */ + '0';  
       }  
     if (info)  
       {  
         info->creation_date[2] = info->creation_date[5] = ' ';  
         info->creation_date[10] = 0;  
       }  
   }  
     
   /* Time. */  
   {  
     static const int map[] = {0, 1, 3, 4, 6, 7};  
     char *time = read_string (r);  
     int i;  
   
     if (!time)  
       return 0;  
     if (strlen (time) != 6)  
       lose ((r, _("Bad time string length %d."), strlen (time)));  
     for (i = 0; i < 6; i++)  
       {  
         if (time[i] < 64 /* 0 */ || time[i] > 73 /* 9 */)  
           lose ((r, _("Bad character in time.")));  
         if (info)  
           info->creation_time[map[i]] = time[i] - 64 /* 0 */ + '0';  
       }  
     if (info)  
       {  
         info->creation_time[2] = info->creation_time[5] = ' ';  
         info->creation_time[8] = 0;  
       }  
   }  
425    
426    /* Product. */    /* Save file info. */
427    if (match (65 /* 1 */))    if (info != NULL)
428      {      {
429        char *product;        /* Date. */
430                for (i = 0; i < 8; i++)
431        product = read_string (r);          {
432        if (product == NULL)            static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
433          return 0;            info->creation_date[map[i]] = date[i];
434        if (info)          }
435          strncpy (info->product, product, 61);        info->creation_date[2] = info->creation_date[5] = ' ';
436      }        info->creation_date[10] = 0;
   else if (info)  
     info->product[0] = 0;  
   
   /* Subproduct. */  
   if (match (67 /* 3 */))  
     {  
       char *subproduct;  
   
       subproduct = read_string (r);  
       if (subproduct == NULL)  
         return 0;  
       if (info)  
         strncpy (info->subproduct, subproduct, 61);  
     }  
   else if (info)  
     info->subproduct[0] = 0;  
   return 1;  
     
  error:  
   return 0;  
 }  
437    
438  static int        /* Time. */
439  convert_format (struct pfm_reader *r, int fmt[3], struct fmt_spec *v,        for (i = 0; i < 6; i++)
440                  struct variable *vv)          {
441  {            static const int map[] = {0, 1, 3, 4, 6, 7};
442    v->type = translate_fmt (fmt[0]);            info->creation_time[map[i]] = time[i];
443    if (v->type == -1)          }
444      lose ((r, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));        info->creation_time[2] = info->creation_time[5] = ' ';
445    v->w = fmt[1];        info->creation_time[8] = 0;
   v->d = fmt[2];  
   
   /* FIXME?  Should verify the resulting specifier more thoroughly. */  
   
   if (v->type == -1)  
     lose ((r, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));  
   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))  
     lose ((r, _("%s variable %s has %s format specifier %s."),  
            vv->type == ALPHA ? _("String") : _("Numeric"),  
            vv->name,  
            formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),  
            formats[v->type].name));  
   return 1;  
446    
447   error:        /* Product. */
448    return 0;        st_trim_copy (info->product, product, sizeof info->product);
449          st_trim_copy (info->subproduct, subproduct, sizeof info->subproduct);
450        }
451  }  }
452    
453  /* Translation table from SPSS character code to this computer's  /* Translates a format specification read from portable file R as
454     native character code (which is probably ASCII). */     the three integers INTS into a normal format specifier FORMAT,
455  static const unsigned char spss2ascii[256] =     checking that the format is appropriate for variable V. */
   {  
     "                                                                "  
     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."  
     "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "  
     "                                                                "  
   };  
   
 /* Translate string S into ASCII. */  
456  static void  static void
457  asciify (char *s)  convert_format (struct pfm_reader *r, const int portable_format[3],
458                    struct fmt_spec *format, struct variable *v)
459  {  {
460    for (; *s; s++)    format->type = translate_fmt (portable_format[0]);
461      *s = spss2ascii[(unsigned char) *s];    if (format->type == -1)
462        error (r, _("%s: Bad format specifier byte (%d)."),
463               v->name, portable_format[0]);
464      format->w = portable_format[1];
465      format->d = portable_format[2];
466    
467      if (!check_output_specifier (format, false)
468          || !check_specifier_width (format, v->width, false))
469        error (r, _("%s variable %s has invalid format specifier %s."),
470               v->type == NUMERIC ? _("Numeric") : _("String"),
471               v->name, fmt_to_string (format));
472  }  }
473    
474  static int parse_value (struct pfm_reader *, union value *, struct variable *);  static union value parse_value (struct pfm_reader *, struct variable *);
475    
476  /* Read information on all the variables.  */  /* Read information on all the variables.  */
477  static int  static void
478  read_variables (struct pfm_reader *r, struct dictionary *dict)  read_variables (struct pfm_reader *r, struct dictionary *dict)
479  {  {
480    char *weight_name = NULL;    char *weight_name = NULL;
481    int i;    int i;
482        
483    if (!match (68 /* 4 */))    if (!match (r, '4'))
484      lose ((r, _("Expected variable count record.")));      error (r, _("Expected variable count record."));
485        
486    r->var_cnt = read_int (r);    r->var_cnt = read_int (r);
487    if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)    if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)
488      lose ((r, _("Invalid number of variables %d."), r->var_cnt));      error (r, _("Invalid number of variables %d."), r->var_cnt);
489    r->widths = xmalloc (sizeof *r->widths * r->var_cnt);    r->widths = pool_alloc (r->pool, sizeof *r->widths * r->var_cnt);
490    
491    /* Purpose of this value is unknown.  It is typically 161. */    /* Purpose of this value is unknown.  It is typically 161. */
492    {    read_int (r);
     int x = read_int (r);  
493    
494      if (x == NOT_INT)    if (match (r, '6'))
       goto error;  
     if (x != 161)  
       corrupt_msg (r, _("Unexpected flag value %d."), x);  
   }  
   
   if (match (70 /* 6 */))  
495      {      {
496        weight_name = read_string (r);        weight_name = read_pool_string (r);
       if (!weight_name)  
         goto error;  
   
       asciify (weight_name);  
497        if (strlen (weight_name) > 8)        if (strlen (weight_name) > 8)
498          {          error (r, _("Weight variable name (%s) truncated."), weight_name);
           corrupt_msg (r, _("Weight variable name (%s) truncated."),  
                        weight_name);  
           weight_name[8] = '\0';  
         }  
499      }      }
500        
501    for (i = 0; i < r->var_cnt; i++)    for (i = 0; i < r->var_cnt; i++)
502      {      {
503        int width;        int width;
504        unsigned char *name;        char name[256];
505        int fmt[6];        int fmt[6];
506        struct variable *v;        struct variable *v;
507        int j;        int j;
508    
509        if (!match (71 /* 7 */))        if (!match (r, '7'))
510          lose ((r, _("Expected variable record.")));          error (r, _("Expected variable record."));
511    
512        width = read_int (r);        width = read_int (r);
       if (width == NOT_INT)  
         goto error;  
513        if (width < 0)        if (width < 0)
514          lose ((r, _("Invalid variable width %d."), width));          error (r, _("Invalid variable width %d."), width);
515        r->widths[i] = width;        r->widths[i] = width;
         
       name = read_string (r);  
       if (name == NULL)  
         goto error;  
       for (j = 0; j < 6; j++)  
         {  
           fmt[j] = read_int (r);  
           if (fmt[j] == NOT_INT)  
             goto error;  
         }  
   
       /* Verify first character of variable name.  
   
          Weirdly enough, there is no # character in the SPSS portable  
          character set, so we can't check for it. */  
       if (strlen (name) > 8)  
         lose ((r, _("position %d: Variable name has %u characters."),  
                i, strlen (name)));  
       if ((name[0] < 74 /* A */ || name[0] > 125 /* Z */)  
           && name[0] != 152 /* @ */)  
         lose ((r, _("position %d: Variable name begins with invalid "  
                "character."), i));  
       if (name[0] >= 100 /* a */ && name[0] <= 125 /* z */)  
         {  
           corrupt_msg (r, _("position %d: Variable name begins with "  
                             "lowercase letter %c."),  
                        i, name[0] - 100 + 'a');  
           name[0] -= 26 /* a - A */;  
         }  
516    
517        /* Verify remaining characters of variable name. */        read_string (r, name);
518        for (j = 1; j < (int) strlen (name); j++)        for (j = 0; j < 6; j++)
519          {          fmt[j] = read_int (r);
           int c = name[j];  
520    
521            if (c >= 100 /* a */ && c <= 125 /* z */)        if (!var_is_valid_name (name, false) || *name == '#')
522              {          error (r, _("position %d: Invalid variable name `%s'."), name);
523                corrupt_msg (r, _("position %d: Variable name character %d "        st_uppercase (name);
                                 "is lowercase letter %c."),  
                            i, j + 1, c - 100 + 'a');  
               name[j] -= 26 /* z - Z */;  
             }  
           else if ((c >= 64 /* 0 */ && c <= 99 /* Z */)  
                    || c == 127 /* . */ || c == 152 /* @ */  
                    || c == 136 /* $ */ || c == 146 /* _ */)  
             name[j] = c;  
           else  
             lose ((r, _("position %d: character `\\%03o' is not "  
                         "valid in a variable name."), i, c));  
         }  
524    
       asciify (name);  
525        if (width < 0 || width > 255)        if (width < 0 || width > 255)
526          lose ((r, "Bad width %d for variable %s.", width, name));          error (r, "Bad width %d for variable %s.", width, name);
527    
528        v = dict_create_var (dict, name, width);        v = dict_create_var (dict, name, width);
529        if (v == NULL)        if (v == NULL)
530          lose ((r, _("Duplicate variable name %s."), name));          error (r, _("Duplicate variable name %s."), name);
531        if (!convert_format (r, &fmt[0], &v->print, v))  
532          goto error;        convert_format (r, &fmt[0], &v->print, v);
533        if (!convert_format (r, &fmt[3], &v->write, v))        convert_format (r, &fmt[3], &v->write, v);
         goto error;  
534    
535        /* Range missing values. */        /* Range missing values. */
536        if (match (75 /* B */))        if (match (r, 'B'))
537          {          {
538            v->miss_type = MISSING_RANGE;            v->miss_type = MISSING_RANGE;
539            if (!parse_value (r, &v->missing[0], v)            v->missing[0] = parse_value (r, v);
540                || !parse_value (r, &v->missing[1], v))            v->missing[1] = parse_value (r, v);
             goto error;  
541          }          }
542        else if (match (74 /* A */))        else if (match (r, 'A'))
543          {          {
544            v->miss_type = MISSING_HIGH;            v->miss_type = MISSING_HIGH;
545            if (!parse_value (r, &v->missing[0], v))            v->missing[0] = parse_value (r, v);
             goto error;  
546          }          }
547        else if (match (73 /* 9 */))        else if (match (r, '9'))
548          {          {
549            v->miss_type = MISSING_LOW;            v->miss_type = MISSING_LOW;
550            if (!parse_value (r, &v->missing[0], v))            v->missing[0] = parse_value (r, v);
             goto error;  
551          }          }
552    
553        /* Single missing values. */        /* Single missing values. */
554        while (match (72 /* 8 */))        while (match (r, '8'))
555          {          {
556            static const int map_next[MISSING_COUNT] =            static const int map_next[MISSING_COUNT] =
557              {              {
# Line 782  read_variables (struct pfm_reader *r, st Line 567  read_variables (struct pfm_reader *r, st
567    
568            v->miss_type = map_next[v->miss_type];            v->miss_type = map_next[v->miss_type];
569            if (v->miss_type == -1)            if (v->miss_type == -1)
570              lose ((r, _("Bad missing values for %s."), v->name));              error (r, _("Bad missing values for %s."), v->name);
571                        
572            assert (map_ofs[v->miss_type] != -1);            assert (map_ofs[v->miss_type] != -1);
573            if (!parse_value (r, &v->missing[map_ofs[v->miss_type]], v))            v->missing[map_ofs[v->miss_type]] = parse_value (r, v);
             goto error;  
574          }          }
575    
576        if (match (76 /* C */))        if (match (r, 'C'))
577          {          {
578            char *label = read_string (r);            char label[256];
579                        read_string (r, label);
580            if (label == NULL)            v->label = xstrdup (label);
581              goto error;          }
   
           v->label = xstrdup (label);  
           asciify (v->label);  
         }  
582      }      }
583    
584    if (weight_name != NULL)    if (weight_name != NULL)
585      {      {
586        struct variable *weight_var = dict_lookup_var (dict, weight_name);        struct variable *weight_var = dict_lookup_var (dict, weight_name);
587        if (weight_var == NULL)        if (weight_var == NULL)
588          lose ((r, _("Weighting variable %s not present in dictionary."),          error (r, _("Weighting variable %s not present in dictionary."),
589                 weight_name));                 weight_name);
       free (weight_name);  
590    
591        dict_set_weight (dict, weight_var);        dict_set_weight (dict, weight_var);
592      }      }
   
   return 1;  
   
  error:  
   free (weight_name);  
   return 0;  
593  }  }
594    
595  /* Parse a value for variable VV into value V.  Returns success. */  /* Parse a value for variable VV into value V. */
596  static int  static union value
597  parse_value (struct pfm_reader *r, union value *v, struct variable *vv)  parse_value (struct pfm_reader *r, struct variable *vv)
598  {  {
599    if (vv->type == ALPHA)    union value v;
600      
601      if (vv->type == ALPHA)
602      {      {
603        char *mv = read_string (r);        char string[256];
604        int j;        read_string (r, string);
605                st_bare_pad_copy (v.s, string, 8);
       if (mv == NULL)  
         return 0;  
   
       strncpy (v->s, mv, 8);  
       for (j = 0; j < 8; j++)  
         if (v->s[j])  
           v->s[j] = spss2ascii[v->s[j]];  
         else  
           /* Value labels are always padded with spaces. */  
           v->s[j] = ' ';  
606      }      }
607    else    else
608      {      v.f = read_float (r);
       v->f = read_float (r);  
       if (v->f == second_lowest_value)  
         return 0;  
     }  
609    
610    return 1;    return v;
611  }  }
612    
613  /* Parse a value label record and return success. */  /* Parse a value label record and return success. */
614  static int  static void
615  read_value_label (struct pfm_reader *r, struct dictionary *dict)  read_value_label (struct pfm_reader *r, struct dictionary *dict)
616  {  {
617    /* Variables. */    /* Variables. */
# Line 863  read_value_label (struct pfm_reader *r, Line 624  read_value_label (struct pfm_reader *r,
624    int i;    int i;
625    
626    nv = read_int (r);    nv = read_int (r);
627    if (nv == NOT_INT)    v = pool_alloc (r->pool, sizeof *v * nv);
     return 0;  
   
   v = xmalloc (sizeof *v * nv);  
628    for (i = 0; i < nv; i++)    for (i = 0; i < nv; i++)
629      {      {
630        char *name = read_string (r);        char name[256];
631        if (name == NULL)        read_string (r, name);
         goto error;  
       asciify (name);  
632    
633        v[i] = dict_lookup_var (dict, name);        v[i] = dict_lookup_var (dict, name);
634        if (v[i] == NULL)        if (v[i] == NULL)
635          lose ((r, _("Unknown variable %s while parsing value labels."), name));          error (r, _("Unknown variable %s while parsing value labels."), name);
636    
637        if (v[0]->width != v[i]->width)        if (v[0]->width != v[i]->width)
638          lose ((r, _("Cannot assign value labels to %s and %s, which "          error (r, _("Cannot assign value labels to %s and %s, which "
639                      "have different variable types or widths."),                      "have different variable types or widths."),
640                 v[0]->name, v[i]->name));                 v[0]->name, v[i]->name);
641      }      }
642    
643    n_labels = read_int (r);    n_labels = read_int (r);
   if (n_labels == NOT_INT)  
     goto error;  
   
644    for (i = 0; i < n_labels; i++)    for (i = 0; i < n_labels; i++)
645      {      {
646        union value val;        union value val;
647        char *label;        char label[256];
   
648        int j;        int j;
649          
650        if (!parse_value (r, &val, v[0]))        val = parse_value (r, v[0]);
651          goto error;        read_string (r, label);
         
       label = read_string (r);  
       if (label == NULL)  
         goto error;  
       asciify (label);  
652    
653        /* Assign the value_label's to each variable. */        /* Assign the value_label's to each variable. */
654        for (j = 0; j < nv; j++)        for (j = 0; j < nv; j++)
# Line 912  read_value_label (struct pfm_reader *r, Line 659  read_value_label (struct pfm_reader *r,
659              continue;              continue;
660    
661            if (var->type == NUMERIC)            if (var->type == NUMERIC)
662              lose ((r, _("Duplicate label for value %g for variable %s."),              error (r, _("Duplicate label for value %g for variable %s."),
663                     val.f, var->name));                     val.f, var->name);
664            else            else
665              lose ((r, _("Duplicate label for value `%.*s' for variable %s."),              error (r, _("Duplicate label for value `%.*s' for variable %s."),
666                     var->width, val.s, var->name));                     var->width, val.s, var->name);
667          }          }
668      }      }
   free (v);  
   return 1;  
   
  error:  
   free (v);  
   return 0;  
669  }  }
670    
671  /* Reads one case from portable file R into C.  Returns nonzero  /* Reads one case from portable file R into C. */
672     only if successful. */  bool
 int  
673  pfm_read_case (struct pfm_reader *r, struct ccase *c)  pfm_read_case (struct pfm_reader *r, struct ccase *c)
674  {  {
675    size_t i;    size_t i;
676    size_t idx;    size_t idx;
677    
678    /* Check for end of file. */    if (setjmp (r->bail_out))
679    if (r->cc == 99 /* Z */)      return false;
     return 0;  
680        
681      /* Check for end of file. */
682      if (r->cc == 'Z')
683        return false;
684    
685    idx = 0;    idx = 0;
686    for (i = 0; i < r->var_cnt; i++)    for (i = 0; i < r->var_cnt; i++)
687      {      {
# Line 946  pfm_read_case (struct pfm_reader *r, str Line 689  pfm_read_case (struct pfm_reader *r, str
689                
690        if (width == 0)        if (width == 0)
691          {          {
692            double f = read_float (r);            case_data_rw (c, idx)->f = read_float (r);
           if (f == second_lowest_value)  
             goto unexpected_eof;  
   
           case_data_rw (c, idx)->f = f;  
693            idx++;            idx++;
694          }          }
695        else        else
696          {          {
697            char *s = read_string (r);            char string[256];
698            if (s == NULL)            read_string (r, string);
699              goto unexpected_eof;            st_bare_pad_copy (case_data_rw (c, idx)->s, string, width);
           asciify (s);  
   
           st_bare_pad_copy (case_data_rw (c, idx)->s, s, width);  
700            idx += DIV_RND_UP (width, MAX_SHORT_STRING);            idx += DIV_RND_UP (width, MAX_SHORT_STRING);
701          }          }
702      }      }
703        
704    return 1;    return true;
   
  unexpected_eof:  
   lose ((r, _("End of file midway through case.")));  
   
  error:  
   return 0;  
705  }  }

Legend:
Removed from v.1.16  
changed lines
  Added in v.1.17

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26