From: Ben Pfaff Date: Fri, 24 Sep 2010 04:03:36 +0000 (-0700) Subject: data-in: Get rid of first_column, last_column arguments. X-Git-Tag: sav-api~1 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=3da49359c52cb783db907cc197847bbd5e721c97 data-in: Get rid of first_column, last_column arguments. Most callers can't provide this arguments, and so in the interest of making the data_in() interface simpler, it seems better to put the callers in charge of dealing with error messages, since they better know their own context. --- diff --git a/perl-module/PSPP.xs b/perl-module/PSPP.xs index bf90d986f3..15d3b606cb 100644 --- a/perl-module/PSPP.xs +++ b/perl-module/PSPP.xs @@ -654,9 +654,16 @@ CODE: if ( ifmt ) { struct substring ss = ss_cstr (SvPV_nolen (sv)); - if ( ! data_in (ss, LEGACY_NATIVE, ifmt->type, 0, 0, - case_data_rw (c, v), var_get_width (v), - dict_get_encoding (sfi->dict))) + char *error; + bool ok; + + error = data_in (ss, LEGACY_NATIVE, ifmt->type, + case_data_rw (c, v), var_get_width (v), + dict_get_encoding (sfi->dict)); + ok = error == NULL; + free (error); + + if ( !ok ) { RETVAL = 0; goto finish; diff --git a/src/data/data-in.c b/src/data/data-in.c index de19d67048..d36aefb5e8 100644 --- a/src/data/data-in.c +++ b/src/data/data-in.c @@ -38,7 +38,6 @@ #include "libpspp/i18n.h" #include "libpspp/integer-format.h" #include "libpspp/legacy-encoding.h" -#include "libpspp/message.h" #include "libpspp/misc.h" #include "libpspp/str.h" #include "settings.h" @@ -60,19 +59,13 @@ struct data_in union value *output; /* Destination. */ int width; /* Output width. */ - - int first_column; /* First column of field; 0 if inapplicable. */ - int last_column; /* Last column. */ }; -typedef bool data_in_parser_func (struct data_in *); +typedef char *data_in_parser_func (struct data_in *); #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \ static data_in_parser_func parse_##METHOD; #include "format.def" -static void data_warning (const struct data_in *, const char *, ...) - PRINTF_FORMAT (2, 3); - static void default_result (struct data_in *); static bool trim_spaces_and_check_missing (struct data_in *); @@ -85,9 +78,9 @@ static int hexit_value (int c); initialized with the given WIDTH (0 for a numeric field, otherwise the string width). If FORMAT is FMT_A, then OUTPUT_ENCODING must specify the correct encoding for OUTPUT (normally obtained via dict_get_encoding()). */ -bool +char * data_in (struct substring input, const char *input_encoding, - enum fmt_type format, int first_column, int last_column, + enum fmt_type format, union value *output, int width, const char *output_encoding) { static data_in_parser_func *const handlers[FMT_NUMBER_OF_FORMATS] = @@ -101,7 +94,7 @@ data_in (struct substring input, const char *input_encoding, enum fmt_category cat; const char *dest_encoding; char *s; - bool ok; + char *error; assert ((width != 0) == fmt_is_string (format)); @@ -110,13 +103,10 @@ data_in (struct substring input, const char *input_encoding, i.output = output; i.width = width; - i.first_column = first_column; - i.last_column = last_column; - if (ss_is_empty (input)) { default_result (&i); - return true; + return NULL; } cat = fmt_get_category (format); @@ -161,13 +151,31 @@ data_in (struct substring input, const char *input_encoding, s = NULL; } - ok = handlers[i.format] (&i); - if (!ok) + error = handlers[i.format] (&i); + if (error != NULL) default_result (&i); free (s); - return ok; + return error; +} + +bool +data_in_msg (struct substring input, const char *input_encoding, + enum fmt_type format, + union value *output, int width, const char *output_encoding) +{ + char *error = data_in (input, input_encoding, format, + output, width, output_encoding); + if (error != NULL) + { + msg (SW,_("Data is not valid as format %s: %s"), + fmt_name (format), error); + free (error); + return false; + } + else + return true; } static bool @@ -266,7 +274,7 @@ data_in_imply_decimals (struct substring input, const char *input_encoding, /* Format parsers. */ /* Parses F, COMMA, DOT, DOLLAR, PCT, and E input formats. */ -static bool +static char * parse_number (struct data_in *i) { const struct fmt_number_style *style = @@ -285,7 +293,7 @@ parse_number (struct data_in *i) /* Trim spaces and check for missing value representation. */ if (trim_spaces_and_check_missing (i)) - return true; + return NULL; ds_init_empty (&tmp); ds_extend (&tmp, 64); @@ -364,12 +372,13 @@ parse_number (struct data_in *i) if (!ss_is_empty (i->input)) { + char *error; if (ds_is_empty (&tmp)) - data_warning (i, _("Field contents are not numeric.")); + error = xstrdup (_("Field contents are not numeric.")); else - data_warning (i, _("Number followed by garbage.")); + error = xstrdup (_("Number followed by garbage.")); ds_destroy (&tmp); - return false; + return error; } /* Let c_strtod() do the conversion. */ @@ -378,35 +387,34 @@ parse_number (struct data_in *i) i->output->f = c_strtod (ds_cstr (&tmp), &tail); if (*tail != '\0') { - data_warning (i, _("Invalid numeric syntax.")); errno = save_errno; ds_destroy (&tmp); - return false; + return xstrdup (_("Invalid numeric syntax.")); } else if (errno == ERANGE) { if (fabs (i->output->f) > 1) { - data_warning (i, _("Too-large number set to system-missing.")); i->output->f = SYSMIS; + ds_destroy (&tmp); + return xstrdup (_("Too-large number set to system-missing.")); } else { - data_warning (i, _("Too-small number set to zero.")); i->output->f = 0.0; + ds_destroy (&tmp); + return xstrdup (_("Too-small number set to zero.")); } } else - { - errno = save_errno; - } + errno = save_errno; ds_destroy (&tmp); - return true; + return NULL; } /* Parses N format. */ -static bool +static char * parse_N (struct data_in *i) { int c; @@ -415,18 +423,15 @@ parse_N (struct data_in *i) while ((c = ss_get_char (&i->input)) != EOF) { if (!c_isdigit (c)) - { - data_warning (i, _("All characters in field must be digits.")); - return false; - } + return xstrdup (_("All characters in field must be digits.")); i->output->f = i->output->f * 10.0 + (c - '0'); } - return true; + return NULL; } /* Parses PIBHEX format. */ -static bool +static char * parse_PIBHEX (struct data_in *i) { double n; @@ -437,19 +442,16 @@ parse_PIBHEX (struct data_in *i) while ((c = ss_get_char (&i->input)) != EOF) { if (!c_isxdigit (c)) - { - data_warning (i, _("Unrecognized character in field.")); - return false; - } + return xstrdup (_("Unrecognized character in field.")); n = n * 16.0 + hexit_value (c); } i->output->f = n; - return true; + return NULL; } /* Parses RBHEX format. */ -static bool +static char * parse_RBHEX (struct data_in *i) { double d; @@ -461,21 +463,15 @@ parse_RBHEX (struct data_in *i) int hi = ss_get_char (&i->input); int lo = ss_get_char (&i->input); if (lo == EOF) - { - data_warning (i, _("Field must have even length.")); - return false; - } + return xstrdup (_("Field must have even length.")); else if (!c_isxdigit (hi) || !c_isxdigit (lo)) - { - data_warning (i, _("Field must contain only hex digits.")); - return false; - } + return xstrdup (_("Field must contain only hex digits.")); ((unsigned char *) &d)[j] = 16 * hexit_value (hi) + hexit_value (lo); } i->output->f = d; - return true; + return NULL; } /* Digits for Z format. */ @@ -507,7 +503,7 @@ is_negative_z_digit (int c) } /* Parses Z format. */ -static bool +static char * parse_Z (struct data_in *i) { struct string tmp; @@ -519,7 +515,7 @@ parse_Z (struct data_in *i) /* Trim spaces and check for missing value representation. */ if (trim_spaces_and_check_missing (i)) - return true; + return NULL; ds_init_empty (&tmp); ds_extend (&tmp, 64); @@ -545,18 +541,21 @@ parse_Z (struct data_in *i) else { ds_destroy (&tmp); - return false; + return xstrdup (_("Invalid zoned decimal syntax.")); } } if (!ss_is_empty (i->input)) { + char *error; + if (ds_length (&tmp) == 1) - data_warning (i, _("Field contents are not numeric.")); + error = xstrdup (_("Field contents are not numeric.")); else - data_warning (i, _("Number followed by garbage.")); + error = xstrdup (_("Number followed by garbage.")); + ds_destroy (&tmp); - return false; + return error; } /* Let c_strtod() do the conversion. */ @@ -567,24 +566,26 @@ parse_Z (struct data_in *i) { if (fabs (i->output->f) > 1) { - data_warning (i, _("Too-large number set to system-missing.")); i->output->f = SYSMIS; + ds_destroy (&tmp); + return xstrdup (_("Too-large number set to system-missing.")); } else { - data_warning (i, _("Too-small number set to zero.")); i->output->f = 0.0; + ds_destroy (&tmp); + return xstrdup (_("Too-small number set to zero.")); } } else errno = save_errno; ds_destroy (&tmp); - return true; + return NULL; } /* Parses IB format. */ -static bool +static char * parse_IB (struct data_in *i) { size_t bytes; @@ -604,17 +605,17 @@ parse_IB (struct data_in *i) i->output->f = -(double) -value; } - return true; + return NULL; } /* Parses PIB format. */ -static bool +static char * parse_PIB (struct data_in *i) { i->output->f = integer_get (settings_get_input_integer_format (), ss_data (i->input), MIN (8, ss_length (i->input))); - return true; + return NULL; } /* Consumes the first character of S. Stores its high 4 bits in @@ -629,7 +630,7 @@ get_nibbles (struct substring *s, int *high_nibble, int *low_nibble) } /* Parses P format. */ -static bool +static char * parse_P (struct data_in *i) { int high_nibble, low_nibble; @@ -640,24 +641,24 @@ parse_P (struct data_in *i) { get_nibbles (&i->input, &high_nibble, &low_nibble); if (high_nibble > 9 || low_nibble > 9) - return false; + return xstrdup (_("Invalid syntax for P field.")); i->output->f = (100 * i->output->f) + (10 * high_nibble) + low_nibble; } get_nibbles (&i->input, &high_nibble, &low_nibble); if (high_nibble > 9) - return false; + return xstrdup (_("Invalid syntax for P field.")); i->output->f = (10 * i->output->f) + high_nibble; if (low_nibble < 10) i->output->f = (10 * i->output->f) + low_nibble; else if (low_nibble == 0xb || low_nibble == 0xd) i->output->f = -i->output->f; - return true; + return NULL; } /* Parses PK format. */ -static bool +static char * parse_PK (struct data_in *i) { i->output->f = 0.0; @@ -669,16 +670,16 @@ parse_PK (struct data_in *i) if (high_nibble > 9 || low_nibble > 9) { i->output->f = SYSMIS; - return true; + return NULL; } i->output->f = (100 * i->output->f) + (10 * high_nibble) + low_nibble; } - return true; + return NULL; } /* Parses RB format. */ -static bool +static char * parse_RB (struct data_in *i) { enum float_format ff = settings_get_input_float_format (); @@ -689,11 +690,11 @@ parse_RB (struct data_in *i) else i->output->f = SYSMIS; - return true; + return NULL; } /* Parses A format. */ -static bool +static char * parse_A (struct data_in *i) { /* This is equivalent to buf_copy_rpad, except that we posibly @@ -708,11 +709,11 @@ parse_A (struct data_in *i) if (dst_size > src_size) memset (&dst[src_size], ' ', dst_size - src_size); - return true; + return NULL; } /* Parses AHEX format. */ -static bool +static char * parse_AHEX (struct data_in *i) { uint8_t *s = value_str_rw (i->output, i->width); @@ -725,16 +726,10 @@ parse_AHEX (struct data_in *i) if (hi == EOF) break; else if (lo == EOF) - { - data_warning (i, _("Field must have even length.")); - return false; - } + return xstrdup (_("Field must have even length.")); if (!c_isxdigit (hi) || !c_isxdigit (lo)) - { - data_warning (i, _("Field must contain only hex digits.")); - return false; - } + return xstrdup (_("Field must contain only hex digits.")); if (j < i->width) s[j] = hexit_value (hi) * 16 + hexit_value (lo); @@ -742,7 +737,7 @@ parse_AHEX (struct data_in *i) memset (&s[j], ' ', i->width - j); - return true; + return NULL; } /* Date & time format components. */ @@ -759,7 +754,7 @@ enum time_sign MAX_DIGITS characters in I, storing the result into *RESULT. Returns true if successful, false if no integer was present. */ -static bool +static char * WARN_UNUSED_RESULT parse_int (struct data_in *i, long *result, size_t max_digits) { struct substring head = ss_head (i->input, max_digits); @@ -767,28 +762,25 @@ parse_int (struct data_in *i, long *result, size_t max_digits) if (n) { ss_advance (&i->input, n); - return true; + return NULL; } else - { - data_warning (i, _("Syntax error in date field.")); - return false; - } + return xstrdup (_("Syntax error in date field.")); } /* Parses a date integer between 1 and 31 from I, storing it into *DAY. Returns true if successful, false if no date was present. */ -static bool +static char * parse_day (struct data_in *i, long *day) { - if (!parse_int (i, day, SIZE_MAX)) - return false; + char *error = parse_int (i, day, SIZE_MAX); + if (error != NULL) + return error; if (*day >= 1 && *day <= 31) - return true; + return NULL; - data_warning (i, _("Day (%ld) must be between 1 and 31."), *day); - return false; + return xasprintf (_("Day (%ld) must be between 1 and 31."), *day); } /* Parses an integer from the beginning of I. @@ -797,11 +789,12 @@ parse_day (struct data_in *i, long *day) If *TIME_SIGN is SIGN_NO_TIME, allows a sign to precede the time and sets *TIME_SIGN. Otherwise, does not allow a sign. Returns true if successful, false if no integer was present. */ -static bool +static char * parse_time_units (struct data_in *i, double seconds_per_unit, enum time_sign *time_sign, double *time) { + char *error; long units; if (*time_sign == SIGN_NO_TIME) @@ -814,28 +807,25 @@ parse_time_units (struct data_in *i, double seconds_per_unit, *time_sign = SIGN_POSITIVE; } } - if (!parse_int (i, &units, SIZE_MAX)) - return false; + error = parse_int (i, &units, SIZE_MAX); + if (error != NULL) + return error; if (units < 0) - { - data_warning (i, _("Syntax error in date field.")); - return false; - } + return xstrdup (_("Syntax error in date field.")); *time += units * seconds_per_unit; - return true; + return NULL; } /* Parses a data delimiter from the beginning of I. Returns true if successful, false if no delimiter was present. */ -static bool +static char * parse_date_delimiter (struct data_in *i) { if (ss_ltrim (&i->input, ss_cstr ("-/.," CC_SPACES))) - return true; + return NULL; - data_warning (i, _("Delimiter expected between fields in date.")); - return false; + return xstrdup (_("Delimiter expected between fields in date.")); } /* Parses spaces at the beginning of I. */ @@ -876,15 +866,16 @@ match_name (struct substring token, const char *const *names, long *output) /* Parses a month name or number from the beginning of I, storing the month (in range 1...12) into *MONTH. Returns true if successful, false if no month was present. */ -static bool +static char * parse_month (struct data_in *i, long *month) { if (c_isdigit (ss_first (i->input))) { - if (!parse_int (i, month, SIZE_MAX)) - return false; + char *error = parse_int (i, month, SIZE_MAX); + if (error != NULL) + return error; if (*month >= 1 && *month <= 12) - return true; + return NULL; } else { @@ -905,22 +896,22 @@ parse_month (struct data_in *i, long *month) struct substring token = parse_name_token (i); if (match_name (ss_head (token, 3), english_names, month) || match_name (ss_head (token, 4), roman_names, month)) - return true; + return NULL; } - data_warning (i, _("Unrecognized month format. Months may be specified " - "as Arabic or Roman numerals or as at least 3 letters " - "of their English names.")); - return false; + return xstrdup (_("Unrecognized month format. Months may be specified " + "as Arabic or Roman numerals or as at least 3 letters " + "of their English names.")); } /* Parses a year of at most MAX_DIGITS from the beginning of I, storing a "4-digit" year into *YEAR. */ -static bool +static char * parse_year (struct data_in *i, long *year, size_t max_digits) { - if (!parse_int (i, year, max_digits)) - return false; + char *error = parse_int (i, year, max_digits); + if (error != NULL) + return error; if (*year >= 0 && *year <= 99) { @@ -933,28 +924,26 @@ parse_year (struct data_in *i, long *year, size_t max_digits) *year += epoch_century + 100; } if (*year >= 1582 || *year <= 19999) - return true; + return NULL; - data_warning (i, _("Year (%ld) must be between 1582 and 19999."), *year); - return false; + return xasprintf (_("Year (%ld) must be between 1582 and 19999."), *year); } /* Returns true if input in I has been exhausted, false otherwise. */ -static bool +static char * parse_trailer (struct data_in *i) { if (ss_is_empty (i->input)) - return true; + return NULL; - data_warning (i, _("Trailing garbage `%.*s' following date."), - (int) ss_length (i->input), ss_data (i->input)); - return false; + return xasprintf (_("Trailing garbage `%.*s' following date."), + (int) ss_length (i->input), ss_data (i->input)); } /* Parses a 3-digit Julian day-of-year value from I into *YDAY. Returns true if successful, false on failure. */ -static bool +static char * parse_yday (struct data_in *i, long *yday) { struct substring num_s; @@ -962,98 +951,92 @@ parse_yday (struct data_in *i, long *yday) ss_get_chars (&i->input, 3, &num_s); if (ss_span (num_s, ss_cstr (CC_DIGITS)) != 3) - { - data_warning (i, _("Julian day must have exactly three digits.")); - return false; - } + return xstrdup (_("Julian day must have exactly three digits.")); else if (!ss_get_long (&num_s, &num) || num < 1 || num > 366) - { - data_warning (i, _("Julian day (%ld) must be between 1 and 366."), num); - return false; - } + return xasprintf (_("Julian day (%ld) must be between 1 and 366."), num); *yday = num; - return true; + return NULL; } /* Parses a quarter-of-year integer between 1 and 4 from I. Stores the corresponding month into *MONTH. Returns true if successful, false if no quarter was present. */ -static bool +static char * parse_quarter (struct data_in *i, long int *month) { long quarter; + char *error; - if (!parse_int (i, &quarter, SIZE_MAX)) - return false; + error = parse_int (i, &quarter, SIZE_MAX); + if (error != NULL) + return error; if (quarter >= 1 && quarter <= 4) { *month = (quarter - 1) * 3 + 1; - return true; + return NULL; } - data_warning (i, _("Quarter (%ld) must be between 1 and 4."), quarter); - return false; + return xasprintf (_("Quarter (%ld) must be between 1 and 4."), quarter); } /* Parses a week-of-year integer between 1 and 53 from I, Stores the corresponding year-of-day into *YDAY. Returns true if successful, false if no week was present. */ -static bool +static char * parse_week (struct data_in *i, long int *yday) { + char *error; long week; - if (!parse_int (i, &week, SIZE_MAX)) - return false; + error = parse_int (i, &week, SIZE_MAX); + if (error != NULL) + return error; if (week >= 1 && week <= 53) { *yday = (week - 1) * 7 + 1; - return true; + return NULL; } - data_warning (i, _("Week (%ld) must be between 1 and 53."), week); - return false; + return xasprintf (_("Week (%ld) must be between 1 and 53."), week); } /* Parses a time delimiter from the beginning of I. Returns true if successful, false if no delimiter was present. */ -static bool +static char * parse_time_delimiter (struct data_in *i) { if (ss_ltrim (&i->input, ss_cstr (":" CC_SPACES)) > 0) - return true; + return NULL; - data_warning (i, _("Delimiter expected between fields in time.")); - return false; + return xstrdup (_("Delimiter expected between fields in time.")); } /* Parses minutes and optional seconds from the beginning of I. The time is converted into seconds, which are added to *TIME. Returns true if successful, false if an error was found. */ -static bool +static char * parse_minute_second (struct data_in *i, double *time) { long minute; char buf[64]; + char *error; char *cp; /* Parse minutes. */ - if (!parse_int (i, &minute, SIZE_MAX)) - return false; + error = parse_int (i, &minute, SIZE_MAX); + if (error != NULL) + return error; if (minute < 0 || minute > 59) - { - data_warning (i, _("Minute (%ld) must be between 0 and 59."), minute); - return false; - } + return xasprintf (_("Minute (%ld) must be between 0 and 59."), minute); *time += 60. * minute; /* Check for seconds. */ if (ss_ltrim (&i->input, ss_cstr (":" CC_SPACES)) == 0 || !c_isdigit (ss_first (i->input))) - return true; + return NULL; /* Parse seconds. */ cp = buf; @@ -1067,13 +1050,13 @@ parse_minute_second (struct data_in *i, double *time) *time += strtod (buf, NULL); - return true; + return NULL; } /* Parses a weekday name from the beginning of I, storing a value of 1=Sunday...7=Saturday into *WEEKDAY. Returns true if successful, false if an error was found. */ -static bool +static char * parse_weekday (struct data_in *i, long *weekday) { static const char *const weekday_names[] = @@ -1085,51 +1068,53 @@ parse_weekday (struct data_in *i, long *weekday) struct substring token = parse_name_token (i); bool ok = match_name (ss_head (token, 2), weekday_names, weekday); if (!ok) - data_warning (i, _("Unrecognized weekday name. At least the first two " - "letters of an English weekday name must be " - "specified.")); - return ok; + return xstrdup (_("Unrecognized weekday name. At least the first two " + "letters of an English weekday name must be " + "specified.")); + return NULL; } /* Date & time formats. */ /* Parses WKDAY format. */ -static bool +static char * parse_WKDAY (struct data_in *i) { long weekday; + char *error; if (trim_spaces_and_check_missing (i)) - return true; + return NULL; - if (!parse_weekday (i, &weekday) - || !parse_trailer (i)) - return false; + error = parse_weekday (i, &weekday); + if (error == NULL) + error = parse_trailer (i); i->output->f = weekday; - return true; + return error; } /* Parses MONTH format. */ -static bool +static char * parse_MONTH (struct data_in *i) { long month; + char *error; if (trim_spaces_and_check_missing (i)) - return true; + return NULL; - if (!parse_month (i, &month) - || !parse_trailer (i)) - return false; + error = parse_month (i, &month); + if (error == NULL) + error = parse_trailer (i); i->output->f = month; - return true; + return error; } /* Parses DATE, ADATE, EDATE, JDATE, SDATE, QYR, MOYR, KWYR, DATETIME, TIME and DTIME formats. */ -static bool +static char * parse_date (struct data_in *i) { long int year = INT_MIN; @@ -1141,28 +1126,27 @@ parse_date (struct data_in *i) const char *template = fmt_date_template (i->format); size_t template_width = strlen (template); + char *error; if (trim_spaces_and_check_missing (i)) - return true; + return NULL; while (*template != '\0') { unsigned char ch = *template; int count = 1; - bool ok; while (template[count] == ch) count++; template += count; - ok = true; switch (ch) { case 'd': - ok = count < 3 ? parse_day (i, &day) : parse_yday (i, &yday); + error = count < 3 ? parse_day (i, &day) : parse_yday (i, &yday); break; case 'm': - ok = parse_month (i, &month); + error = parse_month (i, &month); break; case 'y': { @@ -1176,50 +1160,51 @@ parse_date (struct data_in *i) else max_digits = 2; } - ok = parse_year (i, &year, max_digits); + error = parse_year (i, &year, max_digits); } break; case 'q': - ok = parse_quarter (i, &month); + error = parse_quarter (i, &month); break; case 'w': - ok = parse_week (i, &yday); + error = parse_week (i, &yday); break; case 'D': - ok = parse_time_units (i, 60. * 60. * 24., &time_sign, &time); + error = parse_time_units (i, 60. * 60. * 24., &time_sign, &time); break; case 'H': - ok = parse_time_units (i, 60. * 60., &time_sign, &time); + error = parse_time_units (i, 60. * 60., &time_sign, &time); break; case 'M': - ok = parse_minute_second (i, &time); + error = parse_minute_second (i, &time); break; case '-': case '/': case '.': case 'X': - ok = parse_date_delimiter (i); + error = parse_date_delimiter (i); break; case ':': - ok = parse_time_delimiter (i); + error = parse_time_delimiter (i); case ' ': parse_spaces (i); + error = NULL; break; default: assert (count == 1); if (!ss_match_char (&i->input, c_toupper (ch)) && !ss_match_char (&i->input, c_tolower (ch))) - { - data_warning (i, _("`%c' expected in date field."), ch); - return false; - } + error = xasprintf (_("`%c' expected in date field."), ch); + else + error = NULL; break; } - if (!ok) - return false; + if (error != NULL) + return error; } - if (!parse_trailer (i)) - return false; + error = parse_trailer (i); + if (error != NULL) + return error; if (year != INT_MIN) { @@ -1228,50 +1213,18 @@ parse_date (struct data_in *i) ofs = calendar_gregorian_to_offset (year, month, day, &error); if (ofs == SYSMIS) - { - data_warning (i, "%s", error); - free (error); - return false; - } + return error; date = (yday - 1 + ofs) * 60. * 60. * 24.; } else date = 0.; i->output->f = date + (time_sign == SIGN_NEGATIVE ? -time : time); - return true; + return NULL; } - /* Utility functions. */ -/* Outputs FORMAT with as a warning for input I. */ -static void -data_warning (const struct data_in *i, const char *format, ...) -{ - va_list args; - struct msg m; - struct string text; - - ds_init_empty (&text); - ds_put_char (&text, '('); - ds_put_format (&text, _("%s field) "), fmt_name (i->format)); - - va_start (args, format); - ds_put_vformat (&text, format, args); - va_end (args); - - m.category = MSG_C_DATA; - m.severity = MSG_S_WARNING; - m.text = ds_cstr (&text); - m.where.file_name = NULL; - m.where.line_number = 0; - m.where.first_column = i->first_column; - m.where.last_column = i->last_column; - - msg_emit (&m); -} - /* Sets the default result for I. For a numeric format, this is the value set on SET BLANKS (typically system-missing); for a string format, it is all diff --git a/src/data/data-in.h b/src/data/data-in.h index dbf2fa7caa..ceb023e998 100644 --- a/src/data/data-in.h +++ b/src/data/data-in.h @@ -25,9 +25,13 @@ union value; struct dictionary; -bool data_in (struct substring input, const char *input_encoding, - enum fmt_type, int first_column, int last_column, - union value *output, int width, const char *output_encoding); +char *data_in (struct substring input, const char *input_encoding, + enum fmt_type, + union value *output, int width, const char *output_encoding); + +bool data_in_msg (struct substring input, const char *input_encoding, + enum fmt_type, + union value *output, int width, const char *output_encoding); void data_in_imply_decimals (struct substring input, const char *encoding, enum fmt_type format, int d, union value *output); diff --git a/src/language/data-io/data-parser.c b/src/language/data-io/data-parser.c index 800ae57b50..714b2a952e 100644 --- a/src/language/data-io/data-parser.c +++ b/src/language/data-io/data-parser.c @@ -395,7 +395,6 @@ data_parser_parse (struct data_parser *parser, struct dfm_reader *reader, && dfm_get_percent_read (reader) >= parser->percent_cases) return false; - dfm_push (reader); if (parser->type == DP_DELIMITED) { if (parser->span) @@ -405,7 +404,6 @@ data_parser_parse (struct data_parser *parser, struct dfm_reader *reader, } else retval = parse_fixed (parser, reader, c); - dfm_pop (reader); return retval; } @@ -502,6 +500,25 @@ cut_field (const struct data_parser *parser, struct dfm_reader *reader, return true; } +static void +parse_error (const struct dfm_reader *reader, const struct field *field, + int first_column, int last_column, char *error) +{ + struct msg m; + + m.category = MSG_C_DATA; + m.severity = MSG_S_WARNING; + m.where.file_name = CONST_CAST (char *, dfm_get_file_name (reader)); + m.where.line_number = dfm_get_line_number (reader); + m.where.first_column = first_column; + m.where.last_column = last_column; + m.text = xasprintf (_("Data for variable %s is not valid as format %s: %s"), + field->name, fmt_name (field->format.type), error); + msg_emit (&m); + + free (error); +} + /* Reads a case from READER into C, parsing it according to fixed-format syntax rules in PARSER. Returns true if successful, false at end of file or on I/O error. */ @@ -536,13 +553,16 @@ parse_fixed (const struct data_parser *parser, struct dfm_reader *reader, struct substring s = ss_substr (line, f->first_column - 1, f->format.w); union value *value = case_data_rw_idx (c, f->case_idx); - - data_in (s, input_encoding, f->format.type, - f->first_column, f->first_column + f->format.w, - value, fmt_var_width (&f->format), output_encoding); - - data_in_imply_decimals (s, input_encoding, f->format.type, - f->format.d, value); + char *error = data_in (s, input_encoding, f->format.type, + value, fmt_var_width (&f->format), + output_encoding); + + if (error == NULL) + data_in_imply_decimals (s, input_encoding, f->format.type, + f->format.d, value); + else + parse_error (reader, f, f->first_column, + f->first_column + f->format.w, error); } dfm_forward_record (reader); @@ -567,6 +587,7 @@ parse_delimited_span (const struct data_parser *parser, { struct substring s; int first_column, last_column; + char *error; /* Cut out a field and read in a new record if necessary. */ while (!cut_field (parser, reader, @@ -584,9 +605,11 @@ parse_delimited_span (const struct data_parser *parser, } } - data_in (s, input_encoding, f->format.type, first_column, last_column, - case_data_rw_idx (c, f->case_idx), - fmt_var_width (&f->format), output_encoding); + error = data_in (s, input_encoding, f->format.type, + case_data_rw_idx (c, f->case_idx), + fmt_var_width (&f->format), output_encoding); + if (error != NULL) + parse_error (reader, f, first_column, last_column, error); } ds_destroy (&tmp); return true; @@ -612,6 +635,8 @@ parse_delimited_no_span (const struct data_parser *parser, for (f = parser->fields; f < end; f++) { int first_column, last_column; + char *error; + if (!cut_field (parser, reader, &first_column, &last_column, &tmp, &s)) { if (f < end - 1 && settings_get_undefined ()) @@ -625,9 +650,11 @@ parse_delimited_no_span (const struct data_parser *parser, goto exit; } - data_in (s, input_encoding, f->format.type, first_column, last_column, - case_data_rw_idx (c, f->case_idx), - fmt_var_width (&f->format), output_encoding); + error = data_in (s, input_encoding, f->format.type, + case_data_rw_idx (c, f->case_idx), + fmt_var_width (&f->format), output_encoding); + if (error != NULL) + parse_error (reader, f, first_column, last_column, error); } s = dfm_get_record (reader); diff --git a/src/language/data-io/data-reader.c b/src/language/data-io/data-reader.c index e9bcbfa9dc..061505f889 100644 --- a/src/language/data-io/data-reader.c +++ b/src/language/data-io/data-reader.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc. + Copyright (C) 1997-2004, 2006, 2010 Free Software Foundation, Inc. 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 @@ -677,20 +677,16 @@ dfm_get_column (const struct dfm_reader *r, const char *p) return ds_pointer_to_position (&r->line, p) + 1; } -/* Pushes the file name and line number on the fn/ln stack. */ -void -dfm_push (struct dfm_reader *r) +const char * +dfm_get_file_name (const struct dfm_reader *r) { - if (r->fh != fh_inline_file ()) - msg_push_msg_locator (&r->where); + return fh_get_referent (r->fh) == FH_REF_FILE ? r->where.file_name : NULL; } -/* Pops the file name and line number from the fn/ln stack. */ -void -dfm_pop (struct dfm_reader *r) +int +dfm_get_line_number (const struct dfm_reader *r) { - if (r->fh != fh_inline_file ()) - msg_pop_msg_locator (&r->where); + return fh_get_referent (r->fh) == FH_REF_FILE ? r->where.line_number : -1; } /* BEGIN DATA...END DATA procedure. */ diff --git a/src/language/data-io/data-reader.h b/src/language/data-io/data-reader.h index 308701c253..7f7355da07 100644 --- a/src/language/data-io/data-reader.h +++ b/src/language/data-io/data-reader.h @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2010 Free Software Foundation, Inc. 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 @@ -49,8 +49,8 @@ size_t dfm_column_start (const struct dfm_reader *); size_t dfm_columns_past_end (const struct dfm_reader *); size_t dfm_get_column (const struct dfm_reader *, const char *); -/* File stack. */ -void dfm_push (struct dfm_reader *); -void dfm_pop (struct dfm_reader *); +/* Information. */ +const char *dfm_get_file_name (const struct dfm_reader *); +int dfm_get_line_number (const struct dfm_reader *); #endif /* data-reader.h */ diff --git a/src/language/expressions/operations.def b/src/language/expressions/operations.def index b7783257cd..0286acf91e 100644 --- a/src/language/expressions/operations.def +++ b/src/language/expressions/operations.def @@ -581,8 +581,19 @@ string function RTRIM (string s, string c) function NUMBER (string s, ni_format f) { union value out; - data_in (ss_head (s, f->w), LEGACY_NATIVE, f->type, 0, 0, &out, 0, NULL); + char *error; + + if (s.length > f->w) + s.length = f->w; + error = data_in (s, LEGACY_NATIVE, f->type, &out, 0, NULL); + if (error == NULL) data_in_imply_decimals (s, LEGACY_NATIVE, f->type, f->d, &out); + else + { + msg (SE, "Cannot parse \"%.*s\" as format %s: %s", + (int) s.length, s.string, fmt_name (f->type), error); + free (error); + } return out.f; } diff --git a/src/language/lexer/value-parser.c b/src/language/lexer/value-parser.c index 8cbbab4c82..269d24bca4 100644 --- a/src/language/lexer/value-parser.c +++ b/src/language/lexer/value-parser.c @@ -103,9 +103,13 @@ parse_number (struct lexer *lexer, double *x, const enum fmt_type *format) else if (lex_is_string (lexer) && format != NULL) { union value v; - assert (! (fmt_get_category (*format) & ( FMT_CAT_STRING ))); - data_in (ds_ss (lex_tokstr (lexer)), LEGACY_NATIVE, *format, 0, 0, - &v, 0, NULL); + + assert (fmt_get_category (*format) != FMT_CAT_STRING); + + if (!data_in_msg (ds_ss (lex_tokstr (lexer)), LEGACY_NATIVE, + *format, &v, 0, NULL)) + return false; + lex_get (lexer); *x = v.f; if (*x == SYSMIS) diff --git a/src/language/stats/flip.c b/src/language/stats/flip.c index 7390b08c0a..77881868b0 100644 --- a/src/language/stats/flip.c +++ b/src/language/stats/flip.c @@ -407,7 +407,7 @@ flip_casereader_read (struct casereader *reader, void *flip_) c = case_create (casereader_get_proto (reader)); encoding = dict_get_encoding (flip->dict); data_in (ss_cstr (flip->old_names.names[flip->cases_read]), encoding, - FMT_A, 0, 0, case_data_rw_idx (c, 0), 8, encoding); + FMT_A, case_data_rw_idx (c, 0), 8, encoding); for (i = 0; i < flip->n_cases; i++) { diff --git a/src/language/xforms/recode.c b/src/language/xforms/recode.c index c9787b11bd..5cfad0ed76 100644 --- a/src/language/xforms/recode.c +++ b/src/language/xforms/recode.c @@ -629,11 +629,13 @@ find_src_string (struct recode_trns *trns, const uint8_t *value, case MAP_CONVERT: { union value uv; + char *error; + + error = data_in (ss_buffer (CHAR_CAST_BUG (char *, value), width), + LEGACY_NATIVE, FMT_F, &uv, 0, encoding); + match = error == NULL; + free (error); - msg_disable (); - match = data_in (ss_buffer (CHAR_CAST_BUG (char *, value), width), - LEGACY_NATIVE, FMT_F, 0, 0, &uv, 0, encoding); - msg_enable (); out->value.f = uv.f; break; } diff --git a/src/libpspp/message.c b/src/libpspp/message.c index 8bd6af956d..0ca2749d67 100644 --- a/src/libpspp/message.c +++ b/src/libpspp/message.c @@ -271,7 +271,7 @@ process_msg (const struct msg *m) void msg_emit (struct msg *m) { - if ( s_stream ) + if ( s_stream && m->where.file_name == NULL ) { struct msg_locator loc; diff --git a/src/ui/gui/helper.c b/src/ui/gui/helper.c index b8936e6c3b..1ad77bc96e 100644 --- a/src/ui/gui/helper.c +++ b/src/ui/gui/helper.c @@ -97,10 +97,8 @@ text_to_value (const gchar *text, } value_init (val, width); - msg_disable (); - data_in (ss_cstr (text), UTF8, format->type, 0, 0, val, width, - dict_get_encoding (dict->dict)); - msg_enable (); + free (data_in (ss_cstr (text), UTF8, format->type, val, width, + dict_get_encoding (dict->dict))); return val; } diff --git a/src/ui/gui/psppire-data-store.c b/src/ui/gui/psppire-data-store.c index 7d26f5629b..7deb102286 100644 --- a/src/ui/gui/psppire-data-store.c +++ b/src/ui/gui/psppire-data-store.c @@ -959,8 +959,8 @@ psppire_data_store_data_in (PsppireDataStore *ds, casenumber casenum, gint idx, FALSE); value_init (&value, width); ok = (datasheet_get_value (ds->datasheet, casenum, idx, &value) - && data_in (input, UTF8, fmt->type, 0, 0, &value, width, - dict_get_encoding (dict->dict)) + && data_in_msg (input, UTF8, fmt->type, &value, width, + dict_get_encoding (dict->dict)) && datasheet_put_value (ds->datasheet, casenum, idx, &value)); value_destroy (&value, width); diff --git a/src/ui/gui/text-data-import-dialog.c b/src/ui/gui/text-data-import-dialog.c index c9227ff854..13c6d96804 100644 --- a/src/ui/gui/text-data-import-dialog.c +++ b/src/ui/gui/text-data-import-dialog.c @@ -1775,19 +1775,19 @@ parse_field (struct import_assistant *ia, tooltip = NULL; if (field.string != NULL) { - msg_disable (); + char *error; - if (!data_in (field, LEGACY_NATIVE, in->type, 0, 0, &val, - var_get_width (var), dict_get_encoding (ia->formats.dict))) + error = data_in (field, LEGACY_NATIVE, in->type, &val, + var_get_width (var), + dict_get_encoding (ia->formats.dict)); + if (error != NULL) { - char fmt_string[FMT_STRING_LEN_MAX + 1]; - fmt_to_string (in, fmt_string); - tooltip = xasprintf (_("Field content `%.*s' cannot be parsed in " - "format %s."), + tooltip = xasprintf (_("Cannot parse field content `%.*s' as " + "format %s: %s"), (int) field.length, field.string, - fmt_string); + fmt_name (in->type), error); + free (error); } - msg_enable (); } else { diff --git a/src/ui/syntax-gen.c b/src/ui/syntax-gen.c index aae8c972f9..b49911f5e8 100644 --- a/src/ui/syntax-gen.c +++ b/src/ui/syntax-gen.c @@ -147,16 +147,18 @@ syntax_gen_number (struct string *output, & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT))) { union value v_in, v_out; - char *s; + char *s, *error; bool ok; v_in.f = number; s = data_out (&v_in, "FIXME", format); - msg_disable (); + /* FIXME: UTF8 encoded strings will fail here */ - ok = data_in (ss_cstr (s), LEGACY_NATIVE, - format->type, 0, 0, &v_out, 0, NULL); - msg_enable (); + error = data_in (ss_cstr (s), LEGACY_NATIVE, + format->type, &v_out, 0, NULL); + ok = error == NULL; + free (error); + if (ok && v_out.f == number) { syntax_gen_string (output, ss_cstr (s));