X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fdata-out.c;h=cda2b75fb0b01a48e0ac14d1e4300d2304922716;hb=c1b1583b96cc05a2bf9f3f6d01bbfa063fafb253;hp=10ca4ede77f9e4fbe742927ad80a9fce4471d444;hpb=83868d9b4cfc2259dad94c8de72f03f918995cfd;p=pspp diff --git a/src/data/data-out.c b/src/data/data-out.c index 10ca4ede77..cda2b75fb0 100644 --- a/src/data/data-out.c +++ b/src/data/data-out.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000, 2006, 2009, 2011, 2012 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006, 2009, 2011, 2012, 2013, 2014 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 @@ -41,6 +41,7 @@ #include "libpspp/str.h" #include "gl/minmax.h" +#include "gl/c-snprintf.h" #include "gettext.h" #define _(msgid) gettext (msgid) @@ -57,7 +58,8 @@ struct rounder bool negative; /* Is the number negative? */ }; -static void rounder_init (struct rounder *, double number, int max_decimals); +static void rounder_init (struct rounder *, const struct fmt_number_style *, + double number, int max_decimals); static int rounder_width (const struct rounder *, int decimals, int *integer_digits, bool *negative); static void rounder_format (const struct rounder *, int decimals, @@ -65,14 +67,16 @@ static void rounder_format (const struct rounder *, int decimals, typedef void data_out_converter_func (const union value *, const struct fmt_spec *, - char *); + const struct fmt_settings *, char *); #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \ static data_out_converter_func output_##METHOD; #include "format.def" static bool output_decimal (const struct rounder *, const struct fmt_spec *, + const struct fmt_number_style *, bool require_affixes, char *); static bool output_scientific (double, const struct fmt_spec *, + const struct fmt_number_style *, bool require_affixes, char *); static double power10 (int) PURE_FUNCTION; @@ -107,23 +111,24 @@ static data_out_converter_func *const converters[FMT_NUMBER_OF_FORMATS] = void data_out_recode (const union value *input, const char *input_encoding, const struct fmt_spec *format, + const struct fmt_settings *settings, struct string *output, const char *output_encoding) { assert (fmt_check_output (format)); if (format->type == FMT_A) { - char *in = CHAR_CAST (char *, value_str (input, format->w)); + char *in = CHAR_CAST (char *, input->s); char *out = recode_string (output_encoding, input_encoding, in, format->w); ds_put_cstr (output, out); free (out); } else if (fmt_get_category (format->type) == FMT_CAT_BINARY) - converters[format->type] (input, format, + converters[format->type] (input, format, settings, ds_put_uninit (output, format->w)); else { - char *utf8_encoded = data_out (input, input_encoding, format); + char *utf8_encoded = data_out (input, input_encoding, format, settings); char *output_encoded = recode_string (output_encoding, UTF8, utf8_encoded, -1); ds_put_cstr (output, output_encoded); @@ -156,46 +161,81 @@ binary_to_utf8 (const char *in, struct pool *pool) VALUE must be the correct width for FORMAT, that is, its width must be fmt_var_width(FORMAT). - ENCODING must be the encoding of INPUT. Normally this can be obtained by - calling dict_get_encoding() on the dictionary with which INPUT is - associated. ENCODING is only important when FORMAT's type is FMT_A. + INPUT_ENCODING must be the encoding of INPUT. Normally this can be obtained + by calling dict_get_encoding() on the dictionary with which INPUT is + associated. INPUT_ENCODING is only important when FORMAT's type is FMT_A. The return value is dynamically allocated, and must be freed by the caller. If POOL is non-null, then the return value is allocated on that pool. */ char * -data_out_pool (const union value *input, const char *encoding, - const struct fmt_spec *format, struct pool *pool) +data_out_pool (const union value *input, const char *input_encoding, + const struct fmt_spec *format, + const struct fmt_settings *settings, struct pool *pool) { assert (fmt_check_output (format)); if (format->type == FMT_A) { - char *in = CHAR_CAST (char *, value_str (input, format->w)); - return recode_string_pool (UTF8, encoding, in, format->w, pool); + char *in = CHAR_CAST (char *, input->s); + return recode_string_pool (UTF8, input_encoding, in, format->w, pool); } else if (fmt_get_category (format->type) == FMT_CAT_BINARY) { char tmp[16]; assert (format->w + 1 <= sizeof tmp); - converters[format->type] (input, format, tmp); + converters[format->type] (input, format, settings, tmp); return binary_to_utf8 (tmp, pool); } else { - const struct fmt_number_style *style = settings_get_style (format->type); + const struct fmt_number_style *style = fmt_settings_get_style ( + settings, format->type); size_t size = format->w + style->extra_bytes + 1; char *output; output = pool_alloc_unaligned (pool, size); - converters[format->type] (input, format, output); + converters[format->type] (input, format, settings, output); return output; } } +/* Like data_out_pool(), except that for basic numeric formats (F, COMMA, DOT, + COLLAR, PCT, E) and custom currency formats are formatted as wide as + necessary to fully display the selected number of decimal places. */ char * -data_out (const union value *input, const char *encoding, const struct fmt_spec *format) +data_out_stretchy (const union value *input, const char *encoding, + const struct fmt_spec *format, + const struct fmt_settings *settings, struct pool *pool) { - return data_out_pool (input, encoding, format, NULL); + + if (fmt_get_category (format->type) & (FMT_CAT_BASIC | FMT_CAT_CUSTOM)) + { + const struct fmt_number_style *style + = fmt_settings_get_style (settings, format->type); + struct fmt_spec wide_format; + char tmp[128]; + size_t size; + + wide_format.type = format->type; + wide_format.w = 40; + wide_format.d = format->d; + + size = format->w + style->extra_bytes + 1; + if (size <= sizeof tmp) + { + output_number (input, &wide_format, settings, tmp); + return pool_strdup (pool, tmp + strspn (tmp, " ")); + } + } + + return data_out_pool (input, encoding, format, settings, pool); +} + +char * +data_out (const union value *input, const char *input_encoding, + const struct fmt_spec *format, const struct fmt_settings *settings) +{ + return data_out_pool (input, input_encoding, format, settings, NULL); } @@ -205,7 +245,7 @@ data_out (const union value *input, const char *encoding, const struct fmt_spec CCE formats. */ static void output_number (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings, char *output) { double number = input->f; @@ -215,18 +255,21 @@ output_number (const union value *input, const struct fmt_spec *format, output_infinite (number, format, output); else { + const struct fmt_number_style *style = + fmt_settings_get_style (settings, format->type); + if (format->type != FMT_E && fabs (number) < 1.5 * power10 (format->w)) { struct rounder r; - rounder_init (&r, number, format->d); + rounder_init (&r, style, number, format->d); - if (output_decimal (&r, format, true, output) - || output_scientific (number, format, true, output) - || output_decimal (&r, format, false, output)) + if (output_decimal (&r, format, style, true, output) + || output_scientific (number, format, style, true, output) + || output_decimal (&r, format, style, false, output)) return; } - if (!output_scientific (number, format, false, output)) + if (!output_scientific (number, format, style, false, output)) output_overflow (format, output); } } @@ -234,7 +277,7 @@ output_number (const union value *input, const struct fmt_spec *format, /* Outputs N format. */ static void output_N (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double number = input->f * power10 (format->d); if (input->f == SYSMIS || number < 0) @@ -244,7 +287,7 @@ output_N (const union value *input, const struct fmt_spec *format, char buf[128]; number = fabs (round (number)); if (number < power10 (format->w) - && sprintf (buf, "%0*.0f", format->w, number) == format->w) + && c_snprintf (buf, 128, "%0*.0f", format->w, number) == format->w) memcpy (output, buf, format->w); else output_overflow (format, output); @@ -256,14 +299,14 @@ output_N (const union value *input, const struct fmt_spec *format, /* Outputs Z format. */ static void output_Z (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double number = input->f * power10 (format->d); char buf[128]; if (input->f == SYSMIS) output_missing (format, output); else if (fabs (number) < power10 (format->w) - && sprintf (buf, "%0*.0f", format->w, + && c_snprintf (buf, 128, "%0*.0f", format->w, fabs (round (number))) == format->w) { if (number < 0 && strspn (buf, "0") < format->w) @@ -281,7 +324,7 @@ output_Z (const union value *input, const struct fmt_spec *format, /* Outputs P format. */ static void output_P (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { if (output_bcd_integer (fabs (input->f * power10 (format->d)), format->w * 2 - 1, output) @@ -294,7 +337,7 @@ output_P (const union value *input, const struct fmt_spec *format, /* Outputs PK format. */ static void output_PK (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { output_bcd_integer (input->f * power10 (format->d), format->w * 2, output); } @@ -302,7 +345,7 @@ output_PK (const union value *input, const struct fmt_spec *format, /* Outputs IB format. */ static void output_IB (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double number = round (input->f * power10 (format->d)); if (input->f == SYSMIS @@ -325,7 +368,7 @@ output_IB (const union value *input, const struct fmt_spec *format, /* Outputs PIB format. */ static void output_PIB (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double number = round (input->f * power10 (format->d)); if (input->f == SYSMIS @@ -341,7 +384,7 @@ output_PIB (const union value *input, const struct fmt_spec *format, /* Outputs PIBHEX format. */ static void output_PIBHEX (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double number = round (input->f); if (input->f == SYSMIS) @@ -360,7 +403,7 @@ output_PIBHEX (const union value *input, const struct fmt_spec *format, /* Outputs RB format. */ static void output_RB (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double d = input->f; memcpy (output, &d, format->w); @@ -371,7 +414,7 @@ output_RB (const union value *input, const struct fmt_spec *format, /* Outputs RBHEX format. */ static void output_RBHEX (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { double d = input->f; @@ -382,7 +425,7 @@ output_RBHEX (const union value *input, const struct fmt_spec *format, DATETIME, TIME, and DTIME formats. */ static void output_date (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings, char *output) { double number = input->f; int year, month, day, yday; @@ -442,14 +485,15 @@ output_date (const union value *input, const struct fmt_spec *format, { if (year <= 9999) p += sprintf (p, "%04d", year); - else if (format->type == FMT_DATETIME) + else if (format->type == FMT_DATETIME + || format->type == FMT_YMDHMS) p = stpcpy (p, "****"); else goto overflow; } else { - int epoch = settings_get_epoch (); + int epoch = fmt_settings_get_epoch (settings); int offset = year - epoch; if (offset < 0 || offset > 99) goto overflow; @@ -466,21 +510,25 @@ output_date (const union value *input, const struct fmt_spec *format, if (number < 0) *p++ = '-'; number = fabs (number); - p += sprintf (p, "%*.0f", count, floor (number / 60. / 60. / 24.)); + p += c_snprintf (p, 64, "%*.0f", count, floor (number / 60. / 60. / 24.)); number = fmod (number, 60. * 60. * 24.); break; case 'H': if (number < 0) *p++ = '-'; number = fabs (number); - p += sprintf (p, "%0*.0f", count, floor (number / 60. / 60.)); + p += c_snprintf (p, 64, "%0*.0f", count, floor (number / 60. / 60.)); number = fmod (number, 60. * 60.); break; case 'M': + if (number < 0) + *p++ = '-'; + number = fabs (number); p += sprintf (p, "%02d", (int) floor (number / 60.)); number = fmod (number, 60.); excess_width = format->w - (p - tmp); - if (excess_width < 0) + if (excess_width < 0 + || (format->type == FMT_MTIME && excess_width < 3)) goto overflow; if (excess_width == 3 || excess_width == 4 || (excess_width >= 5 && format->d == 0)) @@ -489,12 +537,12 @@ output_date (const union value *input, const struct fmt_spec *format, { int d = MIN (format->d, excess_width - 4); int w = d + 3; - sprintf (p, ":%0*.*f", w, d, number); - if (settings_get_decimal_char (FMT_F) != '.') + c_snprintf (p, 64, ":%0*.*f", w, d, number); + if (settings->decimal != '.') { char *cp = strchr (p, '.'); if (cp != NULL) - *cp = settings_get_decimal_char (FMT_F); + *cp = settings->decimal; } p += strlen (p); } @@ -523,7 +571,7 @@ output_date (const union value *input, const struct fmt_spec *format, /* Outputs WKDAY format. */ static void output_WKDAY (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { static const char *const weekdays[7] = { @@ -549,7 +597,7 @@ output_WKDAY (const union value *input, const struct fmt_spec *format, /* Outputs MONTH format. */ static void output_MONTH (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { static const char *const months[12] = { @@ -574,7 +622,8 @@ output_MONTH (const union value *input, const struct fmt_spec *format, /* Outputs A format. */ static void output_A (const union value *input UNUSED, - const struct fmt_spec *format UNUSED, char *output UNUSED) + const struct fmt_spec *format UNUSED, + const struct fmt_settings *settings UNUSED, char *output UNUSED) { NOT_REACHED (); } @@ -582,9 +631,9 @@ output_A (const union value *input UNUSED, /* Outputs AHEX format. */ static void output_AHEX (const union value *input, const struct fmt_spec *format, - char *output) + const struct fmt_settings *settings UNUSED, char *output) { - output_hex (value_str (input, format->w), format->w / 2, output); + output_hex (input->s, format->w / 2, output); } /* Decimal and scientific formatting. */ @@ -606,18 +655,16 @@ allocate_space (int request, int max_width, int *width) } /* Tries to compose the number represented by R, in the style of - FORMAT, into OUTPUT. Returns true if successful, false on - failure, which occurs if FORMAT's width is too narrow. If + FORMAT and STYLE, into OUTPUT. Returns true if successful, false on + failure, which cocurs if FORMAT's width is too narrow. If REQUIRE_AFFIXES is true, then the prefix and suffix specified by FORMAT's style must be included; otherwise, they may be omitted to make the number fit. */ static bool output_decimal (const struct rounder *r, const struct fmt_spec *format, - bool require_affixes, char *output) + const struct fmt_number_style *style, bool require_affixes, + char *output) { - const struct fmt_number_style *style = - settings_get_style (format->type); - int decimals; for (decimals = format->d; decimals >= 0; decimals--) @@ -714,14 +761,13 @@ output_decimal (const struct rounder *r, const struct fmt_spec *format, return false; } -/* Formats NUMBER into OUTPUT in scientific notation according to - the style of the format specified in FORMAT. */ +/* Formats NUMBER into OUTPUT in scientific notation according to FORMAT and + STYLE. */ static bool output_scientific (double number, const struct fmt_spec *format, + const struct fmt_number_style *style, bool require_affixes, char *output) { - const struct fmt_number_style *style = - settings_get_style (format->type); int width; int fraction_width; bool add_affixes; @@ -742,7 +788,7 @@ output_scientific (double number, const struct fmt_spec *format, /* Figure out number of characters we can use for the fraction, if any. (If that turns out to be 1, then we'll output a decimal point without any digits following; that's what the - # flag does in the call to sprintf, below.) */ + # flag does in the call to c_snprintf, below.) */ fraction_width = MIN (MIN (format->d + 1, format->w - width), 16); if (format->type != FMT_E && fraction_width == 1) fraction_width = 0; @@ -757,9 +803,9 @@ output_scientific (double number, const struct fmt_spec *format, if (add_affixes) p = stpcpy (p, style->prefix.s); if (fraction_width > 0) - sprintf (p, "%#.*E", fraction_width - 1, fabs (number)); + c_snprintf (p, 64, "%#.*E", fraction_width - 1, fabs (number)); else - sprintf (p, "%.0E", fabs (number)); + c_snprintf (p, 64, "%.0E", fabs (number)); /* The C locale always uses a period `.' as a decimal point. Translate to comma if necessary. */ @@ -774,7 +820,7 @@ output_scientific (double number, const struct fmt_spec *format, { char *cp = strchr (p, 'E') + 1; long int exponent = strtol (cp, NULL, 10); - if (abs (exponent) > 999) + if (labs (exponent) > 999) return false; sprintf (cp, "%+04ld", exponent); } @@ -806,10 +852,11 @@ should_round_up (const struct rounder *r, int decimals) return digit >= '5'; } -/* Initializes R for formatting the magnitude of NUMBER to no +/* Initializes R for formatting the magnitude of NUMBER with STYLE to no more than MAX_DECIMAL decimal places. */ static void -rounder_init (struct rounder *r, double number, int max_decimals) +rounder_init (struct rounder *r, const struct fmt_number_style *style, + double number, int max_decimals) { assert (fabs (number) < 1e41); assert (max_decimals >= 0 && max_decimals <= 16); @@ -819,7 +866,7 @@ rounder_init (struct rounder *r, double number, int max_decimals) We append ".00" to the integer representation because round_up assumes that fractional digits are present. */ - sprintf (r->string, "%.0f.00", fabs (round (number))); + c_snprintf (r->string, 64, "%.0f.00", fabs (round (number))); } else { @@ -846,7 +893,7 @@ rounder_init (struct rounder *r, double number, int max_decimals) numbers does not hint how to do what we want, and it's not obvious how to change their algorithms to do so. It would also be a lot of work. */ - sprintf (r->string, "%.*f", max_decimals + 2, fabs (number)); + c_snprintf (r->string, 64, "%.*f", max_decimals + 2, fabs (number)); if (!strcmp (r->string + strlen (r->string) - 2, "50")) { int binary_exponent, decimal_exponent, format_decimals; @@ -854,16 +901,17 @@ rounder_init (struct rounder *r, double number, int max_decimals) decimal_exponent = binary_exponent * 3 / 10; format_decimals = (DBL_DIG + 1) - decimal_exponent; if (format_decimals > max_decimals + 2) - sprintf (r->string, "%.*f", format_decimals, fabs (number)); + c_snprintf (r->string, 64, "%.*f", format_decimals, fabs (number)); } } - if (r->string[0] == '0') + if (r->string[0] == '0' && !style->include_leading_zero) memmove (r->string, &r->string[1], strlen (r->string)); r->leading_zeros = strspn (r->string, "0."); r->leading_nines = strspn (r->string, "9."); r->integer_digits = strchr (r->string, '.') - r->string; + assert (r->integer_digits < 64); assert (r->integer_digits >= 0); r->negative = number < 0; } @@ -1089,7 +1137,7 @@ output_bcd_integer (double number, int digits, char *output) if (number != SYSMIS && number >= 0. && number < power10 (digits) - && sprintf (decimal, "%0*.0f", digits, round (number)) == digits) + && c_snprintf (decimal, 64, "%0*.0f", digits, round (number)) == digits) { const char *src = decimal; int i;