X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fpor-file-reader.c;h=f9eb28b620d91d58e88573793f80ba0474e90014;hb=679e253a4777564db48ccec70b8ce2f6e5b7e46b;hp=c4fec4b0ed72d30a2608c9b6af49340d35d40384;hpb=f19d8882cd0ecbf7811c73c2a3cf9ffe422d39d5;p=pspp-builds.git diff --git a/src/data/por-file-reader.c b/src/data/por-file-reader.c index c4fec4b0..f9eb28b6 100644 --- a/src/data/por-file-reader.c +++ b/src/data/por-file-reader.c @@ -1,54 +1,51 @@ -/* PSPP - computes sample statistics. +/* PSPP - a program for statistical analysis. Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. - Written by Ben Pfaff . - Code for parsing floating-point numbers adapted from GNU C - library. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include #include "por-file-reader.h" -#include -#include -#include -#include + #include #include #include #include -#include +#include #include -#include "case.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include "dictionary.h" -#include "file-handle-def.h" -#include "format.h" #include #include +#include #include #include #include -#include "value-labels.h" -#include "variable.h" #include "gettext.h" #define _(msgid) gettext (msgid) -#include - /* portable_to_local[PORTABLE] translates the given portable character into the local character set. */ static const char portable_to_local[256] = @@ -73,10 +70,12 @@ struct pfm_reader int var_cnt; /* Number of variables. */ int weight_index; /* 0-based index of weight variable, or -1. */ int *widths; /* Variable widths, 0 for numeric. */ - int value_cnt; /* Number of `value's per case. */ + size_t value_cnt; /* Number of `value's per case. */ bool ok; /* Set false on I/O error. */ }; +static struct casereader_class por_file_casereader_class; + static void error (struct pfm_reader *r, const char *msg,...) PRINTF_FORMAT (2, 3) @@ -87,34 +86,36 @@ error (struct pfm_reader *r, const char *msg,...) static void error (struct pfm_reader *r, const char *msg, ...) { - struct error e; - const char *filename; - char *title; + struct msg m; + struct string text; va_list args; - e.class = ME; - e.where.filename = NULL; - e.where.line_number = 0; - filename = fh_get_filename (r->fh); - e.title = title = pool_alloc (r->pool, strlen (filename) + 80); - sprintf (title, _("portable file %s corrupt at offset %ld: "), - filename, ftell (r->file)); - + ds_init_empty (&text); + ds_put_format (&text, _("portable file %s corrupt at offset %ld: "), + fh_get_file_name (r->fh), ftell (r->file)); va_start (args, msg); - err_vmsg (&e, msg, args); + ds_put_vformat (&text, msg, args); va_end (args); + m.category = MSG_GENERAL; + m.severity = MSG_ERROR; + m.where.file_name = NULL; + m.where.line_number = 0; + m.text = ds_cstr (&text); + + msg_emit (&m); + r->ok = false; longjmp (r->bail_out, 1); } /* Closes portable file reader R, after we're done with it. */ -void -pfm_close_reader (struct pfm_reader *r) +static void +por_file_casereader_destroy (struct casereader *reader UNUSED, void *r_) { - if (r != NULL) - pool_destroy (r->pool); + struct pfm_reader *r = r_; + pool_destroy (r->pool); } /* Read a single character into cur_char. */ @@ -126,10 +127,10 @@ advance (struct pfm_reader *r) while ((c = getc (r->file)) == '\r' || c == '\n') continue; if (c == EOF) - error (r, _("unexpected end of file")); + error (r, _("unexpected end of file")); if (r->trans != NULL) - c = r->trans[c]; + c = r->trans[c]; r->cc = c; } @@ -156,7 +157,7 @@ void dump_dictionary (struct dictionary *); /* Reads the dictionary from file with handle H, and returns it in a dictionary structure. This dictionary may be modified in order to rename, reorder, and delete variables, etc. */ -struct pfm_reader * +struct casereader * pfm_open_reader (struct file_handle *fh, struct dictionary **dict, struct pfm_read_info *info) { @@ -174,7 +175,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict, if (setjmp (r->bail_out)) goto error; r->fh = fh; - r->file = pool_fopen (r->pool, fh_get_filename (r->fh), "rb"); + r->file = pool_fopen (r->pool, fh_get_file_name (r->fh), "rb"); r->weight_index = -1; r->trans = NULL; r->var_cnt = 0; @@ -187,10 +188,10 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict, { msg (ME, _("An error occurred while opening \"%s\" for reading " "as a portable file: %s."), - fh_get_filename (r->fh), strerror (errno)); + fh_get_file_name (r->fh), strerror (errno)); goto error; } - + /* Read header, version, date info, product id, variables. */ read_header (r); read_version_data (r, info); @@ -204,10 +205,12 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict, if (!match (r, 'F')) error (r, _("Data record expected.")); - return r; + r->value_cnt = dict_get_next_value_idx (*dict); + return casereader_create_sequential (NULL, r->value_cnt, CASENUMBER_MAX, + &por_file_casereader_class, r); error: - pfm_close_reader (r); + pool_destroy (r->pool); dict_destroy (*dict); *dict = NULL; return NULL; @@ -216,7 +219,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict, /* Returns the value of base-30 digit C, or -1 if C is not a base-30 digit. */ static int -base_30_value (unsigned char c) +base_30_value (unsigned char c) { static const char base_30_digits[] = "0123456789ABCDEFGHIJKLMNOPQRST"; const char *p = strchr (base_30_digits, c); @@ -282,7 +285,7 @@ read_float (struct pfm_reader *r) /* Check that we had some digits. */ if (!got_digit) - error (r, "Number expected."); + error (r, _("Number expected.")); /* Get exponent if any. */ if (r->cc == '+' || r->cc == '-') @@ -326,7 +329,7 @@ read_float (struct pfm_reader *r) return negative ? -num : num; } - + /* Read an integer and return its value. */ static int read_int (struct pfm_reader *r) @@ -345,7 +348,7 @@ read_string (struct pfm_reader *r, char *buf) int n = read_int (r); if (n < 0 || n > 255) error (r, _("Bad string length %d."), n); - + while (n-- > 0) { *buf++ = r->cc; @@ -357,7 +360,7 @@ read_string (struct pfm_reader *r, char *buf) /* Reads a string and returns a copy of it allocated from R's pool. */ static char * -read_pool_string (struct pfm_reader *r) +read_pool_string (struct pfm_reader *r) { char string[256]; read_string (r, string); @@ -374,7 +377,7 @@ read_header (struct pfm_reader *r) /* Read and ignore vanity splash strings. */ for (i = 0; i < 200; i++) advance (r); - + /* Skip the first 64 characters of the translation table. We don't care about these. They are probably all set to '0', marking them as untranslatable, and that would screw @@ -385,7 +388,7 @@ read_header (struct pfm_reader *r) /* Read the rest of the translation table. */ trans = pool_malloc (r->pool, 256); memset (trans, 0, 256); - for (; i < 256; i++) + for (; i < 256; i++) { unsigned char c; @@ -399,13 +402,13 @@ read_header (struct pfm_reader *r) /* Set up the translation table, then read the first translated character. */ r->trans = trans; - advance (r); + advance (r); /* Skip and verify signature. */ - for (i = 0; i < 8; i++) - if (!match (r, "SPSSPORT"[i])) + for (i = 0; i < 8; i++) + if (!match (r, "SPSSPORT"[i])) { - msg (SE, _("%s: Not a portable file."), fh_get_filename (r->fh)); + msg (SE, _("%s: Not a portable file."), fh_get_file_name (r->fh)); longjmp (r->bail_out, 1); } } @@ -421,7 +424,7 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info) /* Read file. */ if (!match (r, 'A')) - error (r, "Unrecognized version code `%c'.", r->cc); + error (r, _("Unrecognized version code `%c'."), r->cc); date = read_pool_string (r); time = read_pool_string (r); product = match (r, '1') ? read_pool_string (r) : empty_string; @@ -430,18 +433,18 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info) /* Validate file. */ if (strlen (date) != 8) - error (r, _("Bad date string length %d."), strlen (date)); + error (r, _("Bad date string length %d."), (int) strlen (date)); if (strlen (time) != 6) - error (r, _("Bad time string length %d."), strlen (time)); + error (r, _("Bad time string length %d."), (int) strlen (time)); /* Save file info. */ - if (info != NULL) + if (info != NULL) { /* Date. */ - for (i = 0; i < 8; i++) + for (i = 0; i < 8; i++) { static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1}; - info->creation_date[map[i]] = date[i]; + info->creation_date[map[i]] = date[i]; } info->creation_date[2] = info->creation_date[5] = ' '; info->creation_date[10] = 0; @@ -464,22 +467,34 @@ read_version_data (struct pfm_reader *r, struct pfm_read_info *info) /* Translates a format specification read from portable file R as the three integers INTS into a normal format specifier FORMAT, checking that the format is appropriate for variable V. */ -static void +static struct fmt_spec convert_format (struct pfm_reader *r, const int portable_format[3], - struct fmt_spec *format, struct variable *v) + struct variable *v) { - format->type = translate_fmt (portable_format[0]); - if (format->type == -1) + struct fmt_spec format; + bool ok; + + if (!fmt_from_io (portable_format[0], &format.type)) error (r, _("%s: Bad format specifier byte (%d)."), - v->name, portable_format[0]); - format->w = portable_format[1]; - format->d = portable_format[2]; - - if (!check_output_specifier (format, false) - || !check_specifier_width (format, v->width, false)) - error (r, _("%s variable %s has invalid format specifier %s."), - v->type == NUMERIC ? _("Numeric") : _("String"), - v->name, fmt_to_string (format)); + var_get_name (v), portable_format[0]); + format.w = portable_format[1]; + format.d = portable_format[2]; + + msg_disable (); + ok = (fmt_check_output (&format) + && fmt_check_width_compat (&format, var_get_width (v))); + msg_enable (); + + if (!ok) + { + char fmt_string[FMT_STRING_LEN_MAX + 1]; + error (r, _("%s variable %s has invalid format specifier %s."), + var_is_numeric (v) ? _("Numeric") : _("String"), + var_get_name (v), fmt_to_string (&format, fmt_string)); + format = fmt_default_for_width (var_get_width (v)); + } + + return format; } static union value parse_value (struct pfm_reader *, struct variable *); @@ -490,10 +505,10 @@ read_variables (struct pfm_reader *r, struct dictionary *dict) { char *weight_name = NULL; int i; - + if (!match (r, '4')) error (r, _("Expected variable count record.")); - + r->var_cnt = read_int (r); if (r->var_cnt <= 0 || r->var_cnt == NOT_INT) error (r, _("Invalid number of variables %d."), r->var_cnt); @@ -505,16 +520,18 @@ read_variables (struct pfm_reader *r, struct dictionary *dict) if (match (r, '6')) { weight_name = read_pool_string (r); - if (strlen (weight_name) > SHORT_NAME_LEN) + if (strlen (weight_name) > SHORT_NAME_LEN) error (r, _("Weight variable name (%s) truncated."), weight_name); } - + for (i = 0; i < r->var_cnt; i++) { int width; char name[256]; int fmt[6]; struct variable *v; + struct missing_values miss; + struct fmt_spec print, write; int j; if (!match (r, '7')) @@ -534,43 +551,48 @@ read_variables (struct pfm_reader *r, struct dictionary *dict) str_uppercase (name); if (width < 0 || width > 255) - error (r, "Bad width %d for variable %s.", width, name); + error (r, _("Bad width %d for variable %s."), width, name); v = dict_create_var (dict, name, width); if (v == NULL) error (r, _("Duplicate variable name %s."), name); - convert_format (r, &fmt[0], &v->print, v); - convert_format (r, &fmt[3], &v->write, v); + print = convert_format (r, &fmt[0], v); + write = convert_format (r, &fmt[3], v); + var_set_print_format (v, &print); + var_set_write_format (v, &write); /* Range missing values. */ - if (match (r, 'B')) + mv_init (&miss, var_get_width (v)); + if (match (r, 'B')) { double x = read_float (r); double y = read_float (r); - mv_add_num_range (&v->miss, x, y); + mv_add_num_range (&miss, x, y); } else if (match (r, 'A')) - mv_add_num_range (&v->miss, read_float (r), HIGHEST); + mv_add_num_range (&miss, read_float (r), HIGHEST); else if (match (r, '9')) - mv_add_num_range (&v->miss, LOWEST, read_float (r)); + mv_add_num_range (&miss, LOWEST, read_float (r)); /* Single missing values. */ - while (match (r, '8')) + while (match (r, '8')) { union value value = parse_value (r, v); - mv_add_value (&v->miss, &value); + mv_add_value (&miss, &value); } - if (match (r, 'C')) + var_set_missing_values (v, &miss); + + if (match (r, 'C')) { char label[256]; read_string (r, label); - v->label = xstrdup (label); + var_set_label (v, label); } } - if (weight_name != NULL) + if (weight_name != NULL) { struct variable *weight_var = dict_lookup_var (dict, weight_name); if (weight_var == NULL) @@ -586,12 +608,12 @@ static union value parse_value (struct pfm_reader *r, struct variable *vv) { union value v; - - if (vv->type == ALPHA) + + if (var_is_alpha (vv)) { char string[256]; read_string (r, string); - buf_copy_str_rpad (v.s, 8, string); + buf_copy_str_rpad (v.s, 8, string); } else v.f = read_float (r); @@ -623,10 +645,10 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict) if (v[i] == NULL) error (r, _("Unknown variable %s while parsing value labels."), name); - if (v[0]->width != v[i]->width) + if (var_get_width (v[0]) != var_get_width (v[i])) error (r, _("Cannot assign value labels to %s and %s, which " "have different variable types or widths."), - v[0]->name, v[i]->name); + var_get_name (v[0]), var_get_name (v[i])); } n_labels = read_int (r); @@ -644,68 +666,69 @@ read_value_label (struct pfm_reader *r, struct dictionary *dict) { struct variable *var = v[j]; - if (!val_labs_replace (var->val_labs, val, label)) + if (!var_add_value_label (var, &val, label)) continue; - if (var->type == NUMERIC) + if (var_is_numeric (var)) error (r, _("Duplicate label for value %g for variable %s."), - val.f, var->name); + val.f, var_get_name (var)); else error (r, _("Duplicate label for value `%.*s' for variable %s."), - var->width, val.s, var->name); + var_get_width (var), val.s, var_get_name (var)); } } } /* Reads one case from portable file R into C. */ -bool -pfm_read_case (struct pfm_reader *r, struct ccase *c) +static bool +por_file_casereader_read (struct casereader *reader, void *r_, struct ccase *c) { + struct pfm_reader *r = r_; size_t i; size_t idx; + case_create (c, casereader_get_value_cnt (reader)); setjmp (r->bail_out); if (!r->ok) - return false; - + { + casereader_force_error (reader); + case_destroy (c); + return false; + } + /* Check for end of file. */ if (r->cc == 'Z') - return false; + { + case_destroy (c); + return false; + } idx = 0; - for (i = 0; i < r->var_cnt; i++) + for (i = 0; i < r->var_cnt; i++) { int width = r->widths[i]; - + if (width == 0) { - case_data_rw (c, idx)->f = read_float (r); + case_data_rw_idx (c, idx)->f = read_float (r); idx++; } else { char string[256]; read_string (r, string); - buf_copy_str_rpad (case_data_rw (c, idx)->s, width, string); + buf_copy_str_rpad (case_data_rw_idx (c, idx)->s, width, string); idx += DIV_RND_UP (width, MAX_SHORT_STRING); } } - - return true; -} -/* Returns true if an I/O error has occurred on READER, false - otherwise. */ -bool -pfm_read_error (const struct pfm_reader *reader) -{ - return !reader->ok; + return true; } /* Returns true if FILE is an SPSS portable file, false otherwise. */ bool -pfm_detect (FILE *file) +pfm_detect (FILE *file) { unsigned char header[464]; char trans[256]; @@ -718,21 +741,29 @@ pfm_detect (FILE *file) int c = getc (file); if (c == EOF || raw_cnt++ > 512) return false; - else if (c != '\n' && c != '\r') + else if (c != '\n' && c != '\r') header[cooked_cnt++] = c; } memset (trans, 0, 256); - for (i = 64; i < 256; i++) + for (i = 64; i < 256; i++) { unsigned char c = header[i + 200]; if (trans[c] == 0) trans[c] = portable_to_local[i]; } - for (i = 0; i < 8; i++) - if (trans[header[i + 456]] != "SPSSPORT"[i]) - return false; + for (i = 0; i < 8; i++) + if (trans[header[i + 456]] != "SPSSPORT"[i]) + return false; return true; } + +static struct casereader_class por_file_casereader_class = + { + por_file_casereader_read, + por_file_casereader_destroy, + NULL, + NULL, + };