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=f323424d0868fbf128629bbf845e7bb94726a813;hpb=f20d313de2b85419c3e2e22a78cdcdce499af43d;p=pspp diff --git a/src/data/format.c b/src/data/format.c index f323424d08..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: @@ -185,7 +199,7 @@ struct fmt_spec fmt_for_input (enum fmt_type type, int w, int d) { struct fmt_spec f = { .type = type, .w = w, .d = d }; - assert (fmt_check_input (&f)); + assert (fmt_check_input (f)); return f; } @@ -195,29 +209,29 @@ struct fmt_spec fmt_for_output (enum fmt_type type, int w, int d) { struct fmt_spec f = { .type = type, .w = w, .d = d }; - assert (fmt_check_output (&f)); + 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++; @@ -232,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++; } @@ -248,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: @@ -264,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; @@ -285,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: @@ -304,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: @@ -320,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; } @@ -334,183 +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) { 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); - 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 (spec->w % fmt_step_width (spec->type)) - { - assert (fmt_step_width (spec->type) == 2); - if (use == FMT_FOR_INPUT) - msg (SE, _("Input format %s specifies width %d, " - "but %s requires an even width."), - str, spec->w, fmt_name (spec->type)); - else - msg (SE, _("Output format %s specifies width %d, " - "but %s requires an even width."), - str, spec->w, fmt_name (spec->type)); - return false; - } + if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec.type)) + return xasprintf (_("Format %s may not be used for input."), str); - 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) + if (spec.w % fmt_step_width (spec.type)) { - if (use == FMT_FOR_INPUT) - msg (SE, _("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); - else - msg (SE, _("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); - 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))); } - max_d = fmt_max_decimals (spec->type, spec->w, use); - if (!fmt_takes_decimals (spec->type) && spec->d != 0) - { - if (use == FMT_FOR_INPUT) - msg (SE, 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)); - else - msg (SE, ngettext ("Output format %s specifies %d decimal place, but " - "%s does not allow any decimals.", + 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)); - return false; - } - else if (spec->d > max_d) + spec.d), + str, spec.d, fmt_name (spec.type))); + else if (spec.d > max_d) { if (max_d > 0) - { - if (use == FMT_FOR_INPUT) - msg (SE, ngettext ("Input format %s specifies %d decimal place, " - "but the given width allows at most " - "%d decimals.", + 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 the given width allows at most " - "%d decimals.", - spec->d), - str, spec->d, max_d); - else - msg (SE, ngettext ("Output format %s specifies %d decimal place, " - "but the given width allows at most " - "%d decimals.", + "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 the given width allows at most " - "%d decimals.", - spec->d), - str, spec->d, max_d); - } + "but width %d allows at most %d decimals.", + spec.d), + str, spec.d, spec.w, max_d)); else - { - if (use == FMT_FOR_INPUT) - msg (SE, ngettext ("Input format %s specifies %d decimal place, " - "but the given width does not allow " - "for any decimals.", + 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 the given width does not allow " - "for any decimals.", - spec->d), - str, spec->d); - else - msg (SE, ngettext ("Output format %s specifies %d decimal place, " - "but the given width does not allow " - "for any decimals.", + "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 the given width does not allow " - "for any decimals.", - spec->d), - str, spec->d); - } + "but width %d does not allow for any decimals.", + spec.d), + str, spec.d, spec.w)); + } + + 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) + { + 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); } @@ -521,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. @@ -966,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 }; + + if (loose) + fmt_fix_output (f); + else if (!fmt_check_output (*f)) + return false; - return ok; + return fmt_check_width_compat (*f, width); } /* Returns true if TYPE may be used as an input format, @@ -1270,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;