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)
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,
212 const struct fmt_settings *settings)
214 struct fmt_spec output;
216 assert (fmt_check_input (input));
218 output.type = fmt_input_to_output (input->type);
220 if (output.w > fmt_max_output_width (output.type))
221 output.w = fmt_max_output_width (output.type);
222 else if (output.w < fmt_min_output_width (output.type))
223 output.w = fmt_min_output_width (output.type);
240 const struct fmt_number_style *style =
241 fmt_settings_get_style (settings, input->type);
243 output.w += fmt_affix_width (style);
244 if (style->grouping != 0 && input->w - input->d >= 3)
245 output.w += (input->w - input->d - 1) / 3;
257 output.d = MAX (input->d, 3);
258 output.w = MAX (input->w, output.d + 7);
262 output.w = max_digits_for_bytes (input->w / 2) + 1;
273 output.w = 2 * input->w + (input->d > 0);
278 output.w = max_digits_for_bytes (input->w) + 1;
294 output.w = input->w / 2;
314 output.w = MAX (input->w, input->d + 6);
319 output.w = MAX (input->w, input->d + 20);
326 if (output.w > fmt_max_output_width (output.type))
327 output.w = fmt_max_output_width (output.type);
329 assert (fmt_check_output (&output));
333 /* Returns the default format for the given WIDTH: F8.2 format
334 for a numeric value, A format for a string value. */
336 fmt_default_for_width (int width)
339 ? fmt_for_output (FMT_F, 8, 2)
340 : fmt_for_output (FMT_A, width, 0));
343 /* Checks whether SPEC is valid for USE and returns nonzero if so.
344 Otherwise, emits an error message and returns zero. */
346 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
349 char str[FMT_STRING_LEN_MAX + 1];
350 int min_w, max_w, max_d;
352 assert (is_fmt_type (spec->type));
353 fmt_to_string (spec, str);
355 io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
356 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
358 msg (SE, _("Format %s may not be used for input."), str);
362 if (spec->w % fmt_step_width (spec->type))
364 assert (fmt_step_width (spec->type) == 2);
365 msg (SE, _("%s specifies width %d, but %s requires an even width."),
366 str, spec->w, fmt_name (spec->type));
370 min_w = fmt_min_width (spec->type, use);
371 max_w = fmt_max_width (spec->type, use);
372 if (spec->w < min_w || spec->w > max_w)
374 msg (SE, _("%s %s specifies width %d, but "
375 "%s requires a width between %d and %d."),
376 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
380 max_d = fmt_max_decimals (spec->type, spec->w, use);
381 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
383 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
384 "%s does not allow any decimals.",
385 "%s %s specifies %d decimal places, but "
386 "%s does not allow any decimals.",
388 io_fmt, str, spec->d, fmt_name (spec->type));
391 else if (spec->d > max_d)
394 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
395 "the given width allows at most %d decimals.",
396 "%s %s specifies %d decimal places, but "
397 "the given width allows at most %d decimals.",
399 io_fmt, str, spec->d, max_d);
401 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
402 "the given width does not allow for any decimals.",
403 "%s %s specifies %d decimal places, but "
404 "the given width does not allow for any decimals.",
406 io_fmt, str, spec->d);
413 /* Checks whether SPEC is valid as an input format and returns
414 nonzero if so. Otherwise, emits an error message and returns
417 fmt_check_input (const struct fmt_spec *spec)
419 return fmt_check (spec, FMT_FOR_INPUT);
422 /* Checks whether SPEC is valid as an output format and returns
423 true if so. Otherwise, emits an error message and returns false. */
425 fmt_check_output (const struct fmt_spec *spec)
427 return fmt_check (spec, FMT_FOR_OUTPUT);
430 /* Checks that FORMAT is appropriate for a variable of the given
431 VAR_TYPE and returns true if so. Otherwise returns false and
432 emits an error message. */
434 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
436 assert (val_type_is_valid (var_type));
437 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
439 char str[FMT_STRING_LEN_MAX + 1];
440 msg (SE, _("%s variables are not compatible with %s format %s."),
441 var_type == VAL_STRING ? _("String") : _("Numeric"),
442 var_type == VAL_STRING ? _("numeric") : _("string"),
443 fmt_to_string (format, str));
449 /* Checks that FORMAT is appropriate for a variable of the given
450 WIDTH and returns true if so. Otherwise returns false and
451 emits an error message. */
453 fmt_check_width_compat (const struct fmt_spec *format, int width)
455 if (!fmt_check_type_compat (format, val_type_from_width (width)))
457 if (fmt_var_width (format) != width)
459 char str[FMT_STRING_LEN_MAX + 1];
460 msg (SE, _("String variable with width %d is not compatible with "
462 width, fmt_to_string (format, str));
468 /* Returns the width corresponding to FORMAT. The return value
469 is the width of the `union value's required by FORMAT. */
471 fmt_var_width (const struct fmt_spec *format)
473 return (format->type == FMT_AHEX ? format->w / 2
474 : format->type == FMT_A ? format->w
478 /* Converts F to its string representation (for instance, "F8.2")
479 in BUFFER. Returns BUFFER.
481 If F has decimals, they are included in the output string,
482 even if F's format type does not allow decimals, to allow
483 accurately presenting incorrect formats to the user. */
485 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
487 if (fmt_takes_decimals (f->type) || f->d > 0)
488 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
489 "%s%d.%d", fmt_name (f->type), f->w, f->d);
491 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
492 "%s%d", fmt_name (f->type), f->w);
496 /* Returns true if A and B are identical formats,
499 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
501 return a->type == b->type && a->w == b->w && a->d == b->d;
504 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
505 If nothing needed to be changed the return value is false
508 fmt_resize (struct fmt_spec *fmt, int width)
510 if ((width > 0) != fmt_is_string (fmt->type))
512 /* Changed from numeric to string or vice versa. Set to
513 default format for new width. */
514 *fmt = fmt_default_for_width (width);
518 /* Changed width of string. Preserve format type, adjust
520 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
530 /* Adjusts FMT's width and decimal places to be valid for USE. */
532 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
534 /* Clamp width to those allowed by format. */
535 fmt_clamp_width (fmt, use);
537 /* If FMT has more decimal places than allowed, attempt to increase FMT's
538 width until that number of decimal places can be achieved. */
539 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
540 && fmt_takes_decimals (fmt->type))
542 int max_w = fmt_max_width (fmt->type, use);
543 for (; fmt->w < max_w; fmt->w++)
544 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
548 /* Clamp decimals to those allowed by format and width. */
549 fmt_clamp_decimals (fmt, use);
552 /* Adjusts FMT's width and decimal places to be valid for an
555 fmt_fix_input (struct fmt_spec *fmt)
557 fmt_fix (fmt, FMT_FOR_INPUT);
560 /* Adjusts FMT's width and decimal places to be valid for an
563 fmt_fix_output (struct fmt_spec *fmt)
565 fmt_fix (fmt, FMT_FOR_OUTPUT);
568 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
569 reduces its decimal places as necessary (if necessary) for that width. */
571 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
574 fmt_clamp_width (fmt, use);
575 fmt_clamp_decimals (fmt, use);
578 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
579 places allowed by FMT's type) and increases its width as necessary (if
580 necessary) for that number of decimal places. */
582 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
588 /* Describes a display format. */
592 int min_input_width, min_output_width;
594 enum fmt_category category;
597 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
599 /* Returns the name of the given format TYPE. */
601 fmt_name (enum fmt_type type)
603 return get_fmt_desc (type)->name;
606 /* Tries to parse NAME as a format type.
607 If successful, stores the type in *TYPE and returns true.
608 On failure, returns false. */
610 fmt_from_name (const char *name, enum fmt_type *type)
614 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
615 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
623 /* Returns true if TYPE accepts decimal places,
626 fmt_takes_decimals (enum fmt_type type)
628 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
631 /* Returns the minimum width of the given format TYPE for the given USE. */
633 fmt_min_width (enum fmt_type type, enum fmt_use use)
635 return (use == FMT_FOR_INPUT
636 ? fmt_min_input_width (type)
637 : fmt_min_output_width (type));
640 /* Returns the maximum width of the given format TYPE,
641 for input if FOR_INPUT is true,
642 for output otherwise. */
644 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
646 /* Maximum width is actually invariant of whether the format is
647 for input or output, so FOR_INPUT is unused. */
648 assert (is_fmt_type (type));
666 return 2 * MAX_STRING;
673 /* Returns the maximum number of decimal places allowed for the
674 given format TYPE with a width of WIDTH places, for the given USE. */
676 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
685 max_d = use == FMT_FOR_INPUT ? width : width - 1;
690 max_d = use == FMT_FOR_INPUT ? width : width - 2;
694 max_d = use == FMT_FOR_INPUT ? width : width - 7;
702 assert (use == FMT_FOR_OUTPUT);
712 max_d = width * 2 - 1;
721 max_d = max_digits_for_bytes (width);
782 /* Returns the minimum acceptable width for an input field
783 formatted with the given TYPE. */
785 fmt_min_input_width (enum fmt_type type)
787 return get_fmt_desc (type)->min_input_width;
790 /* Returns the maximum acceptable width for an input field
791 formatted with the given TYPE. */
793 fmt_max_input_width (enum fmt_type type)
795 return fmt_max_width (type, FMT_FOR_INPUT);
798 /* Returns the maximum number of decimal places allowed in an
799 input field of the given TYPE and WIDTH. */
801 fmt_max_input_decimals (enum fmt_type type, int width)
803 assert (valid_width (type, width, true));
804 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
807 /* Returns the minimum acceptable width for an output field
808 formatted with the given TYPE. */
810 fmt_min_output_width (enum fmt_type type)
812 return get_fmt_desc (type)->min_output_width;
815 /* Returns the maximum acceptable width for an output field
816 formatted with the given TYPE. */
818 fmt_max_output_width (enum fmt_type type)
820 return fmt_max_width (type, FMT_FOR_OUTPUT);
823 /* Returns the maximum number of decimal places allowed in an
824 output field of the given TYPE and WIDTH. */
826 fmt_max_output_decimals (enum fmt_type type, int width)
828 assert (valid_width (type, width, false));
829 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
832 /* Returns the width step for a field formatted with the given
833 TYPE. Field width must be a multiple of the width step. */
835 fmt_step_width (enum fmt_type type)
837 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
841 /* Returns true if TYPE is used for string fields,
842 false if it is used for numeric fields. */
844 fmt_is_string (enum fmt_type type)
846 return fmt_get_category (type) == FMT_CAT_STRING;
849 /* Returns true if TYPE is used for numeric fields,
850 false if it is used for string fields. */
852 fmt_is_numeric (enum fmt_type type)
854 return !fmt_is_string (type);
857 /* Returns the format TYPE's category.
858 Each format type is in exactly one category,
859 and each category's value is bitwise disjoint from every other
860 category. Thus, the return value may be tested for equality
861 or compared bitwise against a mask of FMT_CAT_* values. */
863 fmt_get_category (enum fmt_type type)
865 return get_fmt_desc (type)->category;
868 /* Returns the output format selected by default when TYPE is
869 used as an input format. */
871 fmt_input_to_output (enum fmt_type type)
873 switch (fmt_get_category (type))
880 case FMT_CAT_HEXADECIMAL:
888 /* Returns the SPSS format type corresponding to the given PSPP
891 fmt_to_io (enum fmt_type type)
893 return get_fmt_desc (type)->io;
896 /* Determines the PSPP format corresponding to the given SPSS
897 format type. If successful, sets *FMT_TYPE to the PSPP format
898 and returns true. On failure, return false. */
900 fmt_from_io (int io, enum fmt_type *fmt_type)
904 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
906 *fmt_type = FMT_##NAME; \
908 #include "format.def"
914 /* Translate U32, which is in the form found in SAV and SPV files, into a
915 format specification, and stores the new specification in *F.
917 If LOOSE is false, checks that the format specification is appropriate as an
918 output format for a variable with the given WIDTH and reports an error if
919 not. If LOOSE is true, instead adjusts the format's width and decimals as
920 necessary to be suitable.
922 Return true if successful, false if there was an error.. */
924 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
926 uint8_t raw_type = u32 >> 16;
927 uint8_t w = u32 >> 8;
933 bool ok = fmt_from_io (raw_type, &f->type);
939 ok = fmt_check_output (f);
942 ok = fmt_check_width_compat (f, width);
948 /* Returns true if TYPE may be used as an input format,
951 fmt_usable_for_input (enum fmt_type type)
953 assert (is_fmt_type (type));
954 return fmt_get_category (type) != FMT_CAT_CUSTOM;
957 /* For time and date formats, returns a template used for input and output in a
958 field of the given WIDTH.
960 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
961 is, whether the returned string contains "yy" or "yyyy", and whether seconds
962 are include, that is, whether the returned string contains ":SS". A caller
963 that doesn't care whether the returned string contains "yy" or "yyyy" or
964 ":SS" can just specify 0 to omit them. */
966 fmt_date_template (enum fmt_type type, int width)
1013 s1 = "dd-mmm-yyyy HH:MM";
1014 s2 = "dd-mmm-yyyy HH:MM:SS";
1018 s1 = "yyyy-mm-dd HH:MM";
1019 s2 = "yyyy-mm-dd HH:MM:SS";
1041 return width >= strlen (s2) ? s2 : s1;
1044 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1046 fmt_gui_name (enum fmt_type type)
1051 return _("Numeric");
1060 return _("Scientific");
1093 return fmt_name (type);
1097 /* Returns true if TYPE is a valid format type,
1100 is_fmt_type (enum fmt_type type)
1102 return type < FMT_NUMBER_OF_FORMATS;
1105 /* Returns true if WIDTH is a valid width for the given format
1106 TYPE, for the given USE. */
1108 valid_width (enum fmt_type type, int width, enum fmt_use use)
1110 return (width >= fmt_min_width (type, use)
1111 && width <= fmt_max_width (type, use));
1114 /* Returns the maximum number of decimal digits in an unsigned
1115 binary number that is BYTES bytes long. */
1117 max_digits_for_bytes (int bytes)
1119 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1120 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1121 return map[bytes - 1];
1124 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1126 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1131 min_w = fmt_min_width (fmt->type, use);
1132 max_w = fmt_max_width (fmt->type, use);
1135 else if (fmt->w > max_w)
1138 /* Round width to step. */
1139 step = fmt_step_width (fmt->type);
1140 fmt->w = ROUND_DOWN (fmt->w, step);
1143 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1145 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1149 max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1152 else if (fmt->d > max_d)
1156 static struct fmt_affix
1157 fmt_affix_clone (const struct fmt_affix *old)
1159 return (struct fmt_affix) {
1160 .s = xstrdup_if_nonnull (old->s),
1161 .width = old->width,
1165 /* Frees data in AFFIX. */
1167 fmt_affix_free (struct fmt_affix *affix)
1173 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1176 find_cc_separators (const char *cc_string)
1178 /* Count commas and periods. There must be exactly three of
1179 one or the other, except that an apostrophe escapes a
1180 following comma or period. */
1183 for (const char *p = cc_string; *p; p++)
1188 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1191 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1196 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1197 delimited by GROUPING. Returns the first character following the token. */
1198 static struct fmt_affix
1199 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1201 const char *p = *sp;
1202 for (; *p && *p != grouping; p++)
1203 if (*p == '\'' && p[1] == grouping)
1206 size_t length = p - *sp;
1207 char *affix = xmemdup0 (*sp, length);
1208 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1210 *extra_bytes += length - width;
1212 *sp = p + (*p != 0);
1214 return (struct fmt_affix) { .s = affix, .width = width };
1217 struct fmt_number_style *
1218 fmt_number_style_from_string (const char *s)
1220 char grouping = find_cc_separators (s);
1224 size_t extra_bytes = 0;
1225 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1226 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1227 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1228 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1230 struct fmt_number_style *style = xmalloc (sizeof *style);
1231 *style = (struct fmt_number_style) {
1232 .neg_prefix = neg_prefix,
1235 .neg_suffix = neg_suffix,
1236 .decimal = grouping == '.' ? ',' : '.',
1237 .grouping = grouping,
1238 .extra_bytes = extra_bytes,
1244 format_cc (struct string *out, const char *in, char grouping)
1249 if (c == grouping || c == '\'')
1250 ds_put_byte (out, '\'');
1252 ds_put_byte (out, '"');
1253 ds_put_byte (out, c);
1258 fmt_number_style_to_string (const struct fmt_number_style *cc)
1260 struct string out = DS_EMPTY_INITIALIZER;
1261 format_cc (&out, cc->neg_prefix.s, cc->grouping);
1262 ds_put_byte (&out, cc->grouping);
1263 format_cc (&out, cc->prefix.s, cc->grouping);
1264 ds_put_byte (&out, cc->grouping);
1265 format_cc (&out, cc->suffix.s, cc->grouping);
1266 ds_put_byte (&out, cc->grouping);
1267 format_cc (&out, cc->neg_suffix.s, cc->grouping);
1268 return ds_steal_cstr (&out);
1271 struct fmt_number_style *
1272 fmt_number_style_clone (const struct fmt_number_style *old)
1276 struct fmt_number_style *new = xmalloc (sizeof *new);
1277 *new = (struct fmt_number_style) {
1278 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1279 .prefix = fmt_affix_clone (&old->prefix),
1280 .suffix = fmt_affix_clone (&old->suffix),
1281 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1282 .decimal = old->decimal,
1283 .grouping = old->grouping,
1284 .extra_bytes = old->extra_bytes,
1292 /* Destroys a struct fmt_number_style. */
1294 fmt_number_style_destroy (struct fmt_number_style *style)
1298 fmt_affix_free (&style->neg_prefix);
1299 fmt_affix_free (&style->prefix);
1300 fmt_affix_free (&style->suffix);
1301 fmt_affix_free (&style->neg_suffix);
1306 /* Returns the total width of the standard prefix and suffix for STYLE, in
1307 display columns (e.g. as returned by u8_strwidth()). */
1309 fmt_affix_width (const struct fmt_number_style *style)
1311 return style->prefix.width + style->suffix.width;
1314 /* Returns the total width of the negative prefix and suffix for STYLE, in
1315 display columns (e.g. as returned by u8_strwidth()). */
1317 fmt_neg_affix_width (const struct fmt_number_style *style)
1319 return style->neg_prefix.width + style->neg_suffix.width;
1322 /* Returns the struct fmt_desc for the given format TYPE. */
1323 static const struct fmt_desc *
1324 get_fmt_desc (enum fmt_type type)
1326 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1328 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1329 {#NAME, IMIN, OMIN, IO, CATEGORY},
1330 #include "format.def"
1333 assert (is_fmt_type (type));
1334 return &formats[type];
1337 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};
1338 const struct fmt_spec F_8_2 = {FMT_F, 8, 2};
1339 const struct fmt_spec F_4_3 = {FMT_F, 4, 3};
1340 const struct fmt_spec F_5_1 = {FMT_F, 5, 1};