#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)
{
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:
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;
}
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++;
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++;
}
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:
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;
break;
case FMT_AHEX:
- output.w = input->w / 2;
+ output.w = input.w / 2;
break;
case FMT_DATE:
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:
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;
}
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)
+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))
+ 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);
+ 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))
+ 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)));
+ 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)
+ 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));
+ 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)
+ 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 "
"Input format %s specifies %d decimal "
"places, but %s does not allow any "
"decimals.",
- spec->d),
- str, spec->d, fmt_name (spec->type))
+ 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)
+ spec.d),
+ str, spec.d, fmt_name (spec.type)));
+ else if (spec.d > max_d)
{
if (max_d > 0)
return (use == FMT_FOR_INPUT
? xasprintf (ngettext (
"Input format %s specifies %d decimal place, "
- "but the given width allows at most "
- "%d decimals.",
+ "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)
+ "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 the given width allows at most "
- "%d decimals.",
+ "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
return (use == FMT_FOR_INPUT
? xasprintf (ngettext (
"Input 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.",
"Input 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)
: xasprintf (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.",
"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
-fmt_emit_and_free_error (char *error)
+error_to_bool (char *error)
{
if (error)
{
- msg (SE, "%s", error);
free (error);
return false;
}
return true;
}
-/* Checks whether SPEC is valid for USE and returns nonzero if so. Otherwise,
- emits an error message for the current source location and returns zero. */
+/* Returns true if SPEC is valid for USE, false otherwise. */
bool
-fmt_check (const struct fmt_spec *spec, enum fmt_use use)
+fmt_check (struct fmt_spec spec, enum fmt_use use)
{
- return fmt_emit_and_free_error (fmt_check__ (spec, 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 NULL if so. Otherwise returns a malloc()'d error message that the
- calelr must eventually free(). */
+ caller must eventually free(). VARNAME is optional and only used in the
+ error message.*/
char *
-fmt_check_type_compat__ (const struct fmt_spec *format, enum val_type var_type)
+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];
- return xasprintf (_("%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));
+ 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 NULL;
}
-/* 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. */
+/* Returns FORMAT is appropriate for a variable of the given VAR_TYPE and
+ returns true if so, otherwise false. */
bool
-fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
+fmt_check_type_compat (struct fmt_spec format, enum val_type var_type)
{
- return fmt_emit_and_free_error (fmt_check_type_compat__ (format, var_type));
+ 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
- calelr must eventually free(). */
+ caller must eventually free(). VARNAME is optional and only used in the
+ error message. */
char *
-fmt_check_width_compat__ (const struct fmt_spec *format, int width)
+fmt_check_width_compat__ (struct fmt_spec format, const char *varname,
+ int width)
{
- char *error = fmt_check_type_compat__ (format, val_type_from_width (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];
- return xasprintf (_("String variable with width %d is not compatible "
- "with format %s."),
- width, fmt_to_string (format, str));
+ 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 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. */
+/* 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)
+fmt_check_width_compat (struct fmt_spec format, int width)
{
- return fmt_emit_and_free_error (fmt_check_width_compat__ (format, 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);
}
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.
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,
.neg_suffix = neg_suffix,
.decimal = grouping == '.' ? ',' : '.',
.grouping = grouping,
+ .include_leading_zero = false,
.extra_bytes = extra_bytes,
};
return style;