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 (const struct fmt_settings *old)
69 struct fmt_settings new = *old;
70 for (int i = 0; i < FMT_N_CCS; i++)
71 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)
187 struct fmt_spec f = { .type = type, .w = w, .d = d };
188 assert (fmt_check_input (&f));
192 /* Returns an output format specification with type TYPE, width
193 W, and D decimals. */
195 fmt_for_output (enum fmt_type type, int w, int d)
197 struct fmt_spec f = { .type = type, .w = w, .d = d };
198 assert (fmt_check_output (&f));
202 /* Returns the output format specifier corresponding to input
203 format specifier INPUT. */
205 fmt_for_output_from_input (const struct fmt_spec *input,
206 const struct fmt_settings *settings)
208 struct fmt_spec output;
210 assert (fmt_check_input (input));
212 output.type = fmt_input_to_output (input->type);
214 if (output.w > fmt_max_output_width (output.type))
215 output.w = fmt_max_output_width (output.type);
216 else if (output.w < fmt_min_output_width (output.type))
217 output.w = fmt_min_output_width (output.type);
234 const struct fmt_number_style *style =
235 fmt_settings_get_style (settings, input->type);
237 output.w += fmt_affix_width (style);
238 if (style->grouping != 0 && input->w - input->d >= 3)
239 output.w += (input->w - input->d - 1) / 3;
251 output.d = MAX (input->d, 3);
252 output.w = MAX (input->w, output.d + 7);
256 output.w = max_digits_for_bytes (input->w / 2) + 1;
267 output.w = 2 * input->w + (input->d > 0);
272 output.w = max_digits_for_bytes (input->w) + 1;
288 output.w = input->w / 2;
308 output.w = MAX (input->w, input->d + 6);
313 output.w = MAX (input->w, input->d + 20);
320 if (output.w > fmt_max_output_width (output.type))
321 output.w = fmt_max_output_width (output.type);
323 assert (fmt_check_output (&output));
327 /* Returns the default format for the given WIDTH: F8.2 format
328 for a numeric value, A format for a string value. */
330 fmt_default_for_width (int width)
333 ? fmt_for_output (FMT_F, 8, 2)
334 : fmt_for_output (FMT_A, width, 0));
337 /* Checks whether SPEC is valid for USE and returns nonzero if so.
338 Otherwise, emits an error message and returns zero. */
340 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
343 char str[FMT_STRING_LEN_MAX + 1];
344 int min_w, max_w, max_d;
346 assert (is_fmt_type (spec->type));
347 fmt_to_string (spec, str);
349 io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
350 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
352 msg (SE, _("Format %s may not be used for input."), str);
356 if (spec->w % fmt_step_width (spec->type))
358 assert (fmt_step_width (spec->type) == 2);
359 msg (SE, _("%s specifies width %d, but %s requires an even width."),
360 str, spec->w, fmt_name (spec->type));
364 min_w = fmt_min_width (spec->type, use);
365 max_w = fmt_max_width (spec->type, use);
366 if (spec->w < min_w || spec->w > max_w)
368 msg (SE, _("%s %s specifies width %d, but "
369 "%s requires a width between %d and %d."),
370 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
374 max_d = fmt_max_decimals (spec->type, spec->w, use);
375 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
377 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
378 "%s does not allow any decimals.",
379 "%s %s specifies %d decimal places, but "
380 "%s does not allow any decimals.",
382 io_fmt, str, spec->d, fmt_name (spec->type));
385 else if (spec->d > max_d)
388 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
389 "the given width allows at most %d decimals.",
390 "%s %s specifies %d decimal places, but "
391 "the given width allows at most %d decimals.",
393 io_fmt, str, spec->d, max_d);
395 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
396 "the given width does not allow for any decimals.",
397 "%s %s specifies %d decimal places, but "
398 "the given width does not allow for any decimals.",
400 io_fmt, str, spec->d);
407 /* Checks whether SPEC is valid as an input format and returns
408 nonzero if so. Otherwise, emits an error message and returns
411 fmt_check_input (const struct fmt_spec *spec)
413 return fmt_check (spec, FMT_FOR_INPUT);
416 /* Checks whether SPEC is valid as an output format and returns
417 true if so. Otherwise, emits an error message and returns false. */
419 fmt_check_output (const struct fmt_spec *spec)
421 return fmt_check (spec, FMT_FOR_OUTPUT);
424 /* Checks that FORMAT is appropriate for a variable of the given
425 VAR_TYPE and returns true if so. Otherwise returns false and
426 emits an error message. */
428 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
430 assert (val_type_is_valid (var_type));
431 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
433 char str[FMT_STRING_LEN_MAX + 1];
434 msg (SE, _("%s variables are not compatible with %s format %s."),
435 var_type == VAL_STRING ? _("String") : _("Numeric"),
436 var_type == VAL_STRING ? _("numeric") : _("string"),
437 fmt_to_string (format, str));
443 /* Checks that FORMAT is appropriate for a variable of the given
444 WIDTH and returns true if so. Otherwise returns false and
445 emits an error message. */
447 fmt_check_width_compat (const struct fmt_spec *format, int width)
449 if (!fmt_check_type_compat (format, val_type_from_width (width)))
451 if (fmt_var_width (format) != width)
453 char str[FMT_STRING_LEN_MAX + 1];
454 msg (SE, _("String variable with width %d is not compatible with "
456 width, fmt_to_string (format, str));
462 /* Returns the width corresponding to FORMAT. The return value
463 is the width of the `union value's required by FORMAT. */
465 fmt_var_width (const struct fmt_spec *format)
467 return (format->type == FMT_AHEX ? format->w / 2
468 : format->type == FMT_A ? format->w
472 /* Converts F to its string representation (for instance, "F8.2")
473 in BUFFER. Returns BUFFER.
475 If F has decimals, they are included in the output string,
476 even if F's format type does not allow decimals, to allow
477 accurately presenting incorrect formats to the user. */
479 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
481 if (fmt_takes_decimals (f->type) || f->d > 0)
482 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
483 "%s%d.%d", fmt_name (f->type), f->w, f->d);
485 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
486 "%s%d", fmt_name (f->type), f->w);
490 /* Returns true if A and B are identical formats,
493 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
495 return a->type == b->type && a->w == b->w && a->d == b->d;
498 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
499 If nothing needed to be changed the return value is false
502 fmt_resize (struct fmt_spec *fmt, int width)
504 if ((width > 0) != fmt_is_string (fmt->type))
506 /* Changed from numeric to string or vice versa. Set to
507 default format for new width. */
508 *fmt = fmt_default_for_width (width);
512 /* Changed width of string. Preserve format type, adjust
514 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
524 /* Adjusts FMT's width and decimal places to be valid for USE. */
526 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
528 /* Clamp width to those allowed by format. */
529 fmt_clamp_width (fmt, use);
531 /* If FMT has more decimal places than allowed, attempt to increase FMT's
532 width until that number of decimal places can be achieved. */
533 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
534 && fmt_takes_decimals (fmt->type))
536 int max_w = fmt_max_width (fmt->type, use);
537 for (; fmt->w < max_w; fmt->w++)
538 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
542 /* Clamp decimals to those allowed by format and width. */
543 fmt_clamp_decimals (fmt, use);
546 /* Adjusts FMT's width and decimal places to be valid for an
549 fmt_fix_input (struct fmt_spec *fmt)
551 fmt_fix (fmt, FMT_FOR_INPUT);
554 /* Adjusts FMT's width and decimal places to be valid for an
557 fmt_fix_output (struct fmt_spec *fmt)
559 fmt_fix (fmt, FMT_FOR_OUTPUT);
562 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
563 reduces its decimal places as necessary (if necessary) for that width. */
565 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
568 fmt_clamp_width (fmt, use);
569 fmt_clamp_decimals (fmt, use);
572 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
573 places allowed by FMT's type) and increases its width as necessary (if
574 necessary) for that number of decimal places. */
576 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
582 /* Describes a display format. */
586 int min_input_width, min_output_width;
588 enum fmt_category category;
591 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
593 /* Returns the name of the given format TYPE. */
595 fmt_name (enum fmt_type type)
597 return get_fmt_desc (type)->name;
600 /* Tries to parse NAME as a format type.
601 If successful, stores the type in *TYPE and returns true.
602 On failure, returns false. */
604 fmt_from_name (const char *name, enum fmt_type *type)
608 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
609 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
617 /* Returns true if TYPE accepts decimal places,
620 fmt_takes_decimals (enum fmt_type type)
622 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
625 /* Returns the minimum width of the given format TYPE for the given USE. */
627 fmt_min_width (enum fmt_type type, enum fmt_use use)
629 return (use == FMT_FOR_INPUT
630 ? fmt_min_input_width (type)
631 : fmt_min_output_width (type));
634 /* Returns the maximum width of the given format TYPE,
635 for input if FOR_INPUT is true,
636 for output otherwise. */
638 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
640 /* Maximum width is actually invariant of whether the format is
641 for input or output, so FOR_INPUT is unused. */
642 assert (is_fmt_type (type));
660 return 2 * MAX_STRING;
667 /* Returns the maximum number of decimal places allowed for the
668 given format TYPE with a width of WIDTH places, for the given USE. */
670 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
679 max_d = use == FMT_FOR_INPUT ? width : width - 1;
684 max_d = use == FMT_FOR_INPUT ? width : width - 2;
688 max_d = use == FMT_FOR_INPUT ? width : width - 7;
696 assert (use == FMT_FOR_OUTPUT);
706 max_d = width * 2 - 1;
715 max_d = max_digits_for_bytes (width);
776 /* Returns the minimum acceptable width for an input field
777 formatted with the given TYPE. */
779 fmt_min_input_width (enum fmt_type type)
781 return get_fmt_desc (type)->min_input_width;
784 /* Returns the maximum acceptable width for an input field
785 formatted with the given TYPE. */
787 fmt_max_input_width (enum fmt_type type)
789 return fmt_max_width (type, FMT_FOR_INPUT);
792 /* Returns the maximum number of decimal places allowed in an
793 input field of the given TYPE and WIDTH. */
795 fmt_max_input_decimals (enum fmt_type type, int width)
797 assert (valid_width (type, width, true));
798 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
801 /* Returns the minimum acceptable width for an output field
802 formatted with the given TYPE. */
804 fmt_min_output_width (enum fmt_type type)
806 return get_fmt_desc (type)->min_output_width;
809 /* Returns the maximum acceptable width for an output field
810 formatted with the given TYPE. */
812 fmt_max_output_width (enum fmt_type type)
814 return fmt_max_width (type, FMT_FOR_OUTPUT);
817 /* Returns the maximum number of decimal places allowed in an
818 output field of the given TYPE and WIDTH. */
820 fmt_max_output_decimals (enum fmt_type type, int width)
822 assert (valid_width (type, width, false));
823 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
826 /* Returns the width step for a field formatted with the given
827 TYPE. Field width must be a multiple of the width step. */
829 fmt_step_width (enum fmt_type type)
831 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
835 /* Returns true if TYPE is used for string fields,
836 false if it is used for numeric fields. */
838 fmt_is_string (enum fmt_type type)
840 return fmt_get_category (type) == FMT_CAT_STRING;
843 /* Returns true if TYPE is used for numeric fields,
844 false if it is used for string fields. */
846 fmt_is_numeric (enum fmt_type type)
848 return !fmt_is_string (type);
851 /* Returns the format TYPE's category.
852 Each format type is in exactly one category,
853 and each category's value is bitwise disjoint from every other
854 category. Thus, the return value may be tested for equality
855 or compared bitwise against a mask of FMT_CAT_* values. */
857 fmt_get_category (enum fmt_type type)
859 return get_fmt_desc (type)->category;
862 /* Returns the output format selected by default when TYPE is
863 used as an input format. */
865 fmt_input_to_output (enum fmt_type type)
867 switch (fmt_get_category (type))
874 case FMT_CAT_HEXADECIMAL:
882 /* Returns the SPSS format type corresponding to the given PSPP
885 fmt_to_io (enum fmt_type type)
887 return get_fmt_desc (type)->io;
890 /* Determines the PSPP format corresponding to the given SPSS
891 format type. If successful, sets *FMT_TYPE to the PSPP format
892 and returns true. On failure, return false. */
894 fmt_from_io (int io, enum fmt_type *fmt_type)
898 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
900 *fmt_type = FMT_##NAME; \
902 #include "format.def"
908 /* Translate U32, which is in the form found in SAV and SPV files, into a
909 format specification, and stores the new specification in *F.
911 If LOOSE is false, checks that the format specification is appropriate as an
912 output format for a variable with the given WIDTH and reports an error if
913 not. If LOOSE is true, instead adjusts the format's width and decimals as
914 necessary to be suitable.
916 Return true if successful, false if there was an error.. */
918 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
920 uint8_t raw_type = u32 >> 16;
921 uint8_t w = u32 >> 8;
927 bool ok = fmt_from_io (raw_type, &f->type);
933 ok = fmt_check_output (f);
936 ok = fmt_check_width_compat (f, width);
942 /* Returns true if TYPE may be used as an input format,
945 fmt_usable_for_input (enum fmt_type type)
947 assert (is_fmt_type (type));
948 return fmt_get_category (type) != FMT_CAT_CUSTOM;
951 /* For time and date formats, returns a template used for input and output in a
952 field of the given WIDTH.
954 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
955 is, whether the returned string contains "yy" or "yyyy", and whether seconds
956 are include, that is, whether the returned string contains ":SS". A caller
957 that doesn't care whether the returned string contains "yy" or "yyyy" or
958 ":SS" can just specify 0 to omit them. */
960 fmt_date_template (enum fmt_type type, int width)
1007 s1 = "dd-mmm-yyyy HH:MM";
1008 s2 = "dd-mmm-yyyy HH:MM:SS";
1012 s1 = "yyyy-mm-dd HH:MM";
1013 s2 = "yyyy-mm-dd HH:MM:SS";
1035 return width >= strlen (s2) ? s2 : s1;
1038 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1040 fmt_gui_name (enum fmt_type type)
1045 return _("Numeric");
1054 return _("Scientific");
1087 return fmt_name (type);
1091 /* Returns true if TYPE is a valid format type,
1094 is_fmt_type (enum fmt_type type)
1096 return type < FMT_NUMBER_OF_FORMATS;
1099 /* Returns true if WIDTH is a valid width for the given format
1100 TYPE, for the given USE. */
1102 valid_width (enum fmt_type type, int width, enum fmt_use use)
1104 return (width >= fmt_min_width (type, use)
1105 && width <= fmt_max_width (type, use));
1108 /* Returns the maximum number of decimal digits in an unsigned
1109 binary number that is BYTES bytes long. */
1111 max_digits_for_bytes (int bytes)
1113 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1114 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1115 return map[bytes - 1];
1118 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1120 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1125 min_w = fmt_min_width (fmt->type, use);
1126 max_w = fmt_max_width (fmt->type, use);
1129 else if (fmt->w > max_w)
1132 /* Round width to step. */
1133 step = fmt_step_width (fmt->type);
1134 fmt->w = ROUND_DOWN (fmt->w, step);
1137 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1139 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1141 int max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1146 static struct fmt_affix
1147 fmt_affix_clone (const struct fmt_affix *old)
1149 return (struct fmt_affix) {
1150 .s = xstrdup_if_nonnull (old->s),
1151 .width = old->width,
1155 /* Frees data in AFFIX. */
1157 fmt_affix_free (struct fmt_affix *affix)
1163 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1166 find_cc_separators (const char *cc_string)
1168 /* Count commas and periods. There must be exactly three of
1169 one or the other, except that an apostrophe escapes a
1170 following comma or period. */
1173 for (const char *p = cc_string; *p; p++)
1178 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1181 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1186 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1187 delimited by GROUPING. Returns the first character following the token. */
1188 static struct fmt_affix
1189 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1191 const char *p = *sp;
1192 for (; *p && *p != grouping; p++)
1193 if (*p == '\'' && p[1] == grouping)
1196 size_t length = p - *sp;
1197 char *affix = xmemdup0 (*sp, length);
1198 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1200 *extra_bytes += length - width;
1202 *sp = p + (*p != 0);
1204 return (struct fmt_affix) { .s = affix, .width = width };
1207 struct fmt_number_style *
1208 fmt_number_style_from_string (const char *s)
1210 char grouping = find_cc_separators (s);
1214 size_t extra_bytes = 0;
1215 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1216 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1217 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1218 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1220 struct fmt_number_style *style = xmalloc (sizeof *style);
1221 *style = (struct fmt_number_style) {
1222 .neg_prefix = neg_prefix,
1225 .neg_suffix = neg_suffix,
1226 .decimal = grouping == '.' ? ',' : '.',
1227 .grouping = grouping,
1228 .extra_bytes = extra_bytes,
1234 format_cc (struct string *out, const char *in, char grouping)
1239 if (c == grouping || c == '\'')
1240 ds_put_byte (out, '\'');
1242 ds_put_byte (out, '"');
1243 ds_put_byte (out, c);
1248 fmt_number_style_to_string (const struct fmt_number_style *cc)
1250 struct string out = DS_EMPTY_INITIALIZER;
1251 format_cc (&out, cc->neg_prefix.s, cc->grouping);
1252 ds_put_byte (&out, cc->grouping);
1253 format_cc (&out, cc->prefix.s, cc->grouping);
1254 ds_put_byte (&out, cc->grouping);
1255 format_cc (&out, cc->suffix.s, cc->grouping);
1256 ds_put_byte (&out, cc->grouping);
1257 format_cc (&out, cc->neg_suffix.s, cc->grouping);
1258 return ds_steal_cstr (&out);
1261 struct fmt_number_style *
1262 fmt_number_style_clone (const struct fmt_number_style *old)
1266 struct fmt_number_style *new = xmalloc (sizeof *new);
1267 *new = (struct fmt_number_style) {
1268 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1269 .prefix = fmt_affix_clone (&old->prefix),
1270 .suffix = fmt_affix_clone (&old->suffix),
1271 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1272 .decimal = old->decimal,
1273 .grouping = old->grouping,
1274 .extra_bytes = old->extra_bytes,
1282 /* Destroys a struct fmt_number_style. */
1284 fmt_number_style_destroy (struct fmt_number_style *style)
1288 fmt_affix_free (&style->neg_prefix);
1289 fmt_affix_free (&style->prefix);
1290 fmt_affix_free (&style->suffix);
1291 fmt_affix_free (&style->neg_suffix);
1296 /* Returns the total width of the standard prefix and suffix for STYLE, in
1297 display columns (e.g. as returned by u8_strwidth()). */
1299 fmt_affix_width (const struct fmt_number_style *style)
1301 return style->prefix.width + style->suffix.width;
1304 /* Returns the total width of the negative prefix and suffix for STYLE, in
1305 display columns (e.g. as returned by u8_strwidth()). */
1307 fmt_neg_affix_width (const struct fmt_number_style *style)
1309 return style->neg_prefix.width + style->neg_suffix.width;
1312 /* Returns the struct fmt_desc for the given format TYPE. */
1313 static const struct fmt_desc *
1314 get_fmt_desc (enum fmt_type type)
1316 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1318 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1319 {#NAME, IMIN, OMIN, IO, CATEGORY},
1320 #include "format.def"
1323 assert (is_fmt_type (type));
1324 return &formats[type];
1327 const struct fmt_spec F_8_0 = { .type = FMT_F, .w = 8, .d = 0 };
1328 const struct fmt_spec F_8_2 = { .type = FMT_F, .w = 8, .d = 2 };
1329 const struct fmt_spec F_4_3 = { .type = FMT_F, .w = 4, .d = 3 };
1330 const struct fmt_spec F_5_1 = { .type = FMT_F, .w = 5, .d = 1 };