#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:
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;
}
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;
}
: 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
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,
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;
}
\f
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,
};
}
static void
fmt_affix_free (struct fmt_affix *affix)
{
- if (affix->s[0])
+ if (affix->s)
free (affix->s);
}
.neg_suffix = neg_suffix,
.decimal = grouping == '.' ? ',' : '.',
.grouping = grouping,
+ .include_leading_zero = false,
.extra_bytes = extra_bytes,
};
return style;
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 };