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)
342 char str[FMT_STRING_LEN_MAX + 1];
343 int min_w, max_w, max_d;
345 assert (is_fmt_type (spec->type));
346 fmt_to_string (spec, str);
348 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
350 msg (SE, _("Format %s may not be used for input."), str);
354 if (spec->w % fmt_step_width (spec->type))
356 assert (fmt_step_width (spec->type) == 2);
357 if (use == FMT_FOR_INPUT)
358 msg (SE, _("Input format %s specifies width %d, "
359 "but %s requires an even width."),
360 str, spec->w, fmt_name (spec->type));
362 msg (SE, _("Output format %s specifies width %d, "
363 "but %s requires an even width."),
364 str, spec->w, fmt_name (spec->type));
368 min_w = fmt_min_width (spec->type, use);
369 max_w = fmt_max_width (spec->type, use);
370 if (spec->w < min_w || spec->w > max_w)
372 if (use == FMT_FOR_INPUT)
373 msg (SE, _("Input format %s specifies width %d, but "
374 "%s requires a width between %d and %d."),
375 str, spec->w, fmt_name (spec->type), min_w, max_w);
377 msg (SE, _("Output format %s specifies width %d, but "
378 "%s requires a width between %d and %d."),
379 str, spec->w, fmt_name (spec->type), min_w, max_w);
383 max_d = fmt_max_decimals (spec->type, spec->w, use);
384 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
386 if (use == FMT_FOR_INPUT)
387 msg (SE, ngettext ("Input format %s specifies %d decimal place, but "
388 "%s does not allow any decimals.",
389 "Input format %s specifies %d decimal places, but "
390 "%s does not allow any decimals.",
392 str, spec->d, fmt_name (spec->type));
394 msg (SE, ngettext ("Output format %s specifies %d decimal place, but "
395 "%s does not allow any decimals.",
396 "Output format %s specifies %d decimal places, but "
397 "%s does not allow any decimals.",
399 str, spec->d, fmt_name (spec->type));
402 else if (spec->d > max_d)
406 if (use == FMT_FOR_INPUT)
407 msg (SE, ngettext ("Input format %s specifies %d decimal place, "
408 "but the given width allows at most "
410 "Input format %s specifies %d decimal places, "
411 "but the given width allows at most "
414 str, spec->d, max_d);
416 msg (SE, ngettext ("Output format %s specifies %d decimal place, "
417 "but the given width allows at most "
419 "Output format %s specifies %d decimal places, "
420 "but the given width allows at most "
423 str, spec->d, max_d);
427 if (use == FMT_FOR_INPUT)
428 msg (SE, ngettext ("Input format %s specifies %d decimal place, "
429 "but the given width does not allow "
431 "Input format %s specifies %d decimal places, "
432 "but the given width does not allow "
437 msg (SE, ngettext ("Output format %s specifies %d decimal place, "
438 "but the given width does not allow "
440 "Output format %s specifies %d decimal places, "
441 "but the given width does not allow "
452 /* Checks whether SPEC is valid as an input format and returns
453 nonzero if so. Otherwise, emits an error message and returns
456 fmt_check_input (const struct fmt_spec *spec)
458 return fmt_check (spec, FMT_FOR_INPUT);
461 /* Checks whether SPEC is valid as an output format and returns
462 true if so. Otherwise, emits an error message and returns false. */
464 fmt_check_output (const struct fmt_spec *spec)
466 return fmt_check (spec, FMT_FOR_OUTPUT);
469 /* Checks that FORMAT is appropriate for a variable of the given
470 VAR_TYPE and returns true if so. Otherwise returns false and
471 emits an error message. */
473 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
475 assert (val_type_is_valid (var_type));
476 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
478 char str[FMT_STRING_LEN_MAX + 1];
479 msg (SE, _("%s variables are not compatible with %s format %s."),
480 var_type == VAL_STRING ? _("String") : _("Numeric"),
481 var_type == VAL_STRING ? _("numeric") : _("string"),
482 fmt_to_string (format, str));
488 /* Checks that FORMAT is appropriate for a variable of the given
489 WIDTH and returns true if so. Otherwise returns false and
490 emits an error message. */
492 fmt_check_width_compat (const struct fmt_spec *format, int width)
494 if (!fmt_check_type_compat (format, val_type_from_width (width)))
496 if (fmt_var_width (format) != width)
498 char str[FMT_STRING_LEN_MAX + 1];
499 msg (SE, _("String variable with width %d is not compatible with "
501 width, fmt_to_string (format, str));
507 /* Returns the width corresponding to FORMAT. The return value
508 is the width of the `union value's required by FORMAT. */
510 fmt_var_width (const struct fmt_spec *format)
512 return (format->type == FMT_AHEX ? format->w / 2
513 : format->type == FMT_A ? format->w
517 /* Converts F to its string representation (for instance, "F8.2")
518 in BUFFER. Returns BUFFER.
520 If F has decimals, they are included in the output string,
521 even if F's format type does not allow decimals, to allow
522 accurately presenting incorrect formats to the user. */
524 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
526 if (fmt_takes_decimals (f->type) || f->d > 0)
527 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
528 "%s%d.%d", fmt_name (f->type), f->w, f->d);
530 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
531 "%s%d", fmt_name (f->type), f->w);
535 /* Returns true if A and B are identical formats,
538 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
540 return a->type == b->type && a->w == b->w && a->d == b->d;
543 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
544 If nothing needed to be changed the return value is false
547 fmt_resize (struct fmt_spec *fmt, int width)
549 if ((width > 0) != fmt_is_string (fmt->type))
551 /* Changed from numeric to string or vice versa. Set to
552 default format for new width. */
553 *fmt = fmt_default_for_width (width);
557 /* Changed width of string. Preserve format type, adjust
559 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
569 /* Adjusts FMT's width and decimal places to be valid for USE. */
571 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
573 /* Clamp width to those allowed by format. */
574 fmt_clamp_width (fmt, use);
576 /* If FMT has more decimal places than allowed, attempt to increase FMT's
577 width until that number of decimal places can be achieved. */
578 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
579 && fmt_takes_decimals (fmt->type))
581 int max_w = fmt_max_width (fmt->type, use);
582 for (; fmt->w < max_w; fmt->w++)
583 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
587 /* Clamp decimals to those allowed by format and width. */
588 fmt_clamp_decimals (fmt, use);
591 /* Adjusts FMT's width and decimal places to be valid for an
594 fmt_fix_input (struct fmt_spec *fmt)
596 fmt_fix (fmt, FMT_FOR_INPUT);
599 /* Adjusts FMT's width and decimal places to be valid for an
602 fmt_fix_output (struct fmt_spec *fmt)
604 fmt_fix (fmt, FMT_FOR_OUTPUT);
607 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
608 reduces its decimal places as necessary (if necessary) for that width. */
610 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
613 fmt_clamp_width (fmt, use);
614 fmt_clamp_decimals (fmt, use);
617 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
618 places allowed by FMT's type) and increases its width as necessary (if
619 necessary) for that number of decimal places. */
621 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
627 /* Describes a display format. */
631 int min_input_width, min_output_width;
633 enum fmt_category category;
636 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
638 /* Returns the name of the given format TYPE. */
640 fmt_name (enum fmt_type type)
642 return get_fmt_desc (type)->name;
645 /* Tries to parse NAME as a format type.
646 If successful, stores the type in *TYPE and returns true.
647 On failure, returns false. */
649 fmt_from_name (const char *name, enum fmt_type *type)
653 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
654 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
662 /* Returns true if TYPE accepts decimal places,
665 fmt_takes_decimals (enum fmt_type type)
667 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
670 /* Returns the minimum width of the given format TYPE for the given USE. */
672 fmt_min_width (enum fmt_type type, enum fmt_use use)
674 return (use == FMT_FOR_INPUT
675 ? fmt_min_input_width (type)
676 : fmt_min_output_width (type));
679 /* Returns the maximum width of the given format TYPE,
680 for input if FOR_INPUT is true,
681 for output otherwise. */
683 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
685 /* Maximum width is actually invariant of whether the format is
686 for input or output, so FOR_INPUT is unused. */
687 assert (is_fmt_type (type));
705 return 2 * MAX_STRING;
712 /* Returns the maximum number of decimal places allowed for the
713 given format TYPE with a width of WIDTH places, for the given USE. */
715 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
724 max_d = use == FMT_FOR_INPUT ? width : width - 1;
729 max_d = use == FMT_FOR_INPUT ? width : width - 2;
733 max_d = use == FMT_FOR_INPUT ? width : width - 7;
741 assert (use == FMT_FOR_OUTPUT);
751 max_d = width * 2 - 1;
760 max_d = max_digits_for_bytes (width);
821 /* Returns the minimum acceptable width for an input field
822 formatted with the given TYPE. */
824 fmt_min_input_width (enum fmt_type type)
826 return get_fmt_desc (type)->min_input_width;
829 /* Returns the maximum acceptable width for an input field
830 formatted with the given TYPE. */
832 fmt_max_input_width (enum fmt_type type)
834 return fmt_max_width (type, FMT_FOR_INPUT);
837 /* Returns the maximum number of decimal places allowed in an
838 input field of the given TYPE and WIDTH. */
840 fmt_max_input_decimals (enum fmt_type type, int width)
842 assert (valid_width (type, width, true));
843 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
846 /* Returns the minimum acceptable width for an output field
847 formatted with the given TYPE. */
849 fmt_min_output_width (enum fmt_type type)
851 return get_fmt_desc (type)->min_output_width;
854 /* Returns the maximum acceptable width for an output field
855 formatted with the given TYPE. */
857 fmt_max_output_width (enum fmt_type type)
859 return fmt_max_width (type, FMT_FOR_OUTPUT);
862 /* Returns the maximum number of decimal places allowed in an
863 output field of the given TYPE and WIDTH. */
865 fmt_max_output_decimals (enum fmt_type type, int width)
867 assert (valid_width (type, width, false));
868 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
871 /* Returns the width step for a field formatted with the given
872 TYPE. Field width must be a multiple of the width step. */
874 fmt_step_width (enum fmt_type type)
876 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
880 /* Returns true if TYPE is used for string fields,
881 false if it is used for numeric fields. */
883 fmt_is_string (enum fmt_type type)
885 return fmt_get_category (type) == FMT_CAT_STRING;
888 /* Returns true if TYPE is used for numeric fields,
889 false if it is used for string fields. */
891 fmt_is_numeric (enum fmt_type type)
893 return !fmt_is_string (type);
896 /* Returns the format TYPE's category.
897 Each format type is in exactly one category,
898 and each category's value is bitwise disjoint from every other
899 category. Thus, the return value may be tested for equality
900 or compared bitwise against a mask of FMT_CAT_* values. */
902 fmt_get_category (enum fmt_type type)
904 return get_fmt_desc (type)->category;
907 /* Returns the output format selected by default when TYPE is
908 used as an input format. */
910 fmt_input_to_output (enum fmt_type type)
912 switch (fmt_get_category (type))
919 case FMT_CAT_HEXADECIMAL:
927 /* Returns the SPSS format type corresponding to the given PSPP
930 fmt_to_io (enum fmt_type type)
932 return get_fmt_desc (type)->io;
935 /* Determines the PSPP format corresponding to the given SPSS
936 format type. If successful, sets *FMT_TYPE to the PSPP format
937 and returns true. On failure, return false. */
939 fmt_from_io (int io, enum fmt_type *fmt_type)
943 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
945 *fmt_type = FMT_##NAME; \
947 #include "format.def"
953 /* Translate U32, which is in the form found in SAV and SPV files, into a
954 format specification, and stores the new specification in *F.
956 If LOOSE is false, checks that the format specification is appropriate as an
957 output format for a variable with the given WIDTH and reports an error if
958 not. If LOOSE is true, instead adjusts the format's width and decimals as
959 necessary to be suitable.
961 Return true if successful, false if there was an error.. */
963 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
965 uint8_t raw_type = u32 >> 16;
966 uint8_t w = u32 >> 8;
972 bool ok = fmt_from_io (raw_type, &f->type);
978 ok = fmt_check_output (f);
981 ok = fmt_check_width_compat (f, width);
987 /* Returns true if TYPE may be used as an input format,
990 fmt_usable_for_input (enum fmt_type type)
992 assert (is_fmt_type (type));
993 return fmt_get_category (type) != FMT_CAT_CUSTOM;
996 /* For time and date formats, returns a template used for input and output in a
997 field of the given WIDTH.
999 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
1000 is, whether the returned string contains "yy" or "yyyy", and whether seconds
1001 are include, that is, whether the returned string contains ":SS". A caller
1002 that doesn't care whether the returned string contains "yy" or "yyyy" or
1003 ":SS" can just specify 0 to omit them. */
1005 fmt_date_template (enum fmt_type type, int width)
1007 const char *s1, *s2;
1052 s1 = "dd-mmm-yyyy HH:MM";
1053 s2 = "dd-mmm-yyyy HH:MM:SS";
1057 s1 = "yyyy-mm-dd HH:MM";
1058 s2 = "yyyy-mm-dd HH:MM:SS";
1080 return width >= strlen (s2) ? s2 : s1;
1083 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1085 fmt_gui_name (enum fmt_type type)
1090 return _("Numeric");
1099 return _("Scientific");
1132 return fmt_name (type);
1136 /* Returns true if TYPE is a valid format type,
1139 is_fmt_type (enum fmt_type type)
1141 return type < FMT_NUMBER_OF_FORMATS;
1144 /* Returns true if WIDTH is a valid width for the given format
1145 TYPE, for the given USE. */
1147 valid_width (enum fmt_type type, int width, enum fmt_use use)
1149 return (width >= fmt_min_width (type, use)
1150 && width <= fmt_max_width (type, use));
1153 /* Returns the maximum number of decimal digits in an unsigned
1154 binary number that is BYTES bytes long. */
1156 max_digits_for_bytes (int bytes)
1158 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1159 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1160 return map[bytes - 1];
1163 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1165 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1170 min_w = fmt_min_width (fmt->type, use);
1171 max_w = fmt_max_width (fmt->type, use);
1174 else if (fmt->w > max_w)
1177 /* Round width to step. */
1178 step = fmt_step_width (fmt->type);
1179 fmt->w = ROUND_DOWN (fmt->w, step);
1182 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1184 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1186 int max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1191 static struct fmt_affix
1192 fmt_affix_clone (const struct fmt_affix *old)
1194 return (struct fmt_affix) {
1195 .s = xstrdup_if_nonnull (old->s),
1196 .width = old->width,
1200 /* Frees data in AFFIX. */
1202 fmt_affix_free (struct fmt_affix *affix)
1208 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1211 find_cc_separators (const char *cc_string)
1213 /* Count commas and periods. There must be exactly three of
1214 one or the other, except that an apostrophe escapes a
1215 following comma or period. */
1218 for (const char *p = cc_string; *p; p++)
1223 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1226 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1231 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1232 delimited by GROUPING. Returns the first character following the token. */
1233 static struct fmt_affix
1234 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1236 const char *p = *sp;
1237 for (; *p && *p != grouping; p++)
1238 if (*p == '\'' && p[1] == grouping)
1241 size_t length = p - *sp;
1242 char *affix = xmemdup0 (*sp, length);
1243 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1245 *extra_bytes += length - width;
1247 *sp = p + (*p != 0);
1249 return (struct fmt_affix) { .s = affix, .width = width };
1252 struct fmt_number_style *
1253 fmt_number_style_from_string (const char *s)
1255 char grouping = find_cc_separators (s);
1259 size_t extra_bytes = 0;
1260 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1261 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1262 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1263 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1265 struct fmt_number_style *style = xmalloc (sizeof *style);
1266 *style = (struct fmt_number_style) {
1267 .neg_prefix = neg_prefix,
1270 .neg_suffix = neg_suffix,
1271 .decimal = grouping == '.' ? ',' : '.',
1272 .grouping = grouping,
1273 .extra_bytes = extra_bytes,
1279 format_cc (struct string *out, const char *in, char grouping)
1284 if (c == grouping || c == '\'')
1285 ds_put_byte (out, '\'');
1287 ds_put_byte (out, '"');
1288 ds_put_byte (out, c);
1293 fmt_number_style_to_string (const struct fmt_number_style *cc)
1295 struct string out = DS_EMPTY_INITIALIZER;
1296 format_cc (&out, cc->neg_prefix.s, cc->grouping);
1297 ds_put_byte (&out, cc->grouping);
1298 format_cc (&out, cc->prefix.s, cc->grouping);
1299 ds_put_byte (&out, cc->grouping);
1300 format_cc (&out, cc->suffix.s, cc->grouping);
1301 ds_put_byte (&out, cc->grouping);
1302 format_cc (&out, cc->neg_suffix.s, cc->grouping);
1303 return ds_steal_cstr (&out);
1306 struct fmt_number_style *
1307 fmt_number_style_clone (const struct fmt_number_style *old)
1311 struct fmt_number_style *new = xmalloc (sizeof *new);
1312 *new = (struct fmt_number_style) {
1313 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1314 .prefix = fmt_affix_clone (&old->prefix),
1315 .suffix = fmt_affix_clone (&old->suffix),
1316 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1317 .decimal = old->decimal,
1318 .grouping = old->grouping,
1319 .extra_bytes = old->extra_bytes,
1327 /* Destroys a struct fmt_number_style. */
1329 fmt_number_style_destroy (struct fmt_number_style *style)
1333 fmt_affix_free (&style->neg_prefix);
1334 fmt_affix_free (&style->prefix);
1335 fmt_affix_free (&style->suffix);
1336 fmt_affix_free (&style->neg_suffix);
1341 /* Returns the total width of the standard prefix and suffix for STYLE, in
1342 display columns (e.g. as returned by u8_strwidth()). */
1344 fmt_affix_width (const struct fmt_number_style *style)
1346 return style->prefix.width + style->suffix.width;
1349 /* Returns the total width of the negative prefix and suffix for STYLE, in
1350 display columns (e.g. as returned by u8_strwidth()). */
1352 fmt_neg_affix_width (const struct fmt_number_style *style)
1354 return style->neg_prefix.width + style->neg_suffix.width;
1357 /* Returns the struct fmt_desc for the given format TYPE. */
1358 static const struct fmt_desc *
1359 get_fmt_desc (enum fmt_type type)
1361 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1363 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1364 {#NAME, IMIN, OMIN, IO, CATEGORY},
1365 #include "format.def"
1368 assert (is_fmt_type (type));
1369 return &formats[type];
1372 const struct fmt_spec F_8_0 = { .type = FMT_F, .w = 8, .d = 0 };
1373 const struct fmt_spec F_8_2 = { .type = FMT_F, .w = 8, .d = 2 };
1374 const struct fmt_spec F_4_3 = { .type = FMT_F, .w = 4, .d = 3 };
1375 const struct fmt_spec F_5_1 = { .type = FMT_F, .w = 5, .d = 1 };