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/>. */
26 #include "data/identifier.h"
27 #include "data/settings.h"
28 #include "data/value.h"
29 #include "data/variable.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/cast.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/message.h"
34 #include "libpspp/misc.h"
35 #include "libpspp/str.h"
37 #include "gl/c-strcase.h"
38 #include "gl/minmax.h"
39 #include "gl/xalloc.h"
40 #include "gl/xmemdup0.h"
43 #define _(msgid) gettext (msgid)
45 bool is_fmt_type (enum fmt_type);
47 static bool valid_width (enum fmt_type, int width, enum fmt_use);
49 static int max_digits_for_bytes (int bytes);
50 static void fmt_clamp_width (struct fmt_spec *, enum fmt_use);
51 static void fmt_clamp_decimals (struct fmt_spec *, enum fmt_use);
54 fmt_settings_init (struct fmt_settings *settings)
56 *settings = (struct fmt_settings) FMT_SETTINGS_INIT;
60 fmt_settings_uninit (struct fmt_settings *settings)
62 for (int i = 0; i < FMT_N_CCS; i++)
63 fmt_number_style_destroy (settings->ccs[i]);
67 fmt_settings_copy (struct fmt_settings *new, const struct fmt_settings *old)
69 new->epoch = old->epoch;
70 new->decimal = old->decimal;
71 for (int i = 0; i < FMT_N_CCS; i++)
72 new->ccs[i] = fmt_number_style_clone (old->ccs[i]);
76 fmt_type_to_cc_index (enum fmt_type type)
80 case FMT_CCA: return 0;
81 case FMT_CCB: return 1;
82 case FMT_CCC: return 2;
83 case FMT_CCD: return 3;
84 case FMT_CCE: return 4;
85 default: NOT_REACHED ();
89 /* Returns the number formatting style associated with the given
91 const struct fmt_number_style *
92 fmt_settings_get_style (const struct fmt_settings *settings,
96 verify (FMT_COMMA < 6);
98 verify (FMT_DOLLAR < 6);
102 #define OPPOSITE(C) ((C) == ',' ? '.' : ',')
103 #define AFFIX(S) { .s = (char *) (S), .width = sizeof (S) - 1 }
104 #define NS(PREFIX, SUFFIX, DECIMAL, GROUPING) { \
105 .neg_prefix = AFFIX ("-"), \
106 .prefix = AFFIX (PREFIX), \
107 .suffix = AFFIX (SUFFIX), \
108 .neg_suffix = AFFIX (""), \
109 .decimal = DECIMAL, \
110 .grouping = GROUPING, \
112 #define ANS(DECIMAL, GROUPING) { \
113 [FMT_F] = NS( "", "", DECIMAL, 0), \
114 [FMT_E] = NS( "", "", DECIMAL, 0), \
115 [FMT_COMMA] = NS( "", "", DECIMAL, GROUPING), \
116 [FMT_DOT] = NS( "", "", GROUPING, DECIMAL), \
117 [FMT_DOLLAR] = NS("$", "", DECIMAL, GROUPING), \
118 [FMT_PCT] = NS( "", "%", DECIMAL, 0), \
121 static const struct fmt_number_style period_styles[6] = ANS ('.', ',');
122 static const struct fmt_number_style comma_styles[6] = ANS (',', '.');
123 static const struct fmt_number_style default_style = NS ("", "", '.', 0);
133 return (settings->decimal == '.'
134 ? &period_styles[type]
135 : &comma_styles[type]);
143 size_t idx = fmt_type_to_cc_index (type);
144 return settings->ccs[idx] ? settings->ccs[idx] : &default_style;
148 return &default_style;
155 static int epoch = 0;
159 struct tm *tm = localtime (&t);
160 epoch = (tm != NULL ? tm->tm_year + 1900 : 2000) - 69;
166 fmt_settings_get_epoch (const struct fmt_settings *settings)
168 return !settings->epoch ? default_epoch () : settings->epoch;
172 fmt_settings_set_cc (struct fmt_settings *settings, enum fmt_type type,
173 struct fmt_number_style *style)
175 size_t idx = fmt_type_to_cc_index (type);
177 fmt_number_style_destroy (settings->ccs[idx]);
178 settings->ccs[idx] = style;
182 /* Returns an input format specification with type TYPE, width W,
185 fmt_for_input (enum fmt_type type, int w, int d)
191 assert (fmt_check_input (&f));
195 /* Returns an output format specification with type TYPE, width
196 W, and D decimals. */
198 fmt_for_output (enum fmt_type type, int w, int d)
204 assert (fmt_check_output (&f));
208 /* Returns the output format specifier corresponding to input
209 format specifier INPUT. */
211 fmt_for_output_from_input (const struct fmt_spec *input)
213 struct fmt_spec output;
215 assert (fmt_check_input (input));
217 output.type = fmt_input_to_output (input->type);
219 if (output.w > fmt_max_output_width (output.type))
220 output.w = fmt_max_output_width (output.type);
221 else if (output.w < fmt_min_output_width (output.type))
222 output.w = fmt_min_output_width (output.type);
239 const struct fmt_number_style *style =
240 settings_get_style (input->type);
242 output.w += fmt_affix_width (style);
243 if (style->grouping != 0 && input->w - input->d >= 3)
244 output.w += (input->w - input->d - 1) / 3;
256 output.d = MAX (input->d, 3);
257 output.w = MAX (input->w, output.d + 7);
261 output.w = max_digits_for_bytes (input->w / 2) + 1;
272 output.w = 2 * input->w + (input->d > 0);
277 output.w = max_digits_for_bytes (input->w) + 1;
293 output.w = input->w / 2;
313 output.w = MAX (input->w, input->d + 6);
318 output.w = MAX (input->w, input->d + 20);
325 if (output.w > fmt_max_output_width (output.type))
326 output.w = fmt_max_output_width (output.type);
328 assert (fmt_check_output (&output));
332 /* Returns the default format for the given WIDTH: F8.2 format
333 for a numeric value, A format for a string value. */
335 fmt_default_for_width (int width)
338 ? fmt_for_output (FMT_F, 8, 2)
339 : fmt_for_output (FMT_A, width, 0));
342 /* Checks whether SPEC is valid for USE and returns nonzero if so.
343 Otherwise, emits an error message and returns zero. */
345 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
348 char str[FMT_STRING_LEN_MAX + 1];
349 int min_w, max_w, max_d;
351 assert (is_fmt_type (spec->type));
352 fmt_to_string (spec, str);
354 io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
355 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
357 msg (SE, _("Format %s may not be used for input."), str);
361 if (spec->w % fmt_step_width (spec->type))
363 assert (fmt_step_width (spec->type) == 2);
364 msg (SE, _("%s specifies width %d, but %s requires an even width."),
365 str, spec->w, fmt_name (spec->type));
369 min_w = fmt_min_width (spec->type, use);
370 max_w = fmt_max_width (spec->type, use);
371 if (spec->w < min_w || spec->w > max_w)
373 msg (SE, _("%s %s specifies width %d, but "
374 "%s requires a width between %d and %d."),
375 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
379 max_d = fmt_max_decimals (spec->type, spec->w, use);
380 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
382 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
383 "%s does not allow any decimals.",
384 "%s %s specifies %d decimal places, but "
385 "%s does not allow any decimals.",
387 io_fmt, str, spec->d, fmt_name (spec->type));
390 else if (spec->d > max_d)
393 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
394 "the given width allows at most %d decimals.",
395 "%s %s specifies %d decimal places, but "
396 "the given width allows at most %d decimals.",
398 io_fmt, str, spec->d, max_d);
400 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
401 "the given width does not allow for any decimals.",
402 "%s %s specifies %d decimal places, but "
403 "the given width does not allow for any decimals.",
405 io_fmt, str, spec->d);
412 /* Checks whether SPEC is valid as an input format and returns
413 nonzero if so. Otherwise, emits an error message and returns
416 fmt_check_input (const struct fmt_spec *spec)
418 return fmt_check (spec, FMT_FOR_INPUT);
421 /* Checks whether SPEC is valid as an output format and returns
422 true if so. Otherwise, emits an error message and returns false. */
424 fmt_check_output (const struct fmt_spec *spec)
426 return fmt_check (spec, FMT_FOR_OUTPUT);
429 /* Checks that FORMAT is appropriate for a variable of the given
430 VAR_TYPE and returns true if so. Otherwise returns false and
431 emits an error message. */
433 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
435 assert (val_type_is_valid (var_type));
436 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
438 char str[FMT_STRING_LEN_MAX + 1];
439 msg (SE, _("%s variables are not compatible with %s format %s."),
440 var_type == VAL_STRING ? _("String") : _("Numeric"),
441 var_type == VAL_STRING ? _("numeric") : _("string"),
442 fmt_to_string (format, str));
448 /* Checks that FORMAT is appropriate for a variable of the given
449 WIDTH and returns true if so. Otherwise returns false and
450 emits an error message. */
452 fmt_check_width_compat (const struct fmt_spec *format, int width)
454 if (!fmt_check_type_compat (format, val_type_from_width (width)))
456 if (fmt_var_width (format) != width)
458 char str[FMT_STRING_LEN_MAX + 1];
459 msg (SE, _("String variable with width %d is not compatible with "
461 width, fmt_to_string (format, str));
467 /* Returns the width corresponding to FORMAT. The return value
468 is the width of the `union value's required by FORMAT. */
470 fmt_var_width (const struct fmt_spec *format)
472 return (format->type == FMT_AHEX ? format->w / 2
473 : format->type == FMT_A ? format->w
477 /* Converts F to its string representation (for instance, "F8.2")
478 in BUFFER. Returns BUFFER.
480 If F has decimals, they are included in the output string,
481 even if F's format type does not allow decimals, to allow
482 accurately presenting incorrect formats to the user. */
484 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
486 if (fmt_takes_decimals (f->type) || f->d > 0)
487 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
488 "%s%d.%d", fmt_name (f->type), f->w, f->d);
490 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
491 "%s%d", fmt_name (f->type), f->w);
495 /* Returns true if A and B are identical formats,
498 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
500 return a->type == b->type && a->w == b->w && a->d == b->d;
503 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
504 If nothing needed to be changed the return value is false
507 fmt_resize (struct fmt_spec *fmt, int width)
509 if ((width > 0) != fmt_is_string (fmt->type))
511 /* Changed from numeric to string or vice versa. Set to
512 default format for new width. */
513 *fmt = fmt_default_for_width (width);
517 /* Changed width of string. Preserve format type, adjust
519 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
529 /* Adjusts FMT's width and decimal places to be valid for USE. */
531 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
533 /* Clamp width to those allowed by format. */
534 fmt_clamp_width (fmt, use);
536 /* If FMT has more decimal places than allowed, attempt to increase FMT's
537 width until that number of decimal places can be achieved. */
538 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
539 && fmt_takes_decimals (fmt->type))
541 int max_w = fmt_max_width (fmt->type, use);
542 for (; fmt->w < max_w; fmt->w++)
543 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
547 /* Clamp decimals to those allowed by format and width. */
548 fmt_clamp_decimals (fmt, use);
551 /* Adjusts FMT's width and decimal places to be valid for an
554 fmt_fix_input (struct fmt_spec *fmt)
556 fmt_fix (fmt, FMT_FOR_INPUT);
559 /* Adjusts FMT's width and decimal places to be valid for an
562 fmt_fix_output (struct fmt_spec *fmt)
564 fmt_fix (fmt, FMT_FOR_OUTPUT);
567 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
568 reduces its decimal places as necessary (if necessary) for that width. */
570 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
573 fmt_clamp_width (fmt, use);
574 fmt_clamp_decimals (fmt, use);
577 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
578 places allowed by FMT's type) and increases its width as necessary (if
579 necessary) for that number of decimal places. */
581 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
587 /* Describes a display format. */
591 int min_input_width, min_output_width;
593 enum fmt_category category;
596 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
598 /* Returns the name of the given format TYPE. */
600 fmt_name (enum fmt_type type)
602 return get_fmt_desc (type)->name;
605 /* Tries to parse NAME as a format type.
606 If successful, stores the type in *TYPE and returns true.
607 On failure, returns false. */
609 fmt_from_name (const char *name, enum fmt_type *type)
613 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
614 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
622 /* Returns true if TYPE accepts decimal places,
625 fmt_takes_decimals (enum fmt_type type)
627 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
630 /* Returns the minimum width of the given format TYPE for the given USE. */
632 fmt_min_width (enum fmt_type type, enum fmt_use use)
634 return (use == FMT_FOR_INPUT
635 ? fmt_min_input_width (type)
636 : fmt_min_output_width (type));
639 /* Returns the maximum width of the given format TYPE,
640 for input if FOR_INPUT is true,
641 for output otherwise. */
643 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
645 /* Maximum width is actually invariant of whether the format is
646 for input or output, so FOR_INPUT is unused. */
647 assert (is_fmt_type (type));
665 return 2 * MAX_STRING;
672 /* Returns the maximum number of decimal places allowed for the
673 given format TYPE with a width of WIDTH places, for the given USE. */
675 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
684 max_d = use == FMT_FOR_INPUT ? width : width - 1;
689 max_d = use == FMT_FOR_INPUT ? width : width - 2;
693 max_d = use == FMT_FOR_INPUT ? width : width - 7;
701 assert (use == FMT_FOR_OUTPUT);
711 max_d = width * 2 - 1;
720 max_d = max_digits_for_bytes (width);
781 /* Returns the minimum acceptable width for an input field
782 formatted with the given TYPE. */
784 fmt_min_input_width (enum fmt_type type)
786 return get_fmt_desc (type)->min_input_width;
789 /* Returns the maximum acceptable width for an input field
790 formatted with the given TYPE. */
792 fmt_max_input_width (enum fmt_type type)
794 return fmt_max_width (type, FMT_FOR_INPUT);
797 /* Returns the maximum number of decimal places allowed in an
798 input field of the given TYPE and WIDTH. */
800 fmt_max_input_decimals (enum fmt_type type, int width)
802 assert (valid_width (type, width, true));
803 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
806 /* Returns the minimum acceptable width for an output field
807 formatted with the given TYPE. */
809 fmt_min_output_width (enum fmt_type type)
811 return get_fmt_desc (type)->min_output_width;
814 /* Returns the maximum acceptable width for an output field
815 formatted with the given TYPE. */
817 fmt_max_output_width (enum fmt_type type)
819 return fmt_max_width (type, FMT_FOR_OUTPUT);
822 /* Returns the maximum number of decimal places allowed in an
823 output field of the given TYPE and WIDTH. */
825 fmt_max_output_decimals (enum fmt_type type, int width)
827 assert (valid_width (type, width, false));
828 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
831 /* Returns the width step for a field formatted with the given
832 TYPE. Field width must be a multiple of the width step. */
834 fmt_step_width (enum fmt_type type)
836 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
840 /* Returns true if TYPE is used for string fields,
841 false if it is used for numeric fields. */
843 fmt_is_string (enum fmt_type type)
845 return fmt_get_category (type) == FMT_CAT_STRING;
848 /* Returns true if TYPE is used for numeric fields,
849 false if it is used for string fields. */
851 fmt_is_numeric (enum fmt_type type)
853 return !fmt_is_string (type);
856 /* Returns the format TYPE's category.
857 Each format type is in exactly one category,
858 and each category's value is bitwise disjoint from every other
859 category. Thus, the return value may be tested for equality
860 or compared bitwise against a mask of FMT_CAT_* values. */
862 fmt_get_category (enum fmt_type type)
864 return get_fmt_desc (type)->category;
867 /* Returns the output format selected by default when TYPE is
868 used as an input format. */
870 fmt_input_to_output (enum fmt_type type)
872 switch (fmt_get_category (type))
879 case FMT_CAT_HEXADECIMAL:
887 /* Returns the SPSS format type corresponding to the given PSPP
890 fmt_to_io (enum fmt_type type)
892 return get_fmt_desc (type)->io;
895 /* Determines the PSPP format corresponding to the given SPSS
896 format type. If successful, sets *FMT_TYPE to the PSPP format
897 and returns true. On failure, return false. */
899 fmt_from_io (int io, enum fmt_type *fmt_type)
903 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
905 *fmt_type = FMT_##NAME; \
907 #include "format.def"
913 /* Translate U32, which is in the form found in SAV and SPV files, into a
914 format specification, and stores the new specification in *F.
916 If LOOSE is false, checks that the format specification is appropriate as an
917 output format for a variable with the given WIDTH and reports an error if
918 not. If LOOSE is true, instead adjusts the format's width and decimals as
919 necessary to be suitable.
921 Return true if successful, false if there was an error.. */
923 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
925 uint8_t raw_type = u32 >> 16;
926 uint8_t w = u32 >> 8;
932 bool ok = fmt_from_io (raw_type, &f->type);
938 ok = fmt_check_output (f);
941 ok = fmt_check_width_compat (f, width);
947 /* Returns true if TYPE may be used as an input format,
950 fmt_usable_for_input (enum fmt_type type)
952 assert (is_fmt_type (type));
953 return fmt_get_category (type) != FMT_CAT_CUSTOM;
956 /* For time and date formats, returns a template used for input and output in a
957 field of the given WIDTH.
959 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
960 is, whether the returned string contains "yy" or "yyyy", and whether seconds
961 are include, that is, whether the returned string contains ":SS". A caller
962 that doesn't care whether the returned string contains "yy" or "yyyy" or
963 ":SS" can just specify 0 to omit them. */
965 fmt_date_template (enum fmt_type type, int width)
1012 s1 = "dd-mmm-yyyy HH:MM";
1013 s2 = "dd-mmm-yyyy HH:MM:SS";
1017 s1 = "yyyy-mm-dd HH:MM";
1018 s2 = "yyyy-mm-dd HH:MM:SS";
1040 return width >= strlen (s2) ? s2 : s1;
1043 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1045 fmt_gui_name (enum fmt_type type)
1050 return _("Numeric");
1059 return _("Scientific");
1092 return fmt_name (type);
1096 /* Returns true if TYPE is a valid format type,
1099 is_fmt_type (enum fmt_type type)
1101 return type < FMT_NUMBER_OF_FORMATS;
1104 /* Returns true if WIDTH is a valid width for the given format
1105 TYPE, for the given USE. */
1107 valid_width (enum fmt_type type, int width, enum fmt_use use)
1109 return (width >= fmt_min_width (type, use)
1110 && width <= fmt_max_width (type, use));
1113 /* Returns the maximum number of decimal digits in an unsigned
1114 binary number that is BYTES bytes long. */
1116 max_digits_for_bytes (int bytes)
1118 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1119 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1120 return map[bytes - 1];
1123 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1125 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1130 min_w = fmt_min_width (fmt->type, use);
1131 max_w = fmt_max_width (fmt->type, use);
1134 else if (fmt->w > max_w)
1137 /* Round width to step. */
1138 step = fmt_step_width (fmt->type);
1139 fmt->w = ROUND_DOWN (fmt->w, step);
1142 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1144 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1148 max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1151 else if (fmt->d > max_d)
1155 static struct fmt_affix
1156 fmt_affix_clone (const struct fmt_affix *old)
1158 return (struct fmt_affix) {
1159 .s = old->s ? xstrdup (old->s) : NULL,
1160 .width = old->width,
1164 /* Frees data in AFFIX. */
1166 fmt_affix_free (struct fmt_affix *affix)
1172 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1175 find_cc_separators (const char *cc_string)
1177 /* Count commas and periods. There must be exactly three of
1178 one or the other, except that an apostrophe escapes a
1179 following comma or period. */
1182 for (const char *p = cc_string; *p; p++)
1187 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1190 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1195 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1196 delimited by GROUPING. Returns the first character following the token. */
1197 static struct fmt_affix
1198 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1200 const char *p = *sp;
1201 for (; *p && *p != grouping; p++)
1202 if (*p == '\'' && p[1] == grouping)
1205 size_t length = p - *sp;
1206 char *affix = xmemdup0 (*sp, length);
1207 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1209 *extra_bytes += length - width;
1211 *sp = p + (*p != 0);
1213 return (struct fmt_affix) { .s = affix, .width = width };
1216 struct fmt_number_style *
1217 fmt_number_style_from_string (const char *s)
1219 char grouping = find_cc_separators (s);
1223 size_t extra_bytes = 0;
1224 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1225 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1226 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1227 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1229 struct fmt_number_style *style = xmalloc (sizeof *style);
1230 *style = (struct fmt_number_style) {
1231 .neg_prefix = neg_prefix,
1234 .neg_suffix = neg_suffix,
1235 .decimal = grouping == '.' ? ',' : '.',
1236 .grouping = grouping,
1237 .extra_bytes = extra_bytes,
1242 struct fmt_number_style *
1243 fmt_number_style_clone (const struct fmt_number_style *old)
1247 struct fmt_number_style *new = xmalloc (sizeof *new);
1248 *new = (struct fmt_number_style) {
1249 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1250 .prefix = fmt_affix_clone (&old->prefix),
1251 .suffix = fmt_affix_clone (&old->suffix),
1252 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1253 .decimal = old->decimal,
1254 .grouping = old->grouping,
1255 .extra_bytes = old->extra_bytes,
1263 /* Destroys a struct fmt_number_style. */
1265 fmt_number_style_destroy (struct fmt_number_style *style)
1269 fmt_affix_free (&style->neg_prefix);
1270 fmt_affix_free (&style->prefix);
1271 fmt_affix_free (&style->suffix);
1272 fmt_affix_free (&style->neg_suffix);
1277 /* Returns the total width of the standard prefix and suffix for STYLE, in
1278 display columns (e.g. as returned by u8_strwidth()). */
1280 fmt_affix_width (const struct fmt_number_style *style)
1282 return style->prefix.width + style->suffix.width;
1285 /* Returns the total width of the negative prefix and suffix for STYLE, in
1286 display columns (e.g. as returned by u8_strwidth()). */
1288 fmt_neg_affix_width (const struct fmt_number_style *style)
1290 return style->neg_prefix.width + style->neg_suffix.width;
1293 /* Returns the struct fmt_desc for the given format TYPE. */
1294 static const struct fmt_desc *
1295 get_fmt_desc (enum fmt_type type)
1297 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1299 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1300 {#NAME, IMIN, OMIN, IO, CATEGORY},
1301 #include "format.def"
1304 assert (is_fmt_type (type));
1305 return &formats[type];
1308 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};
1309 const struct fmt_spec F_8_2 = {FMT_F, 8, 2};
1310 const struct fmt_spec F_4_3 = {FMT_F, 4, 3};
1311 const struct fmt_spec F_5_1 = {FMT_F, 5, 1};