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 NULL if so. Otherwise,
338 returns a malloc()'d string that describes the error. The caller must
339 eventually free() the string. */
341 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 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
350 return xasprintf (_("Format %s may not be used for input."), str);
352 if (spec->w % fmt_step_width (spec->type))
354 assert (fmt_step_width (spec->type) == 2);
355 return (use == FMT_FOR_INPUT
356 ? xasprintf (_("Input format %s specifies width %d, "
357 "but %s requires an even width."),
358 str, spec->w, fmt_name (spec->type))
359 : xasprintf (_("Output format %s specifies width %d, "
360 "but %s requires an even width."),
361 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)
367 return (use == FMT_FOR_INPUT
368 ? xasprintf (_("Input format %s specifies width %d, but "
369 "%s requires a width between %d and %d."),
370 str, spec->w, fmt_name (spec->type), min_w, max_w)
371 : xasprintf (_("Output format %s specifies width %d, but "
372 "%s requires a width between %d and %d."),
373 str, spec->w, fmt_name (spec->type), min_w, max_w));
375 max_d = fmt_max_decimals (spec->type, spec->w, use);
376 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
377 return (use == FMT_FOR_INPUT
378 ? xasprintf (ngettext (
379 "Input format %s specifies %d decimal "
380 "place, but %s does not allow any decimals.",
381 "Input format %s specifies %d decimal "
382 "places, but %s does not allow any "
385 str, spec->d, fmt_name (spec->type))
386 : xasprintf (ngettext (
387 "Output format %s specifies %d decimal "
388 "place, but %s does not allow any decimals.",
389 "Output format %s specifies %d decimal places, but "
390 "%s does not allow any decimals.",
392 str, spec->d, fmt_name (spec->type)));
393 else if (spec->d > max_d)
396 return (use == FMT_FOR_INPUT
397 ? xasprintf (ngettext (
398 "Input format %s specifies %d decimal place, "
399 "but the given width allows at most "
401 "Input format %s specifies %d decimal places, "
402 "but the given width allows at most "
406 : xasprintf (ngettext (
407 "Output format %s specifies %d decimal place, "
408 "but the given width allows at most "
410 "Output format %s specifies %d decimal places, "
411 "but the given width allows at most "
414 str, spec->d, max_d));
416 return (use == FMT_FOR_INPUT
417 ? xasprintf (ngettext (
418 "Input format %s specifies %d decimal place, "
419 "but the given width does not allow "
421 "Input format %s specifies %d decimal places, "
422 "but the given width does not allow "
426 : xasprintf (ngettext (
427 "Output format %s specifies %d decimal place, "
428 "but the given width does not allow "
430 "Output format %s specifies %d decimal places, "
431 "but the given width does not allow "
441 fmt_emit_and_free_error (char *error)
445 msg (SE, "%s", error);
453 /* Checks whether SPEC is valid for USE and returns nonzero if so. Otherwise,
454 emits an error message for the current source location and returns zero. */
456 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
458 return fmt_emit_and_free_error (fmt_check__ (spec, use));
461 /* Checks whether SPEC is valid as an input format and returns
462 nonzero if so. Otherwise, emits an error message and returns
465 fmt_check_input (const struct fmt_spec *spec)
467 return fmt_check (spec, FMT_FOR_INPUT);
470 /* Checks whether SPEC is valid as an output format and returns
471 true if so. Otherwise, emits an error message and returns false. */
473 fmt_check_output (const struct fmt_spec *spec)
475 return fmt_check (spec, FMT_FOR_OUTPUT);
478 /* Checks that FORMAT is appropriate for a variable of the given VAR_TYPE and
479 returns NULL if so. Otherwise returns a malloc()'d error message that the
480 calelr must eventually free(). */
482 fmt_check_type_compat__ (const struct fmt_spec *format, enum val_type var_type)
484 assert (val_type_is_valid (var_type));
485 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
487 char str[FMT_STRING_LEN_MAX + 1];
488 return xasprintf (_("%s variables are not compatible with %s format %s."),
489 var_type == VAL_STRING ? _("String") : _("Numeric"),
490 var_type == VAL_STRING ? _("numeric") : _("string"),
491 fmt_to_string (format, str));
496 /* Checks that FORMAT is appropriate for a variable of the given
497 VAR_TYPE and returns true if so. Otherwise returns false and
498 emits an error message. */
500 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
502 return fmt_emit_and_free_error (fmt_check_type_compat__ (format, var_type));
505 /* Checks that FORMAT is appropriate for a variable of the given WIDTH and
506 returns NULL if so. Otherwise returns a malloc()'d error message that the
507 calelr must eventually free(). */
509 fmt_check_width_compat__ (const struct fmt_spec *format, int width)
511 char *error = fmt_check_type_compat__ (format, val_type_from_width (width));
515 if (fmt_var_width (format) != width)
517 char str[FMT_STRING_LEN_MAX + 1];
518 return xasprintf (_("String variable with width %d is not compatible "
520 width, fmt_to_string (format, str));
526 /* Checks that FORMAT is appropriate for a variable of the given
527 WIDTH and returns true if so. Otherwise returns false and
528 emits an error message. */
530 fmt_check_width_compat (const struct fmt_spec *format, int width)
532 return fmt_emit_and_free_error (fmt_check_width_compat__ (format, width));
535 /* Returns the width corresponding to FORMAT. The return value
536 is the width of the `union value's required by FORMAT. */
538 fmt_var_width (const struct fmt_spec *format)
540 return (format->type == FMT_AHEX ? format->w / 2
541 : format->type == FMT_A ? format->w
545 /* Converts F to its string representation (for instance, "F8.2")
546 in BUFFER. Returns BUFFER.
548 If F has decimals, they are included in the output string,
549 even if F's format type does not allow decimals, to allow
550 accurately presenting incorrect formats to the user. */
552 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
554 if (fmt_takes_decimals (f->type) || f->d > 0)
555 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
556 "%s%d.%d", fmt_name (f->type), f->w, f->d);
558 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
559 "%s%d", fmt_name (f->type), f->w);
563 /* Returns true if A and B are identical formats,
566 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
568 return a->type == b->type && a->w == b->w && a->d == b->d;
571 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
572 If nothing needed to be changed the return value is false
575 fmt_resize (struct fmt_spec *fmt, int width)
577 if ((width > 0) != fmt_is_string (fmt->type))
579 /* Changed from numeric to string or vice versa. Set to
580 default format for new width. */
581 *fmt = fmt_default_for_width (width);
585 /* Changed width of string. Preserve format type, adjust
587 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
597 /* Adjusts FMT's width and decimal places to be valid for USE. */
599 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
601 /* Clamp width to those allowed by format. */
602 fmt_clamp_width (fmt, use);
604 /* If FMT has more decimal places than allowed, attempt to increase FMT's
605 width until that number of decimal places can be achieved. */
606 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
607 && fmt_takes_decimals (fmt->type))
609 int max_w = fmt_max_width (fmt->type, use);
610 for (; fmt->w < max_w; fmt->w++)
611 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
615 /* Clamp decimals to those allowed by format and width. */
616 fmt_clamp_decimals (fmt, use);
619 /* Adjusts FMT's width and decimal places to be valid for an
622 fmt_fix_input (struct fmt_spec *fmt)
624 fmt_fix (fmt, FMT_FOR_INPUT);
627 /* Adjusts FMT's width and decimal places to be valid for an
630 fmt_fix_output (struct fmt_spec *fmt)
632 fmt_fix (fmt, FMT_FOR_OUTPUT);
635 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
636 reduces its decimal places as necessary (if necessary) for that width. */
638 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
641 fmt_clamp_width (fmt, use);
642 fmt_clamp_decimals (fmt, use);
645 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
646 places allowed by FMT's type) and increases its width as necessary (if
647 necessary) for that number of decimal places. */
649 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
655 /* Describes a display format. */
659 int min_input_width, min_output_width;
661 enum fmt_category category;
664 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
666 /* Returns the name of the given format TYPE. */
668 fmt_name (enum fmt_type type)
670 return get_fmt_desc (type)->name;
673 /* Tries to parse NAME as a format type.
674 If successful, stores the type in *TYPE and returns true.
675 On failure, returns false. */
677 fmt_from_name (const char *name, enum fmt_type *type)
681 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
682 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
690 /* Returns true if TYPE accepts decimal places,
693 fmt_takes_decimals (enum fmt_type type)
695 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
698 /* Returns the minimum width of the given format TYPE for the given USE. */
700 fmt_min_width (enum fmt_type type, enum fmt_use use)
702 return (use == FMT_FOR_INPUT
703 ? fmt_min_input_width (type)
704 : fmt_min_output_width (type));
707 /* Returns the maximum width of the given format TYPE,
708 for input if FOR_INPUT is true,
709 for output otherwise. */
711 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
713 /* Maximum width is actually invariant of whether the format is
714 for input or output, so FOR_INPUT is unused. */
715 assert (is_fmt_type (type));
733 return 2 * MAX_STRING;
740 /* Returns the maximum number of decimal places allowed for the
741 given format TYPE with a width of WIDTH places, for the given USE. */
743 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
752 max_d = use == FMT_FOR_INPUT ? width : width - 1;
757 max_d = use == FMT_FOR_INPUT ? width : width - 2;
761 max_d = use == FMT_FOR_INPUT ? width : width - 7;
769 assert (use == FMT_FOR_OUTPUT);
779 max_d = width * 2 - 1;
788 max_d = max_digits_for_bytes (width);
849 /* Returns the minimum acceptable width for an input field
850 formatted with the given TYPE. */
852 fmt_min_input_width (enum fmt_type type)
854 return get_fmt_desc (type)->min_input_width;
857 /* Returns the maximum acceptable width for an input field
858 formatted with the given TYPE. */
860 fmt_max_input_width (enum fmt_type type)
862 return fmt_max_width (type, FMT_FOR_INPUT);
865 /* Returns the maximum number of decimal places allowed in an
866 input field of the given TYPE and WIDTH. */
868 fmt_max_input_decimals (enum fmt_type type, int width)
870 assert (valid_width (type, width, true));
871 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
874 /* Returns the minimum acceptable width for an output field
875 formatted with the given TYPE. */
877 fmt_min_output_width (enum fmt_type type)
879 return get_fmt_desc (type)->min_output_width;
882 /* Returns the maximum acceptable width for an output field
883 formatted with the given TYPE. */
885 fmt_max_output_width (enum fmt_type type)
887 return fmt_max_width (type, FMT_FOR_OUTPUT);
890 /* Returns the maximum number of decimal places allowed in an
891 output field of the given TYPE and WIDTH. */
893 fmt_max_output_decimals (enum fmt_type type, int width)
895 assert (valid_width (type, width, false));
896 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
899 /* Returns the width step for a field formatted with the given
900 TYPE. Field width must be a multiple of the width step. */
902 fmt_step_width (enum fmt_type type)
904 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
908 /* Returns true if TYPE is used for string fields,
909 false if it is used for numeric fields. */
911 fmt_is_string (enum fmt_type type)
913 return fmt_get_category (type) == FMT_CAT_STRING;
916 /* Returns true if TYPE is used for numeric fields,
917 false if it is used for string fields. */
919 fmt_is_numeric (enum fmt_type type)
921 return !fmt_is_string (type);
924 /* Returns the format TYPE's category.
925 Each format type is in exactly one category,
926 and each category's value is bitwise disjoint from every other
927 category. Thus, the return value may be tested for equality
928 or compared bitwise against a mask of FMT_CAT_* values. */
930 fmt_get_category (enum fmt_type type)
932 return get_fmt_desc (type)->category;
935 /* Returns the output format selected by default when TYPE is
936 used as an input format. */
938 fmt_input_to_output (enum fmt_type type)
940 switch (fmt_get_category (type))
947 case FMT_CAT_HEXADECIMAL:
955 /* Returns the SPSS format type corresponding to the given PSPP
958 fmt_to_io (enum fmt_type type)
960 return get_fmt_desc (type)->io;
963 /* Determines the PSPP format corresponding to the given SPSS
964 format type. If successful, sets *FMT_TYPE to the PSPP format
965 and returns true. On failure, return false. */
967 fmt_from_io (int io, enum fmt_type *fmt_type)
971 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
973 *fmt_type = FMT_##NAME; \
975 #include "format.def"
981 /* Translate U32, which is in the form found in SAV and SPV files, into a
982 format specification, and stores the new specification in *F.
984 If LOOSE is false, checks that the format specification is appropriate as an
985 output format for a variable with the given WIDTH and reports an error if
986 not. If LOOSE is true, instead adjusts the format's width and decimals as
987 necessary to be suitable.
989 Return true if successful, false if there was an error.. */
991 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
993 uint8_t raw_type = u32 >> 16;
994 uint8_t w = u32 >> 8;
1000 bool ok = fmt_from_io (raw_type, &f->type);
1006 ok = fmt_check_output (f);
1009 ok = fmt_check_width_compat (f, width);
1015 /* Returns true if TYPE may be used as an input format,
1018 fmt_usable_for_input (enum fmt_type type)
1020 assert (is_fmt_type (type));
1021 return fmt_get_category (type) != FMT_CAT_CUSTOM;
1024 /* For time and date formats, returns a template used for input and output in a
1025 field of the given WIDTH.
1027 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
1028 is, whether the returned string contains "yy" or "yyyy", and whether seconds
1029 are include, that is, whether the returned string contains ":SS". A caller
1030 that doesn't care whether the returned string contains "yy" or "yyyy" or
1031 ":SS" can just specify 0 to omit them. */
1033 fmt_date_template (enum fmt_type type, int width)
1035 const char *s1, *s2;
1080 s1 = "dd-mmm-yyyy HH:MM";
1081 s2 = "dd-mmm-yyyy HH:MM:SS";
1085 s1 = "yyyy-mm-dd HH:MM";
1086 s2 = "yyyy-mm-dd HH:MM:SS";
1108 return width >= strlen (s2) ? s2 : s1;
1111 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1113 fmt_gui_name (enum fmt_type type)
1118 return _("Numeric");
1127 return _("Scientific");
1160 return fmt_name (type);
1164 /* Returns true if TYPE is a valid format type,
1167 is_fmt_type (enum fmt_type type)
1169 return type < FMT_NUMBER_OF_FORMATS;
1172 /* Returns true if WIDTH is a valid width for the given format
1173 TYPE, for the given USE. */
1175 valid_width (enum fmt_type type, int width, enum fmt_use use)
1177 return (width >= fmt_min_width (type, use)
1178 && width <= fmt_max_width (type, use));
1181 /* Returns the maximum number of decimal digits in an unsigned
1182 binary number that is BYTES bytes long. */
1184 max_digits_for_bytes (int bytes)
1186 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1187 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1188 return map[bytes - 1];
1191 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1193 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1198 min_w = fmt_min_width (fmt->type, use);
1199 max_w = fmt_max_width (fmt->type, use);
1202 else if (fmt->w > max_w)
1205 /* Round width to step. */
1206 step = fmt_step_width (fmt->type);
1207 fmt->w = ROUND_DOWN (fmt->w, step);
1210 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1212 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1214 int max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1219 static struct fmt_affix
1220 fmt_affix_clone (const struct fmt_affix *old)
1222 return (struct fmt_affix) {
1223 .s = xstrdup_if_nonnull (old->s),
1224 .width = old->width,
1228 /* Frees data in AFFIX. */
1230 fmt_affix_free (struct fmt_affix *affix)
1236 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1239 find_cc_separators (const char *cc_string)
1241 /* Count commas and periods. There must be exactly three of
1242 one or the other, except that an apostrophe escapes a
1243 following comma or period. */
1246 for (const char *p = cc_string; *p; p++)
1251 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1254 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1259 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1260 delimited by GROUPING. Returns the first character following the token. */
1261 static struct fmt_affix
1262 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1264 const char *p = *sp;
1265 for (; *p && *p != grouping; p++)
1266 if (*p == '\'' && p[1] == grouping)
1269 size_t length = p - *sp;
1270 char *affix = xmemdup0 (*sp, length);
1271 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1273 *extra_bytes += length - width;
1275 *sp = p + (*p != 0);
1277 return (struct fmt_affix) { .s = affix, .width = width };
1280 struct fmt_number_style *
1281 fmt_number_style_from_string (const char *s)
1283 char grouping = find_cc_separators (s);
1287 size_t extra_bytes = 0;
1288 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1289 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1290 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1291 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1293 struct fmt_number_style *style = xmalloc (sizeof *style);
1294 *style = (struct fmt_number_style) {
1295 .neg_prefix = neg_prefix,
1298 .neg_suffix = neg_suffix,
1299 .decimal = grouping == '.' ? ',' : '.',
1300 .grouping = grouping,
1301 .extra_bytes = extra_bytes,
1307 format_cc (struct string *out, const char *in, char grouping)
1312 if (c == grouping || c == '\'')
1313 ds_put_byte (out, '\'');
1315 ds_put_byte (out, '"');
1316 ds_put_byte (out, c);
1321 fmt_number_style_to_string (const struct fmt_number_style *cc)
1323 struct string out = DS_EMPTY_INITIALIZER;
1324 format_cc (&out, cc->neg_prefix.s, cc->grouping);
1325 ds_put_byte (&out, cc->grouping);
1326 format_cc (&out, cc->prefix.s, cc->grouping);
1327 ds_put_byte (&out, cc->grouping);
1328 format_cc (&out, cc->suffix.s, cc->grouping);
1329 ds_put_byte (&out, cc->grouping);
1330 format_cc (&out, cc->neg_suffix.s, cc->grouping);
1331 return ds_steal_cstr (&out);
1334 struct fmt_number_style *
1335 fmt_number_style_clone (const struct fmt_number_style *old)
1339 struct fmt_number_style *new = xmalloc (sizeof *new);
1340 *new = (struct fmt_number_style) {
1341 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1342 .prefix = fmt_affix_clone (&old->prefix),
1343 .suffix = fmt_affix_clone (&old->suffix),
1344 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1345 .decimal = old->decimal,
1346 .grouping = old->grouping,
1347 .extra_bytes = old->extra_bytes,
1355 /* Destroys a struct fmt_number_style. */
1357 fmt_number_style_destroy (struct fmt_number_style *style)
1361 fmt_affix_free (&style->neg_prefix);
1362 fmt_affix_free (&style->prefix);
1363 fmt_affix_free (&style->suffix);
1364 fmt_affix_free (&style->neg_suffix);
1369 /* Returns the total width of the standard prefix and suffix for STYLE, in
1370 display columns (e.g. as returned by u8_strwidth()). */
1372 fmt_affix_width (const struct fmt_number_style *style)
1374 return style->prefix.width + style->suffix.width;
1377 /* Returns the total width of the negative prefix and suffix for STYLE, in
1378 display columns (e.g. as returned by u8_strwidth()). */
1380 fmt_neg_affix_width (const struct fmt_number_style *style)
1382 return style->neg_prefix.width + style->neg_suffix.width;
1385 /* Returns the struct fmt_desc for the given format TYPE. */
1386 static const struct fmt_desc *
1387 get_fmt_desc (enum fmt_type type)
1389 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1391 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1392 {#NAME, IMIN, OMIN, IO, CATEGORY},
1393 #include "format.def"
1396 assert (is_fmt_type (type));
1397 return &formats[type];
1400 const struct fmt_spec F_8_0 = { .type = FMT_F, .w = 8, .d = 0 };
1401 const struct fmt_spec F_8_2 = { .type = FMT_F, .w = 8, .d = 2 };
1402 const struct fmt_spec F_4_3 = { .type = FMT_F, .w = 4, .d = 3 };
1403 const struct fmt_spec F_5_1 = { .type = FMT_F, .w = 5, .d = 1 };