X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdata%2Fformat.c;h=87a40e2eed32a75785a9ef7302bd86bfcbe95f59;hb=refs%2Fheads%2Fvariable-sets;hp=a4c52b788ebd811541b9e3ac7d0e25beab016d81;hpb=cb08510bbbab7646bc1031427243489024d22a3b;p=pspp diff --git a/src/data/format.c b/src/data/format.c index a4c52b788e..87a40e2eed 100644 --- a/src/data/format.c +++ b/src/data/format.c @@ -101,26 +101,38 @@ fmt_settings_get_style (const struct fmt_settings *settings, #define OPPOSITE(C) ((C) == ',' ? '.' : ',') #define AFFIX(S) { .s = (char *) (S), .width = sizeof (S) - 1 } -#define NS(PREFIX, SUFFIX, DECIMAL, GROUPING) { \ +#define NS(PREFIX, SUFFIX, DECIMAL, GROUPING, INCLUDE_LEADING_ZERO) { \ .neg_prefix = AFFIX ("-"), \ .prefix = AFFIX (PREFIX), \ .suffix = AFFIX (SUFFIX), \ .neg_suffix = AFFIX (""), \ .decimal = DECIMAL, \ .grouping = GROUPING, \ + .include_leading_zero = INCLUDE_LEADING_ZERO \ } -#define ANS(DECIMAL, GROUPING) { \ - [FMT_F] = NS( "", "", DECIMAL, 0), \ - [FMT_E] = NS( "", "", DECIMAL, 0), \ - [FMT_COMMA] = NS( "", "", DECIMAL, GROUPING), \ - [FMT_DOT] = NS( "", "", GROUPING, DECIMAL), \ - [FMT_DOLLAR] = NS("$", "", DECIMAL, GROUPING), \ - [FMT_PCT] = NS( "", "%", DECIMAL, 0), \ +#define ANS(DECIMAL, GROUPING, INCLUDE_LEADING_ZERO) { \ + [FMT_F] = NS( "", "", DECIMAL, 0, INCLUDE_LEADING_ZERO), \ + [FMT_E] = NS( "", "", DECIMAL, 0, INCLUDE_LEADING_ZERO), \ + [FMT_COMMA] = NS( "", "", DECIMAL, GROUPING, INCLUDE_LEADING_ZERO), \ + [FMT_DOT] = NS( "", "", GROUPING, DECIMAL, INCLUDE_LEADING_ZERO), \ + [FMT_DOLLAR] = NS("$", "", DECIMAL, GROUPING, false), \ + [FMT_PCT] = NS( "", "%", DECIMAL, 0, false), \ } +#define ANS2(DECIMAL, GROUPING) { \ + ANS(DECIMAL, GROUPING, false), \ + ANS(DECIMAL, GROUPING, true), \ + } + + /* First index: 0 for ',' decimal point, 1 for '.' decimal point. + Second index: 0 for no leading zero, 1 for leading zero. + Third index: TYPE. + */ + static const struct fmt_number_style styles[2][2][6] = { + ANS2 (',', '.'), + ANS2 ('.', ','), + }; - static const struct fmt_number_style period_styles[6] = ANS ('.', ','); - static const struct fmt_number_style comma_styles[6] = ANS (',', '.'); - static const struct fmt_number_style default_style = NS ("", "", '.', 0); + static const struct fmt_number_style default_style = NS ("", "", '.', 0, false); switch (type) { @@ -130,9 +142,11 @@ fmt_settings_get_style (const struct fmt_settings *settings, case FMT_DOLLAR: case FMT_PCT: case FMT_E: - return (settings->decimal == '.' - ? &period_styles[type] - : &comma_styles[type]); + { + int decimal_idx = settings->decimal == '.'; + int leadzero_idx = settings->include_leading_zero; + return &styles[decimal_idx][leadzero_idx][type]; + } case FMT_CCA: case FMT_CCB: @@ -184,11 +198,8 @@ fmt_settings_set_cc (struct fmt_settings *settings, enum fmt_type type, struct fmt_spec fmt_for_input (enum fmt_type type, int w, int d) { - struct fmt_spec f; - f.type = type; - f.w = w; - f.d = d; - assert (fmt_check_input (&f)); + struct fmt_spec f = { .type = type, .w = w, .d = d }; + assert (fmt_check_input (f)); return f; } @@ -197,33 +208,30 @@ fmt_for_input (enum fmt_type type, int w, int d) struct fmt_spec fmt_for_output (enum fmt_type type, int w, int d) { - struct fmt_spec f; - f.type = type; - f.w = w; - f.d = d; - assert (fmt_check_output (&f)); + struct fmt_spec f = { .type = type, .w = w, .d = d }; + assert (fmt_check_output (f)); return f; } /* Returns the output format specifier corresponding to input format specifier INPUT. */ struct fmt_spec -fmt_for_output_from_input (const struct fmt_spec *input, +fmt_for_output_from_input (struct fmt_spec input, const struct fmt_settings *settings) { struct fmt_spec output; assert (fmt_check_input (input)); - output.type = fmt_input_to_output (input->type); - output.w = input->w; + output.type = fmt_input_to_output (input.type); + output.w = input.w; if (output.w > fmt_max_output_width (output.type)) output.w = fmt_max_output_width (output.type); else if (output.w < fmt_min_output_width (output.type)) output.w = fmt_min_output_width (output.type); - output.d = input->d; + output.d = input.d; - switch (input->type) + switch (input.type) { case FMT_Z: output.w++; @@ -238,11 +246,11 @@ fmt_for_output_from_input (const struct fmt_spec *input, case FMT_PCT: { const struct fmt_number_style *style = - fmt_settings_get_style (settings, input->type); + fmt_settings_get_style (settings, input.type); output.w += fmt_affix_width (style); - if (style->grouping != 0 && input->w - input->d >= 3) - output.w += (input->w - input->d - 1) / 3; + if (style->grouping != 0 && input.w - input.d >= 3) + output.w += (input.w - input.d - 1) / 3; if (output.d > 0) output.w++; } @@ -254,12 +262,12 @@ fmt_for_output_from_input (const struct fmt_spec *input, break; case FMT_E: - output.d = MAX (input->d, 3); - output.w = MAX (input->w, output.d + 7); + output.d = MAX (input.d, 3); + output.w = MAX (input.w, output.d + 7); break; case FMT_PIBHEX: - output.w = max_digits_for_bytes (input->w / 2) + 1; + output.w = max_digits_for_bytes (input.w / 2) + 1; break; case FMT_RB: @@ -270,12 +278,12 @@ fmt_for_output_from_input (const struct fmt_spec *input, case FMT_P: case FMT_PK: - output.w = 2 * input->w + (input->d > 0); + output.w = 2 * input.w + (input.d > 0); break; case FMT_IB: case FMT_PIB: - output.w = max_digits_for_bytes (input->w) + 1; + output.w = max_digits_for_bytes (input.w) + 1; if (output.d > 0) output.w++; break; @@ -291,7 +299,7 @@ fmt_for_output_from_input (const struct fmt_spec *input, break; case FMT_AHEX: - output.w = input->w / 2; + output.w = input.w / 2; break; case FMT_DATE: @@ -310,13 +318,13 @@ fmt_for_output_from_input (const struct fmt_spec *input, break; case FMT_MTIME: - if (input->d) - output.w = MAX (input->w, input->d + 6); + if (input.d) + output.w = MAX (input.w, input.d + 6); break; case FMT_YMDHMS: - if (input->w) - output.w = MAX (input->w, input->d + 20); + if (input.w) + output.w = MAX (input.w, input.d + 20); break; default: @@ -326,7 +334,7 @@ fmt_for_output_from_input (const struct fmt_spec *input, if (output.w > fmt_max_output_width (output.type)) output.w = fmt_max_output_width (output.type); - assert (fmt_check_output (&output)); + assert (fmt_check_output (output)); return output; } @@ -340,138 +348,242 @@ fmt_default_for_width (int width) : fmt_for_output (FMT_A, width, 0)); } -/* Checks whether SPEC is valid for USE and returns nonzero if so. - Otherwise, emits an error message and returns zero. */ -bool -fmt_check (const struct fmt_spec *spec, enum fmt_use use) +/* Checks whether SPEC is valid for USE and returns NULL if so. Otherwise, + returns a malloc()'d string that describes the error. The caller must + eventually free() the string. */ +char * +fmt_check__ (struct fmt_spec spec, enum fmt_use use) { - const char *io_fmt; char str[FMT_STRING_LEN_MAX + 1]; int min_w, max_w, max_d; - assert (is_fmt_type (spec->type)); + assert (is_fmt_type (spec.type)); fmt_to_string (spec, str); - io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format"); - if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type)) - { - msg (SE, _("Format %s may not be used for input."), str); - return false; - } + if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec.type)) + return xasprintf (_("Format %s may not be used for input."), str); - if (spec->w % fmt_step_width (spec->type)) + if (spec.w % fmt_step_width (spec.type)) { - assert (fmt_step_width (spec->type) == 2); - msg (SE, _("%s specifies width %d, but %s requires an even width."), - str, spec->w, fmt_name (spec->type)); - return false; + assert (fmt_step_width (spec.type) == 2); + return (use == FMT_FOR_INPUT + ? xasprintf (_("Input format %s specifies width %d, " + "but %s requires an even width."), + str, spec.w, fmt_name (spec.type)) + : xasprintf (_("Output format %s specifies width %d, " + "but %s requires an even width."), + str, spec.w, fmt_name (spec.type))); } - min_w = fmt_min_width (spec->type, use); - max_w = fmt_max_width (spec->type, use); - if (spec->w < min_w || spec->w > max_w) + min_w = fmt_min_width (spec.type, use); + max_w = fmt_max_width (spec.type, use); + if (spec.w < min_w || spec.w > max_w) + return (use == FMT_FOR_INPUT + ? xasprintf (_("Input format %s specifies width %d, but " + "%s requires a width between %d and %d."), + str, spec.w, fmt_name (spec.type), min_w, max_w) + : xasprintf (_("Output format %s specifies width %d, but " + "%s requires a width between %d and %d."), + str, spec.w, fmt_name (spec.type), min_w, max_w)); + + max_d = fmt_max_decimals (spec.type, spec.w, use); + if (!fmt_takes_decimals (spec.type) && spec.d != 0) + return (use == FMT_FOR_INPUT + ? xasprintf (ngettext ( + "Input format %s specifies %d decimal " + "place, but %s does not allow any decimals.", + "Input format %s specifies %d decimal " + "places, but %s does not allow any " + "decimals.", + spec.d), + str, spec.d, fmt_name (spec.type)) + : xasprintf (ngettext ( + "Output format %s specifies %d decimal " + "place, but %s does not allow any decimals.", + "Output format %s specifies %d decimal places, but " + "%s does not allow any decimals.", + spec.d), + str, spec.d, fmt_name (spec.type))); + else if (spec.d > max_d) { - msg (SE, _("%s %s specifies width %d, but " - "%s requires a width between %d and %d."), - io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w); - return false; + if (max_d > 0) + return (use == FMT_FOR_INPUT + ? xasprintf (ngettext ( + "Input format %s specifies %d decimal place, " + "but width %d allows at most %d decimals.", + "Input format %s specifies %d decimal places, " + "but width %d allows at most %d decimals.", + spec.d), + str, spec.d, spec.w, max_d) + : xasprintf (ngettext ( + "Output format %s specifies %d decimal place, " + "but width %d allows at most %d decimals.", + "Output format %s specifies %d decimal places, " + "but width %d allows at most %d decimals.", + spec.d), + str, spec.d, spec.w, max_d)); + else + return (use == FMT_FOR_INPUT + ? xasprintf (ngettext ( + "Input format %s specifies %d decimal place, " + "but width %d does not allow for any decimals.", + "Input format %s specifies %d decimal places, " + "but width %d does not allow for any decimals.", + spec.d), + str, spec.d, spec.w) + : xasprintf (ngettext ( + "Output format %s specifies %d decimal place, " + "but width %d does not allow for any decimals.", + "Output format %s specifies %d decimal places, " + "but width %d does not allow for any decimals.", + spec.d), + str, spec.d, spec.w)); } - max_d = fmt_max_decimals (spec->type, spec->w, use); - if (!fmt_takes_decimals (spec->type) && spec->d != 0) - { - msg (SE, ngettext ("%s %s specifies %d decimal place, but " - "%s does not allow any decimals.", - "%s %s specifies %d decimal places, but " - "%s does not allow any decimals.", - spec->d), - io_fmt, str, spec->d, fmt_name (spec->type)); - return false; - } - else if (spec->d > max_d) + return NULL; +} + +char * +fmt_check_input__ (struct fmt_spec spec) +{ + return fmt_check__ (spec, FMT_FOR_INPUT); +} + +char * +fmt_check_output__ (struct fmt_spec spec) +{ + return fmt_check__ (spec, FMT_FOR_OUTPUT); +} + +static bool +error_to_bool (char *error) +{ + if (error) { - if (max_d > 0) - msg (SE, ngettext ("%s %s specifies %d decimal place, but " - "the given width allows at most %d decimals.", - "%s %s specifies %d decimal places, but " - "the given width allows at most %d decimals.", - spec->d), - io_fmt, str, spec->d, max_d); - else - msg (SE, ngettext ("%s %s specifies %d decimal place, but " - "the given width does not allow for any decimals.", - "%s %s specifies %d decimal places, but " - "the given width does not allow for any decimals.", - spec->d), - io_fmt, str, spec->d); + free (error); return false; } + else + return true; +} - return true; +/* Returns true if SPEC is valid for USE, false otherwise. */ +bool +fmt_check (struct fmt_spec spec, enum fmt_use use) +{ + return error_to_bool (fmt_check__ (spec, use)); } -/* Checks whether SPEC is valid as an input format and returns - nonzero if so. Otherwise, emits an error message and returns - zero. */ +/* Returns true if SPEC is valid as an input format, otherwise false. */ bool -fmt_check_input (const struct fmt_spec *spec) +fmt_check_input (struct fmt_spec spec) { return fmt_check (spec, FMT_FOR_INPUT); } -/* Checks whether SPEC is valid as an output format and returns - true if so. Otherwise, emits an error message and returns false. */ +/* Returnst true SPEC is valid as an output format, false otherwise. */ bool -fmt_check_output (const struct fmt_spec *spec) +fmt_check_output (struct fmt_spec spec) { return fmt_check (spec, FMT_FOR_OUTPUT); } -/* Checks that FORMAT is appropriate for a variable of the given - VAR_TYPE and returns true if so. Otherwise returns false and - emits an error message. */ -bool -fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type) +/* Checks that FORMAT is appropriate for a variable of the given VAR_TYPE and + returns NULL if so. Otherwise returns a malloc()'d error message that the + caller must eventually free(). VARNAME is optional and only used in the + error message.*/ +char * +fmt_check_type_compat__ (struct fmt_spec format, const char *varname, + enum val_type var_type) { assert (val_type_is_valid (var_type)); - if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0)) + if ((var_type == VAL_STRING) != (fmt_is_string (format.type) != 0)) { char str[FMT_STRING_LEN_MAX + 1]; - msg (SE, _("%s variables are not compatible with %s format %s."), - var_type == VAL_STRING ? _("String") : _("Numeric"), - var_type == VAL_STRING ? _("numeric") : _("string"), - fmt_to_string (format, str)); - return false; + fmt_to_string (format, str); + if (var_type == VAL_STRING) + { + if (varname) + return xasprintf (_("String variable %s is not compatible with " + "numeric format %s."), varname, str); + else + return xasprintf (_("String variables are not compatible with " + "numeric format %s."), str); + } + else + { + if (varname) + return xasprintf (_("Numeric variable %s is not compatible with " + "string format %s."), varname, str); + else + return xasprintf (_("Numeric variables are not compatible with " + "string format %s."), str); + } } - return true; + return NULL; } -/* Checks that FORMAT is appropriate for a variable of the given - WIDTH and returns true if so. Otherwise returns false and - emits an error message. */ +/* Returns FORMAT is appropriate for a variable of the given VAR_TYPE and + returns true if so, otherwise false. */ bool -fmt_check_width_compat (const struct fmt_spec *format, int width) +fmt_check_type_compat (struct fmt_spec format, enum val_type var_type) { - if (!fmt_check_type_compat (format, val_type_from_width (width))) - return false; + return error_to_bool (fmt_check_type_compat__ (format, NULL, var_type)); +} + +/* Checks that FORMAT is appropriate for a variable of the given WIDTH and + returns NULL if so. Otherwise returns a malloc()'d error message that the + caller must eventually free(). VARNAME is optional and only used in the + error message. */ +char * +fmt_check_width_compat__ (struct fmt_spec format, const char *varname, + int width) +{ + char *error = fmt_check_type_compat__ (format, varname, + val_type_from_width (width)); + if (error) + return error; + if (fmt_var_width (format) != width) { - char str[FMT_STRING_LEN_MAX + 1]; - msg (SE, _("String variable with width %d is not compatible with " - "format %s."), - width, fmt_to_string (format, str)); - return false; + char format_str[FMT_STRING_LEN_MAX + 1]; + fmt_to_string (format, format_str); + + char better_str[FMT_STRING_LEN_MAX + 1]; + if (format.type == FMT_A) + snprintf (better_str, sizeof better_str, "A%d", width); + else + snprintf (better_str, sizeof better_str, "AHEX%d", width * 2); + + if (varname) + return xasprintf (_("String variable %s with width %d is not " + "compatible with format %s. " + "Use format %s instead."), + varname, width, format_str, better_str); + else + return xasprintf (_("String variable with width %d is not compatible " + "with format %s. Use format %s instead."), + width, format_str, better_str); } - return true; + + return NULL; +} + +/* Checks that FORMAT is appropriate for a variable of the given WIDTH and + returns true if so, otherwise false. */ +bool +fmt_check_width_compat (struct fmt_spec format, int width) +{ + return error_to_bool (fmt_check_width_compat__ (format, NULL, width)); } /* Returns the width corresponding to FORMAT. The return value is the width of the `union value's required by FORMAT. */ int -fmt_var_width (const struct fmt_spec *format) +fmt_var_width (struct fmt_spec format) { - return (format->type == FMT_AHEX ? format->w / 2 - : format->type == FMT_A ? format->w + return (format.type == FMT_AHEX ? format.w / 2 + : format.type == FMT_A ? format.w : 0); } @@ -482,23 +594,23 @@ fmt_var_width (const struct fmt_spec *format) even if F's format type does not allow decimals, to allow accurately presenting incorrect formats to the user. */ char * -fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1]) +fmt_to_string (struct fmt_spec f, char buffer[FMT_STRING_LEN_MAX + 1]) { - if (fmt_takes_decimals (f->type) || f->d > 0) + if (fmt_takes_decimals (f.type) || f.d > 0) snprintf (buffer, FMT_STRING_LEN_MAX + 1, - "%s%d.%d", fmt_name (f->type), f->w, f->d); + "%s%d.%d", fmt_name (f.type), f.w, f.d); else snprintf (buffer, FMT_STRING_LEN_MAX + 1, - "%s%d", fmt_name (f->type), f->w); + "%s%d", fmt_name (f.type), f.w); return buffer; } /* Returns true if A and B are identical formats, false otherwise. */ bool -fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b) +fmt_equal (struct fmt_spec a, struct fmt_spec b) { - return a->type == b->type && a->w == b->w && a->d == b->d; + return a.type == b.type && a.w == b.w && a.d == b.d; } /* Adjusts FMT to be valid for a value of the given WIDTH if necessary. @@ -927,22 +1039,18 @@ fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f) uint8_t w = u32 >> 8; uint8_t d = u32; - msg_disable (); - f->w = w; - f->d = d; - bool ok = fmt_from_io (raw_type, &f->type); - if (ok) - { - if (loose) - fmt_fix_output (f); - else - ok = fmt_check_output (f); - } - if (ok) - ok = fmt_check_width_compat (f, width); - msg_enable (); + enum fmt_type type; + if (!fmt_from_io (raw_type, &type)) + return false; + + *f = (struct fmt_spec) { .type = type, .w = w, .d = d }; - return ok; + if (loose) + fmt_fix_output (f); + else if (!fmt_check_output (*f)) + return false; + + return fmt_check_width_compat (*f, width); } /* Returns true if TYPE may be used as an input format, @@ -1144,12 +1252,8 @@ fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use) static void fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use) { - int max_d; - - max_d = fmt_max_decimals (fmt->type, fmt->w, use); - if (fmt->d < 0) - fmt->d = 0; - else if (fmt->d > max_d) + int max_d = fmt_max_decimals (fmt->type, fmt->w, use); + if (fmt->d > max_d) fmt->d = max_d; } @@ -1166,7 +1270,7 @@ fmt_affix_clone (const struct fmt_affix *old) static void fmt_affix_free (struct fmt_affix *affix) { - if (affix->s[0]) + if (affix->s) free (affix->s); } @@ -1235,6 +1339,7 @@ fmt_number_style_from_string (const char *s) .neg_suffix = neg_suffix, .decimal = grouping == '.' ? ',' : '.', .grouping = grouping, + .include_leading_zero = false, .extra_bytes = extra_bytes, }; return style; @@ -1334,7 +1439,7 @@ get_fmt_desc (enum fmt_type type) return &formats[type]; } -const struct fmt_spec F_8_0 = {FMT_F, 8, 0}; -const struct fmt_spec F_8_2 = {FMT_F, 8, 2}; -const struct fmt_spec F_4_3 = {FMT_F, 4, 3}; -const struct fmt_spec F_5_1 = {FMT_F, 5, 1}; +const struct fmt_spec F_8_0 = { .type = FMT_F, .w = 8, .d = 0 }; +const struct fmt_spec F_8_2 = { .type = FMT_F, .w = 8, .d = 2 }; +const struct fmt_spec F_4_3 = { .type = FMT_F, .w = 4, .d = 3 }; +const struct fmt_spec F_5_1 = { .type = FMT_F, .w = 5, .d = 1 };