X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fdata-in.c;h=b8226b37f926601dd1ed04f0ce368a242a5346ff;hb=00129e248ab4ce96af72d380d8a0ccfad2f5d776;hp=4666a173e7ce091d5fc185f99fdfc99dd2b9cea4;hpb=2764b3157e26955a31af5f4aa7d14e27098ddf19;p=pspp diff --git a/src/data/data-in.c b/src/data/data-in.c index 4666a173e7..b8226b37f9 100644 --- a/src/data/data-in.c +++ b/src/data/data-in.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 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 @@ -20,33 +20,34 @@ #include #include +#include #include #include +#include #include #include #include #include -#include -#include #include "calendar.h" +#include "dictionary.h" +#include "format.h" #include "identifier.h" +#include "libpspp/assertion.h" +#include "libpspp/compiler.h" +#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" #include "value.h" -#include "format.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "c-ctype.h" -#include "c-strtod.h" -#include "minmax.h" -#include "xalloc.h" +#include "gl/c-ctype.h" +#include "gl/c-strtod.h" +#include "gl/minmax.h" +#include "gl/xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -54,10 +55,8 @@ /* Information about parsing one data field. */ struct data_in { - const char *encoding; /* Encoding of source. */ struct substring input; /* Source. */ enum fmt_type format; /* Input format. */ - int implied_decimals; /* Number of implied decimal places. */ union value *output; /* Destination. */ int width; /* Output width. */ @@ -66,19 +65,14 @@ struct data_in int last_column; /* Last column. */ }; - - typedef bool 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 vdata_warning (const struct data_in *, const char *, va_list) - PRINTF_FORMAT (2, 0); static void data_warning (const struct data_in *, const char *, ...) PRINTF_FORMAT (2, 3); -static void apply_implied_decimals (struct data_in *); static void default_result (struct data_in *); static bool trim_spaces_and_check_missing (struct data_in *); @@ -89,21 +83,13 @@ static int hexit_value (int c); representation in OUTPUT, which the caller must have initialized with the given WIDTH (0 for a numeric field, otherwise the string width). - - If no decimal point is included in a numeric format, then - IMPLIED_DECIMALS decimal places are implied. Specify 0 if no - decimal places should be implied. - - If FIRST_COLUMN and LAST_COLUMN are nonzero, then they should - be the 1-based column number of the first and - one-past-the-last-character in INPUT, for use in error - messages. (LAST_COLUMN cannot always be calculated from - FIRST_COLUMN plus the length of the input because of the - possibility of escaped quotes in strings, etc.) */ + Iff FORMAT is a string format, then DICT must be a pointer + to the dictionary associated with OUTPUT. Otherwise, DICT + may be null. */ bool data_in (struct substring input, const char *encoding, - enum fmt_type format, int implied_decimals, - int first_column, int last_column, union value *output, int width) + enum fmt_type format, int first_column, int last_column, + const struct dictionary *dict, union value *output, int width) { static data_in_parser_func *const handlers[FMT_NUMBER_OF_FORMATS] = { @@ -112,30 +98,15 @@ data_in (struct substring input, const char *encoding, }; struct data_in i; - void *copy = NULL; + + enum fmt_category cat; + const char *dest_encoding; + char *s; bool ok; assert ((width != 0) == fmt_is_string (format)); - if (0 == strcmp (encoding, LEGACY_NATIVE) - || fmt_get_category (format) & (FMT_CAT_BINARY | FMT_CAT_STRING)) - { - i.input = input; - i.encoding = encoding; - } - else - { - char *s; - ss_alloc_uninit (&i.input, ss_length (input)); - - s = recode_string (LEGACY_NATIVE, encoding, ss_data (input), ss_length (input)); - memcpy (ss_data (i.input), s, ss_length (input)); - free (s); - i.encoding = LEGACY_NATIVE; - copy = ss_data (i.input); - } i.format = format; - i.implied_decimals = implied_decimals; i.output = output; i.width = width; @@ -143,24 +114,154 @@ data_in (struct substring input, const char *encoding, i.first_column = first_column; i.last_column = last_column; - if (!ss_is_empty (i.input)) + if (ss_is_empty (input)) { - ok = handlers[i.format] (&i); - if (!ok) - default_result (&i); + default_result (&i); + return true; + } + + cat = fmt_get_category (format); + if (cat & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL + | FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)) + { + /* We're going to parse these into numbers. For this purpose we want to + deal with them in the local "C" encoding. Any character not in that + encoding wouldn't be valid anyhow. */ + dest_encoding = LEGACY_NATIVE; + } + else if (cat & (FMT_CAT_BINARY | FMT_CAT_LEGACY)) + { + /* Don't recode these binary formats at all, since they are not text. */ + dest_encoding = NULL; } else { - default_result (&i); - ok = true; + assert (cat == FMT_CAT_STRING); + if (format == FMT_AHEX) + { + /* We want the hex digits in the local "C" encoding, even though the + result may not be in that encoding. */ + dest_encoding = LEGACY_NATIVE; + } + else + { + /* Use the final output encoding. */ + dest_encoding = dict_get_encoding (dict); + } } - if (copy) - free (copy); + if (dest_encoding != NULL) + { + i.input = recode_substring_pool (dest_encoding, encoding, input, NULL); + s = i.input.string; + } + else + { + i.input = input; + s = NULL; + } + + ok = handlers[i.format] (&i); + if (!ok) + default_result (&i); + + free (s); return ok; } +static bool +number_has_implied_decimals (const char *s, enum fmt_type type) +{ + int decimal = settings_get_style (type)->decimal; + bool got_digit = false; + for (;;) + { + switch (*s) + { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + got_digit = true; + break; + + case '+': case '-': + if (got_digit) + return false; + break; + + case 'e': case 'E': case 'd': case 'D': + return false; + + case '.': case ',': + if (*s == decimal) + return false; + break; + + case '\0': + return true; + + default: + break; + } + + s++; + } +} + +static bool +has_implied_decimals (struct substring input, const char *encoding, + enum fmt_type format) +{ + bool retval; + char *s; + + switch (format) + { + case FMT_F: + case FMT_COMMA: + case FMT_DOT: + case FMT_DOLLAR: + case FMT_PCT: + case FMT_E: + case FMT_Z: + break; + + case FMT_N: + case FMT_IB: + case FMT_PIB: + case FMT_P: + case FMT_PK: + return true; + + default: + return false; + } + + s = recode_string (LEGACY_NATIVE, encoding, + ss_data (input), ss_length (input)); + retval = (format == FMT_Z + ? strchr (s, '.') == NULL + : number_has_implied_decimals (s, format)); + free (s); + + return retval; +} + +/* In some cases, when no decimal point is explicitly included in numeric + input, its position is implied by the number of decimal places in the input + format. In such a case, this function may be called just after data_in(). + Its arguments are a subset of that function's arguments plus D, the number + of decimal places associated with FORMAT. + + If it is appropriate, this function modifies the numeric value in OUTPUT. */ +void +data_in_imply_decimals (struct substring input, const char *encoding, + enum fmt_type format, int d, union value *output) +{ + if (d > 0 && output->f != SYSMIS + && has_implied_decimals (input, encoding, format)) + output->f /= pow (10., d); +} /* Format parsers. */ @@ -298,8 +399,6 @@ parse_number (struct data_in *i) else { errno = save_errno; - if (!explicit_decimals) - apply_implied_decimals (i); } ds_destroy (&tmp); @@ -323,7 +422,6 @@ parse_N (struct data_in *i) i->output->f = i->output->f * 10.0 + (c - '0'); } - apply_implied_decimals (i); return true; } @@ -479,11 +577,7 @@ parse_Z (struct data_in *i) } } else - { - errno = save_errno; - if (!got_dot) - apply_implied_decimals (i); - } + errno = save_errno; ds_destroy (&tmp); return true; @@ -510,8 +604,6 @@ parse_IB (struct data_in *i) i->output->f = -(double) -value; } - apply_implied_decimals (i); - return true; } @@ -522,8 +614,6 @@ 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))); - apply_implied_decimals (i); - return true; } @@ -563,8 +653,6 @@ parse_P (struct data_in *i) else if (low_nibble == 0xb || low_nibble == 0xd) i->output->f = -i->output->f; - apply_implied_decimals (i); - return true; } @@ -586,8 +674,6 @@ parse_PK (struct data_in *i) i->output->f = (100 * i->output->f) + (10 * high_nibble) + low_nibble; } - apply_implied_decimals (i); - return true; } @@ -612,14 +698,13 @@ parse_A (struct data_in *i) { /* This is equivalent to buf_copy_rpad, except that we posibly do a character set recoding in the middle. */ - char *dst = value_str_rw (i->output, i->width); + uint8_t *dst = value_str_rw (i->output, i->width); size_t dst_size = i->width; const char *src = ss_data (i->input); size_t src_size = ss_length (i->input); - char *s = recode_string (LEGACY_NATIVE, i->encoding, src, MIN (src_size, dst_size)); - memcpy (dst, s, dst_size); - free (s); + memcpy (dst, src, MIN (src_size, dst_size)); + if (dst_size > src_size) memset (&dst[src_size], ' ', dst_size - src_size); @@ -630,7 +715,7 @@ parse_A (struct data_in *i) static bool parse_AHEX (struct data_in *i) { - char *s = value_str_rw (i->output, i->width); + uint8_t *s = value_str_rw (i->output, i->width); size_t j; for (j = 0; ; j++) @@ -645,11 +730,6 @@ parse_AHEX (struct data_in *i) return false; } - if (0 != strcmp (i->encoding, LEGACY_NATIVE)) - { - hi = legacy_to_native (i->encoding, hi); - lo = legacy_to_native (i->encoding, lo); - } if (!c_isxdigit (hi) || !c_isxdigit (lo)) { data_warning (i, _("Field must contain only hex digits.")); @@ -867,7 +947,7 @@ parse_trailer (struct data_in *i) if (ss_is_empty (i->input)) return true; - data_warning (i, _("Trailing garbage \"%.*s\" following date."), + data_warning (i, _("Trailing garbage `%.*s' following date."), (int) ss_length (i->input), ss_data (i->input)); return false; } @@ -1013,19 +1093,6 @@ parse_weekday (struct data_in *i, long *weekday) /* Date & time formats. */ -/* Helper function for passing to - calendar_gregorian_to_offset. */ -static void -calendar_error (void *i_, const char *format, ...) -{ - struct data_in *i = i_; - va_list args; - - va_start (args, format); - vdata_warning (i, format, args); - va_end (args); -} - /* Parses WKDAY format. */ static bool parse_WKDAY (struct data_in *i) @@ -1156,10 +1223,16 @@ parse_date (struct data_in *i) if (year != INT_MIN) { - double ofs = calendar_gregorian_to_offset (year, month, day, - calendar_error, i); + char *error; + double ofs; + + ofs = calendar_gregorian_to_offset (year, month, day, &error); if (ofs == SYSMIS) - return false; + { + data_warning (i, "%s", error); + free (error); + return false; + } date = (yday - 1 + ofs) * 60. * 60. * 24.; } else @@ -1168,58 +1241,37 @@ parse_date (struct data_in *i) return true; } + /* Utility functions. */ -/* Outputs FORMAT with the given ARGS as a warning for input - I. */ +/* Outputs FORMAT with as a warning for input I. */ static void -vdata_warning (const struct data_in *i, const char *format, va_list args) +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, '('); - if (i->first_column != 0) - { - if (i->first_column == i->last_column - 1) - ds_put_format (&text, _("column %d"), i->first_column); - else - ds_put_format (&text, _("columns %d-%d"), - i->first_column, i->last_column - 1); - ds_put_cstr (&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_DATA; - m.severity = MSG_WARNING; + 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); } -/* Outputs FORMAT with the given ARGS as a warning for input - I. */ -static void -data_warning (const struct data_in *i, const char *format, ...) -{ - va_list args; - - va_start (args, format); - vdata_warning (i, format, args); - va_end (args); -} - -/* Apply implied decimal places to output. */ -static void -apply_implied_decimals (struct data_in *i) -{ - if (i->implied_decimals > 0) - i->output->f /= pow (10., i->implied_decimals); -} - /* 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