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" |
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" |
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 |
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) |
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 |
|
|
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.)) |
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 |
{ |
{ |
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. */ |
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++) |
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 |
{ |
{ |
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 |
} |
} |