X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=src%2Fdata%2Fformat.c;h=73e5dd60f4259df5932f60002a37a0c86c7225b0;hb=8502b942ecdbc7d5a688f695cebc6f1bfac79868;hp=d0b87a503f198a8d7db58978abbf6c2fa27a6d06;hpb=81e3a36c830463d97ff4f5937a670206cc26e193;p=pspp diff --git a/src/data/format.c b/src/data/format.c index d0b87a503f..73e5dd60f4 100644 --- a/src/data/format.c +++ b/src/data/format.c @@ -63,13 +63,13 @@ fmt_settings_uninit (struct fmt_settings *settings) fmt_number_style_destroy (settings->ccs[i]); } -void -fmt_settings_copy (struct fmt_settings *new, const struct fmt_settings *old) +struct fmt_settings +fmt_settings_copy (const struct fmt_settings *old) { - new->epoch = old->epoch; - new->decimal = old->decimal; + struct fmt_settings new = *old; for (int i = 0; i < FMT_N_CCS; i++) - new->ccs[i] = fmt_number_style_clone (old->ccs[i]); + new.ccs[i] = fmt_number_style_clone (old->ccs[i]); + return new; } static size_t @@ -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, 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 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 ANS2(DECIMAL, GROUPING) { \ + ANS(DECIMAL, GROUPING, false), \ + ANS(DECIMAL, GROUPING, true), \ } - 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); + /* 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 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,10 +198,7 @@ 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; + struct fmt_spec f = { .type = type, .w = w, .d = d }; assert (fmt_check_input (&f)); return f; } @@ -197,10 +208,7 @@ 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; + struct fmt_spec f = { .type = type, .w = w, .d = d }; assert (fmt_check_output (&f)); return f; } @@ -208,7 +216,8 @@ fmt_for_output (enum fmt_type type, int w, int d) /* 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 (const struct fmt_spec *input, + const struct fmt_settings *settings) { struct fmt_spec output; @@ -237,7 +246,7 @@ fmt_for_output_from_input (const struct fmt_spec *input) case FMT_PCT: { const struct fmt_number_style *style = - settings_get_style (input->type); + fmt_settings_get_style (settings, input->type); output.w += fmt_affix_width (style); if (style->grouping != 0 && input->w - input->d >= 3) @@ -339,129 +348,233 @@ 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__ (const 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)); 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; - } + return xasprintf (_("Format %s may not be used for input."), str); 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; + 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) - { - 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; - } + 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) - { - 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; - } + 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) { 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); + 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 - 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); + 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)); + } + + return NULL; +} + +char * +fmt_check_input__ (const struct fmt_spec *spec) +{ + return fmt_check__ (spec, FMT_FOR_INPUT); +} + +char * +fmt_check_output__ (const 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 (const 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) { 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) { 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__ (const 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)) { 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 (const 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__ (const 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 (const 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 @@ -926,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, @@ -1143,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; } @@ -1156,7 +1261,7 @@ static struct fmt_affix fmt_affix_clone (const struct fmt_affix *old) { return (struct fmt_affix) { - .s = old->s ? xstrdup (old->s) : NULL, + .s = xstrdup_if_nonnull (old->s), .width = old->width, }; } @@ -1165,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); } @@ -1234,11 +1339,40 @@ 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; } +static void +format_cc (struct string *out, const char *in, char grouping) +{ + while (*in != '\0') + { + char c = *in++; + if (c == grouping || c == '\'') + ds_put_byte (out, '\''); + else if (c == '"') + ds_put_byte (out, '"'); + ds_put_byte (out, c); + } +} + +char * +fmt_number_style_to_string (const struct fmt_number_style *cc) +{ + struct string out = DS_EMPTY_INITIALIZER; + format_cc (&out, cc->neg_prefix.s, cc->grouping); + ds_put_byte (&out, cc->grouping); + format_cc (&out, cc->prefix.s, cc->grouping); + ds_put_byte (&out, cc->grouping); + format_cc (&out, cc->suffix.s, cc->grouping); + ds_put_byte (&out, cc->grouping); + format_cc (&out, cc->neg_suffix.s, cc->grouping); + return ds_steal_cstr (&out); +} + struct fmt_number_style * fmt_number_style_clone (const struct fmt_number_style *old) { @@ -1305,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 };