1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2010, 2011, 2012 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
25 #include "data/identifier.h"
26 #include "data/settings.h"
27 #include "data/value.h"
28 #include "data/variable.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/cast.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/message.h"
33 #include "libpspp/misc.h"
34 #include "libpspp/str.h"
36 #include "gl/c-strcase.h"
37 #include "gl/minmax.h"
38 #include "gl/xalloc.h"
39 #include "gl/xmemdup0.h"
42 #define _(msgid) gettext (msgid)
44 bool is_fmt_type (enum fmt_type);
46 static bool valid_width (enum fmt_type, int width, enum fmt_use);
48 static int max_digits_for_bytes (int bytes);
49 static void fmt_clamp_width (struct fmt_spec *, enum fmt_use);
50 static void fmt_clamp_decimals (struct fmt_spec *, enum fmt_use);
53 fmt_settings_init (struct fmt_settings *settings)
55 *settings = (struct fmt_settings) FMT_SETTINGS_INIT;
59 fmt_settings_uninit (struct fmt_settings *settings)
61 for (int i = 0; i < FMT_N_CCS; i++)
62 fmt_number_style_destroy (settings->ccs[i]);
66 fmt_settings_copy (struct fmt_settings *new, const struct fmt_settings *old)
68 new->decimal = old->decimal;
69 for (int i = 0; i < FMT_N_CCS; i++)
70 new->ccs[i] = fmt_number_style_clone (old->ccs[i]);
74 fmt_type_to_cc_index (enum fmt_type type)
78 case FMT_CCA: return 0;
79 case FMT_CCB: return 1;
80 case FMT_CCC: return 2;
81 case FMT_CCD: return 3;
82 case FMT_CCE: return 4;
83 default: NOT_REACHED ();
87 /* Returns the number formatting style associated with the given
89 const struct fmt_number_style *
90 fmt_settings_get_style (const struct fmt_settings *settings,
94 verify (FMT_COMMA < 6);
96 verify (FMT_DOLLAR < 6);
100 #define OPPOSITE(C) ((C) == ',' ? '.' : ',')
101 #define AFFIX(S) { .s = (char *) (S), .width = sizeof (S) - 1 }
102 #define NS(PREFIX, SUFFIX, DECIMAL, GROUPING) { \
103 .neg_prefix = AFFIX ("-"), \
104 .prefix = AFFIX (PREFIX), \
105 .suffix = AFFIX (SUFFIX), \
106 .neg_suffix = AFFIX (""), \
107 .decimal = DECIMAL, \
108 .grouping = GROUPING, \
110 #define ANS(DECIMAL, GROUPING) { \
111 [FMT_F] = NS( "", "", DECIMAL, 0), \
112 [FMT_E] = NS( "", "", DECIMAL, 0), \
113 [FMT_COMMA] = NS( "", "", DECIMAL, GROUPING), \
114 [FMT_DOT] = NS( "", "", GROUPING, DECIMAL), \
115 [FMT_DOLLAR] = NS("$", "", DECIMAL, GROUPING), \
116 [FMT_PCT] = NS( "", "%", DECIMAL, 0), \
119 static const struct fmt_number_style period_styles[6] = ANS ('.', ',');
120 static const struct fmt_number_style comma_styles[6] = ANS (',', '.');
121 static const struct fmt_number_style default_style = NS ("", "", '.', 0);
131 return (settings->decimal == '.'
132 ? &period_styles[type]
133 : &comma_styles[type]);
141 size_t idx = fmt_type_to_cc_index (type);
142 return settings->ccs[idx] ? settings->ccs[idx] : &default_style;
146 return &default_style;
151 fmt_settings_set_cc (struct fmt_settings *settings, enum fmt_type type,
152 struct fmt_number_style *style)
154 size_t idx = fmt_type_to_cc_index (type);
156 fmt_number_style_destroy (settings->ccs[idx]);
157 settings->ccs[idx] = style;
161 /* Returns an input format specification with type TYPE, width W,
164 fmt_for_input (enum fmt_type type, int w, int d)
170 assert (fmt_check_input (&f));
174 /* Returns an output format specification with type TYPE, width
175 W, and D decimals. */
177 fmt_for_output (enum fmt_type type, int w, int d)
183 assert (fmt_check_output (&f));
187 /* Returns the output format specifier corresponding to input
188 format specifier INPUT. */
190 fmt_for_output_from_input (const struct fmt_spec *input)
192 struct fmt_spec output;
194 assert (fmt_check_input (input));
196 output.type = fmt_input_to_output (input->type);
198 if (output.w > fmt_max_output_width (output.type))
199 output.w = fmt_max_output_width (output.type);
200 else if (output.w < fmt_min_output_width (output.type))
201 output.w = fmt_min_output_width (output.type);
218 const struct fmt_number_style *style =
219 settings_get_style (input->type);
221 output.w += fmt_affix_width (style);
222 if (style->grouping != 0 && input->w - input->d >= 3)
223 output.w += (input->w - input->d - 1) / 3;
235 output.d = MAX (input->d, 3);
236 output.w = MAX (input->w, output.d + 7);
240 output.w = max_digits_for_bytes (input->w / 2) + 1;
251 output.w = 2 * input->w + (input->d > 0);
256 output.w = max_digits_for_bytes (input->w) + 1;
272 output.w = input->w / 2;
292 output.w = MAX (input->w, input->d + 6);
297 output.w = MAX (input->w, input->d + 20);
304 if (output.w > fmt_max_output_width (output.type))
305 output.w = fmt_max_output_width (output.type);
307 assert (fmt_check_output (&output));
311 /* Returns the default format for the given WIDTH: F8.2 format
312 for a numeric value, A format for a string value. */
314 fmt_default_for_width (int width)
317 ? fmt_for_output (FMT_F, 8, 2)
318 : fmt_for_output (FMT_A, width, 0));
321 /* Checks whether SPEC is valid for USE and returns nonzero if so.
322 Otherwise, emits an error message and returns zero. */
324 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
327 char str[FMT_STRING_LEN_MAX + 1];
328 int min_w, max_w, max_d;
330 assert (is_fmt_type (spec->type));
331 fmt_to_string (spec, str);
333 io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
334 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
336 msg (SE, _("Format %s may not be used for input."), str);
340 if (spec->w % fmt_step_width (spec->type))
342 assert (fmt_step_width (spec->type) == 2);
343 msg (SE, _("%s specifies width %d, but %s requires an even width."),
344 str, spec->w, fmt_name (spec->type));
348 min_w = fmt_min_width (spec->type, use);
349 max_w = fmt_max_width (spec->type, use);
350 if (spec->w < min_w || spec->w > max_w)
352 msg (SE, _("%s %s specifies width %d, but "
353 "%s requires a width between %d and %d."),
354 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
358 max_d = fmt_max_decimals (spec->type, spec->w, use);
359 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
361 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
362 "%s does not allow any decimals.",
363 "%s %s specifies %d decimal places, but "
364 "%s does not allow any decimals.",
366 io_fmt, str, spec->d, fmt_name (spec->type));
369 else if (spec->d > max_d)
372 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
373 "the given width allows at most %d decimals.",
374 "%s %s specifies %d decimal places, but "
375 "the given width allows at most %d decimals.",
377 io_fmt, str, spec->d, max_d);
379 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
380 "the given width does not allow for any decimals.",
381 "%s %s specifies %d decimal places, but "
382 "the given width does not allow for any decimals.",
384 io_fmt, str, spec->d);
391 /* Checks whether SPEC is valid as an input format and returns
392 nonzero if so. Otherwise, emits an error message and returns
395 fmt_check_input (const struct fmt_spec *spec)
397 return fmt_check (spec, FMT_FOR_INPUT);
400 /* Checks whether SPEC is valid as an output format and returns
401 true if so. Otherwise, emits an error message and returns false. */
403 fmt_check_output (const struct fmt_spec *spec)
405 return fmt_check (spec, FMT_FOR_OUTPUT);
408 /* Checks that FORMAT is appropriate for a variable of the given
409 VAR_TYPE and returns true if so. Otherwise returns false and
410 emits an error message. */
412 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
414 assert (val_type_is_valid (var_type));
415 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
417 char str[FMT_STRING_LEN_MAX + 1];
418 msg (SE, _("%s variables are not compatible with %s format %s."),
419 var_type == VAL_STRING ? _("String") : _("Numeric"),
420 var_type == VAL_STRING ? _("numeric") : _("string"),
421 fmt_to_string (format, str));
427 /* Checks that FORMAT is appropriate for a variable of the given
428 WIDTH and returns true if so. Otherwise returns false and
429 emits an error message. */
431 fmt_check_width_compat (const struct fmt_spec *format, int width)
433 if (!fmt_check_type_compat (format, val_type_from_width (width)))
435 if (fmt_var_width (format) != width)
437 char str[FMT_STRING_LEN_MAX + 1];
438 msg (SE, _("String variable with width %d is not compatible with "
440 width, fmt_to_string (format, str));
446 /* Returns the width corresponding to FORMAT. The return value
447 is the width of the `union value's required by FORMAT. */
449 fmt_var_width (const struct fmt_spec *format)
451 return (format->type == FMT_AHEX ? format->w / 2
452 : format->type == FMT_A ? format->w
456 /* Converts F to its string representation (for instance, "F8.2")
457 in BUFFER. Returns BUFFER.
459 If F has decimals, they are included in the output string,
460 even if F's format type does not allow decimals, to allow
461 accurately presenting incorrect formats to the user. */
463 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
465 if (fmt_takes_decimals (f->type) || f->d > 0)
466 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
467 "%s%d.%d", fmt_name (f->type), f->w, f->d);
469 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
470 "%s%d", fmt_name (f->type), f->w);
474 /* Returns true if A and B are identical formats,
477 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
479 return a->type == b->type && a->w == b->w && a->d == b->d;
482 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
483 If nothing needed to be changed the return value is false
486 fmt_resize (struct fmt_spec *fmt, int width)
488 if ((width > 0) != fmt_is_string (fmt->type))
490 /* Changed from numeric to string or vice versa. Set to
491 default format for new width. */
492 *fmt = fmt_default_for_width (width);
496 /* Changed width of string. Preserve format type, adjust
498 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
508 /* Adjusts FMT's width and decimal places to be valid for USE. */
510 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
512 /* Clamp width to those allowed by format. */
513 fmt_clamp_width (fmt, use);
515 /* If FMT has more decimal places than allowed, attempt to increase FMT's
516 width until that number of decimal places can be achieved. */
517 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
518 && fmt_takes_decimals (fmt->type))
520 int max_w = fmt_max_width (fmt->type, use);
521 for (; fmt->w < max_w; fmt->w++)
522 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
526 /* Clamp decimals to those allowed by format and width. */
527 fmt_clamp_decimals (fmt, use);
530 /* Adjusts FMT's width and decimal places to be valid for an
533 fmt_fix_input (struct fmt_spec *fmt)
535 fmt_fix (fmt, FMT_FOR_INPUT);
538 /* Adjusts FMT's width and decimal places to be valid for an
541 fmt_fix_output (struct fmt_spec *fmt)
543 fmt_fix (fmt, FMT_FOR_OUTPUT);
546 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
547 reduces its decimal places as necessary (if necessary) for that width. */
549 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
552 fmt_clamp_width (fmt, use);
553 fmt_clamp_decimals (fmt, use);
556 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
557 places allowed by FMT's type) and increases its width as necessary (if
558 necessary) for that number of decimal places. */
560 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
566 /* Describes a display format. */
570 int min_input_width, min_output_width;
572 enum fmt_category category;
575 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
577 /* Returns the name of the given format TYPE. */
579 fmt_name (enum fmt_type type)
581 return get_fmt_desc (type)->name;
584 /* Tries to parse NAME as a format type.
585 If successful, stores the type in *TYPE and returns true.
586 On failure, returns false. */
588 fmt_from_name (const char *name, enum fmt_type *type)
592 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
593 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
601 /* Returns true if TYPE accepts decimal places,
604 fmt_takes_decimals (enum fmt_type type)
606 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
609 /* Returns the minimum width of the given format TYPE for the given USE. */
611 fmt_min_width (enum fmt_type type, enum fmt_use use)
613 return (use == FMT_FOR_INPUT
614 ? fmt_min_input_width (type)
615 : fmt_min_output_width (type));
618 /* Returns the maximum width of the given format TYPE,
619 for input if FOR_INPUT is true,
620 for output otherwise. */
622 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
624 /* Maximum width is actually invariant of whether the format is
625 for input or output, so FOR_INPUT is unused. */
626 assert (is_fmt_type (type));
644 return 2 * MAX_STRING;
651 /* Returns the maximum number of decimal places allowed for the
652 given format TYPE with a width of WIDTH places, for the given USE. */
654 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
663 max_d = use == FMT_FOR_INPUT ? width : width - 1;
668 max_d = use == FMT_FOR_INPUT ? width : width - 2;
672 max_d = use == FMT_FOR_INPUT ? width : width - 7;
680 assert (use == FMT_FOR_OUTPUT);
690 max_d = width * 2 - 1;
699 max_d = max_digits_for_bytes (width);
760 /* Returns the minimum acceptable width for an input field
761 formatted with the given TYPE. */
763 fmt_min_input_width (enum fmt_type type)
765 return get_fmt_desc (type)->min_input_width;
768 /* Returns the maximum acceptable width for an input field
769 formatted with the given TYPE. */
771 fmt_max_input_width (enum fmt_type type)
773 return fmt_max_width (type, FMT_FOR_INPUT);
776 /* Returns the maximum number of decimal places allowed in an
777 input field of the given TYPE and WIDTH. */
779 fmt_max_input_decimals (enum fmt_type type, int width)
781 assert (valid_width (type, width, true));
782 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
785 /* Returns the minimum acceptable width for an output field
786 formatted with the given TYPE. */
788 fmt_min_output_width (enum fmt_type type)
790 return get_fmt_desc (type)->min_output_width;
793 /* Returns the maximum acceptable width for an output field
794 formatted with the given TYPE. */
796 fmt_max_output_width (enum fmt_type type)
798 return fmt_max_width (type, FMT_FOR_OUTPUT);
801 /* Returns the maximum number of decimal places allowed in an
802 output field of the given TYPE and WIDTH. */
804 fmt_max_output_decimals (enum fmt_type type, int width)
806 assert (valid_width (type, width, false));
807 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
810 /* Returns the width step for a field formatted with the given
811 TYPE. Field width must be a multiple of the width step. */
813 fmt_step_width (enum fmt_type type)
815 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
819 /* Returns true if TYPE is used for string fields,
820 false if it is used for numeric fields. */
822 fmt_is_string (enum fmt_type type)
824 return fmt_get_category (type) == FMT_CAT_STRING;
827 /* Returns true if TYPE is used for numeric fields,
828 false if it is used for string fields. */
830 fmt_is_numeric (enum fmt_type type)
832 return !fmt_is_string (type);
835 /* Returns the format TYPE's category.
836 Each format type is in exactly one category,
837 and each category's value is bitwise disjoint from every other
838 category. Thus, the return value may be tested for equality
839 or compared bitwise against a mask of FMT_CAT_* values. */
841 fmt_get_category (enum fmt_type type)
843 return get_fmt_desc (type)->category;
846 /* Returns the output format selected by default when TYPE is
847 used as an input format. */
849 fmt_input_to_output (enum fmt_type type)
851 switch (fmt_get_category (type))
858 case FMT_CAT_HEXADECIMAL:
866 /* Returns the SPSS format type corresponding to the given PSPP
869 fmt_to_io (enum fmt_type type)
871 return get_fmt_desc (type)->io;
874 /* Determines the PSPP format corresponding to the given SPSS
875 format type. If successful, sets *FMT_TYPE to the PSPP format
876 and returns true. On failure, return false. */
878 fmt_from_io (int io, enum fmt_type *fmt_type)
882 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
884 *fmt_type = FMT_##NAME; \
886 #include "format.def"
892 /* Translate U32, which is in the form found in SAV and SPV files, into a
893 format specification, and stores the new specification in *F.
895 If LOOSE is false, checks that the format specification is appropriate as an
896 output format for a variable with the given WIDTH and reports an error if
897 not. If LOOSE is true, instead adjusts the format's width and decimals as
898 necessary to be suitable.
900 Return true if successful, false if there was an error.. */
902 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
904 uint8_t raw_type = u32 >> 16;
905 uint8_t w = u32 >> 8;
911 bool ok = fmt_from_io (raw_type, &f->type);
917 ok = fmt_check_output (f);
920 ok = fmt_check_width_compat (f, width);
926 /* Returns true if TYPE may be used as an input format,
929 fmt_usable_for_input (enum fmt_type type)
931 assert (is_fmt_type (type));
932 return fmt_get_category (type) != FMT_CAT_CUSTOM;
935 /* For time and date formats, returns a template used for input and output in a
936 field of the given WIDTH.
938 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
939 is, whether the returned string contains "yy" or "yyyy", and whether seconds
940 are include, that is, whether the returned string contains ":SS". A caller
941 that doesn't care whether the returned string contains "yy" or "yyyy" or
942 ":SS" can just specify 0 to omit them. */
944 fmt_date_template (enum fmt_type type, int width)
991 s1 = "dd-mmm-yyyy HH:MM";
992 s2 = "dd-mmm-yyyy HH:MM:SS";
996 s1 = "yyyy-mm-dd HH:MM";
997 s2 = "yyyy-mm-dd HH:MM:SS";
1019 return width >= strlen (s2) ? s2 : s1;
1022 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1024 fmt_gui_name (enum fmt_type type)
1029 return _("Numeric");
1038 return _("Scientific");
1071 return fmt_name (type);
1075 /* Returns true if TYPE is a valid format type,
1078 is_fmt_type (enum fmt_type type)
1080 return type < FMT_NUMBER_OF_FORMATS;
1083 /* Returns true if WIDTH is a valid width for the given format
1084 TYPE, for the given USE. */
1086 valid_width (enum fmt_type type, int width, enum fmt_use use)
1088 return (width >= fmt_min_width (type, use)
1089 && width <= fmt_max_width (type, use));
1092 /* Returns the maximum number of decimal digits in an unsigned
1093 binary number that is BYTES bytes long. */
1095 max_digits_for_bytes (int bytes)
1097 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1098 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1099 return map[bytes - 1];
1102 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1104 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1109 min_w = fmt_min_width (fmt->type, use);
1110 max_w = fmt_max_width (fmt->type, use);
1113 else if (fmt->w > max_w)
1116 /* Round width to step. */
1117 step = fmt_step_width (fmt->type);
1118 fmt->w = ROUND_DOWN (fmt->w, step);
1121 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1123 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1127 max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1130 else if (fmt->d > max_d)
1134 static struct fmt_affix
1135 fmt_affix_clone (const struct fmt_affix *old)
1137 return (struct fmt_affix) {
1138 .s = old->s ? xstrdup (old->s) : NULL,
1139 .width = old->width,
1143 /* Frees data in AFFIX. */
1145 fmt_affix_free (struct fmt_affix *affix)
1151 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1154 find_cc_separators (const char *cc_string)
1156 /* Count commas and periods. There must be exactly three of
1157 one or the other, except that an apostrophe escapes a
1158 following comma or period. */
1161 for (const char *p = cc_string; *p; p++)
1166 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1169 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1174 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1175 delimited by GROUPING. Returns the first character following the token. */
1176 static struct fmt_affix
1177 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1179 const char *p = *sp;
1180 for (; *p && *p != grouping; p++)
1181 if (*p == '\'' && p[1] == grouping)
1184 size_t length = p - *sp;
1185 char *affix = xmemdup0 (*sp, length);
1186 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1188 *extra_bytes += length - width;
1190 *sp = p + (*p != 0);
1192 return (struct fmt_affix) { .s = affix, .width = width };
1195 struct fmt_number_style *
1196 fmt_number_style_from_string (const char *s)
1198 char grouping = find_cc_separators (s);
1202 size_t extra_bytes = 0;
1203 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1204 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1205 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1206 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1208 struct fmt_number_style *style = xmalloc (sizeof *style);
1209 *style = (struct fmt_number_style) {
1210 .neg_prefix = neg_prefix,
1213 .neg_suffix = neg_suffix,
1214 .decimal = grouping == '.' ? ',' : '.',
1215 .grouping = grouping,
1216 .extra_bytes = extra_bytes,
1221 struct fmt_number_style *
1222 fmt_number_style_clone (const struct fmt_number_style *old)
1226 struct fmt_number_style *new = xmalloc (sizeof *new);
1227 *new = (struct fmt_number_style) {
1228 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1229 .prefix = fmt_affix_clone (&old->prefix),
1230 .suffix = fmt_affix_clone (&old->suffix),
1231 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1232 .decimal = old->decimal,
1233 .grouping = old->grouping,
1234 .extra_bytes = old->extra_bytes,
1242 /* Destroys a struct fmt_number_style. */
1244 fmt_number_style_destroy (struct fmt_number_style *style)
1248 fmt_affix_free (&style->neg_prefix);
1249 fmt_affix_free (&style->prefix);
1250 fmt_affix_free (&style->suffix);
1251 fmt_affix_free (&style->neg_suffix);
1256 /* Returns the total width of the standard prefix and suffix for STYLE, in
1257 display columns (e.g. as returned by u8_strwidth()). */
1259 fmt_affix_width (const struct fmt_number_style *style)
1261 return style->prefix.width + style->suffix.width;
1264 /* Returns the total width of the negative prefix and suffix for STYLE, in
1265 display columns (e.g. as returned by u8_strwidth()). */
1267 fmt_neg_affix_width (const struct fmt_number_style *style)
1269 return style->neg_prefix.width + style->neg_suffix.width;
1272 /* Returns the struct fmt_desc for the given format TYPE. */
1273 static const struct fmt_desc *
1274 get_fmt_desc (enum fmt_type type)
1276 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1278 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1279 {#NAME, IMIN, OMIN, IO, CATEGORY},
1280 #include "format.def"
1283 assert (is_fmt_type (type));
1284 return &formats[type];
1287 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};
1288 const struct fmt_spec F_8_2 = {FMT_F, 8, 2};
1289 const struct fmt_spec F_4_3 = {FMT_F, 4, 3};
1290 const struct fmt_spec F_5_1 = {FMT_F, 5, 1};