1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2010, 2011 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/>. */
25 #include "data/identifier.h"
26 #include "data/settings.h"
27 #include "data/value.h"
28 #include "data/variable.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/cast.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/message.h"
33 #include "libpspp/misc.h"
34 #include "libpspp/str.h"
36 #include "gl/minmax.h"
37 #include "gl/xalloc.h"
40 #define _(msgid) gettext (msgid)
44 struct fmt_number_style styles[FMT_NUMBER_OF_FORMATS];
47 bool is_fmt_type (enum fmt_type);
49 static bool valid_width (enum fmt_type, int width, enum fmt_use);
51 static int max_digits_for_bytes (int bytes);
52 static void fmt_clamp_width (struct fmt_spec *, enum fmt_use);
53 static void fmt_clamp_decimals (struct fmt_spec *, enum fmt_use);
55 static void fmt_affix_set (struct fmt_affix *, const char *);
56 static void fmt_affix_free (struct fmt_affix *);
58 static void fmt_number_style_init (struct fmt_number_style *);
59 static void fmt_number_style_clone (struct fmt_number_style *,
60 const struct fmt_number_style *);
61 static void fmt_number_style_destroy (struct fmt_number_style *);
63 /* Creates and returns a new struct fmt_settings with default format styles. */
65 fmt_settings_create (void)
67 struct fmt_settings *settings;
70 settings = xzalloc (sizeof *settings);
71 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
72 fmt_number_style_init (&settings->styles[t]);
73 fmt_settings_set_decimal (settings, '.');
78 /* Destroys SETTINGS. */
80 fmt_settings_destroy (struct fmt_settings *settings)
86 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
87 fmt_number_style_destroy (&settings->styles[t]);
89 free (settings->styles);
93 /* Returns a copy of SETTINGS. */
95 fmt_settings_clone (const struct fmt_settings *old)
97 struct fmt_settings *new;
100 new = xmalloc (sizeof *new);
101 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
102 fmt_number_style_clone (&new->styles[t], &old->styles[t]);
107 /* Returns the number formatting style associated with the given
109 const struct fmt_number_style *
110 fmt_settings_get_style (const struct fmt_settings *settings,
113 assert (is_fmt_type (type));
114 return &settings->styles[type];
117 /* Sets the number style for TYPE to have the given DECIMAL and GROUPING
118 characters, negative prefix NEG_PREFIX, prefix PREFIX, suffix SUFFIX, and
119 negative suffix NEG_SUFFIX. All of the strings are UTF-8 encoded. */
121 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
122 char decimal, char grouping,
123 const char *neg_prefix, const char *prefix,
124 const char *suffix, const char *neg_suffix)
126 struct fmt_number_style *style = &settings->styles[type];
127 int total_bytes, total_width;
129 assert (grouping == '.' || grouping == ',' || grouping == 0);
130 assert (decimal == '.' || decimal == ',');
131 assert (decimal != grouping);
133 fmt_number_style_destroy (style);
135 fmt_affix_set (&style->neg_prefix, neg_prefix);
136 fmt_affix_set (&style->prefix, prefix);
137 fmt_affix_set (&style->suffix, suffix);
138 fmt_affix_set (&style->neg_suffix, neg_suffix);
139 style->decimal = decimal;
140 style->grouping = grouping;
142 total_bytes = (strlen (neg_prefix) + strlen (prefix)
143 + strlen (suffix) + strlen (neg_suffix));
144 total_width = (style->neg_prefix.width + style->prefix.width
145 + style->suffix.width + style->neg_suffix.width);
146 style->extra_bytes = MAX (0, total_bytes - total_width);
149 /* Sets the decimal point character for the settings in S to DECIMAL.
151 This has no effect on custom currency formats. */
153 fmt_settings_set_decimal (struct fmt_settings *s, char decimal)
155 int grouping = decimal == '.' ? ',' : '.';
156 assert (decimal == '.' || decimal == ',');
158 fmt_settings_set_style (s, FMT_F, decimal, 0, "-", "", "", "");
159 fmt_settings_set_style (s, FMT_E, decimal, 0, "-", "", "", "");
160 fmt_settings_set_style (s, FMT_COMMA, decimal, grouping, "-", "", "", "");
161 fmt_settings_set_style (s, FMT_DOT, grouping, decimal, "-", "", "", "");
162 fmt_settings_set_style (s, FMT_DOLLAR, decimal, grouping, "-", "$", "", "");
163 fmt_settings_set_style (s, FMT_PCT, decimal, 0, "-", "", "%", "");
166 /* Returns an input format specification with type TYPE, width W,
169 fmt_for_input (enum fmt_type type, int w, int d)
175 assert (fmt_check_input (&f));
179 /* Returns an output format specification with type TYPE, width
180 W, and D decimals. */
182 fmt_for_output (enum fmt_type type, int w, int d)
188 assert (fmt_check_output (&f));
192 /* Returns the output format specifier corresponding to input
193 format specifier INPUT. */
195 fmt_for_output_from_input (const struct fmt_spec *input)
197 struct fmt_spec output;
199 assert (fmt_check_input (input));
201 output.type = fmt_input_to_output (input->type);
203 if (output.w > fmt_max_output_width (output.type))
204 output.w = fmt_max_output_width (output.type);
205 else if (output.w < fmt_min_output_width (output.type))
206 output.w = fmt_min_output_width (output.type);
223 const struct fmt_number_style *style =
224 settings_get_style (input->type);
226 output.w += fmt_affix_width (style);
227 if (style->grouping != 0 && input->w - input->d >= 3)
228 output.w += (input->w - input->d - 1) / 3;
240 output.d = MAX (input->d, 3);
241 output.w = MAX (input->w, output.d + 7);
245 output.w = max_digits_for_bytes (input->w / 2) + 1;
256 output.w = 2 * input->w + (input->d > 0);
261 output.w = max_digits_for_bytes (input->w) + 1;
277 output.w = input->w / 2;
299 if (output.w > fmt_max_output_width (output.type))
300 output.w = fmt_max_output_width (output.type);
302 assert (fmt_check_output (&output));
306 /* Returns the default format for the given WIDTH: F8.2 format
307 for a numeric value, A format for a string value. */
309 fmt_default_for_width (int width)
312 ? fmt_for_output (FMT_F, 8, 2)
313 : fmt_for_output (FMT_A, width, 0));
316 /* Checks whether SPEC is valid for USE and returns nonzero if so.
317 Otherwise, emits an error message and returns zero. */
319 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
322 char str[FMT_STRING_LEN_MAX + 1];
323 int min_w, max_w, max_d;
325 assert (is_fmt_type (spec->type));
326 fmt_to_string (spec, str);
328 io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
329 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
331 msg (SE, _("Format %s may not be used for input."), str);
335 if (spec->w % fmt_step_width (spec->type))
337 assert (fmt_step_width (spec->type) == 2);
338 msg (SE, _("%s specifies width %d, but %s requires an even width."),
339 str, spec->w, fmt_name (spec->type));
343 min_w = fmt_min_width (spec->type, use);
344 max_w = fmt_max_width (spec->type, use);
345 if (spec->w < min_w || spec->w > max_w)
347 msg (SE, _("%s %s specifies width %d, but "
348 "%s requires a width between %d and %d."),
349 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
353 max_d = fmt_max_decimals (spec->type, spec->w, use);
354 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
356 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
357 "%s does not allow any decimals.",
358 "%s %s specifies %d decimal places, but "
359 "%s does not allow any decimals.",
361 io_fmt, str, spec->d, fmt_name (spec->type));
364 else if (spec->d > max_d)
367 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
368 "the given width allows at most %d decimals.",
369 "%s %s specifies %d decimal places, but "
370 "the given width allows at most %d decimals.",
372 io_fmt, str, spec->d, max_d);
374 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
375 "the given width does not allow for any decimals.",
376 "%s %s specifies %d decimal places, but "
377 "the given width does not allow for any decimals.",
379 io_fmt, str, spec->d);
386 /* Checks whether SPEC is valid as an input format and returns
387 nonzero if so. Otherwise, emits an error message and returns
390 fmt_check_input (const struct fmt_spec *spec)
392 return fmt_check (spec, FMT_FOR_INPUT);
395 /* Checks whether SPEC is valid as an output format and returns
396 true if so. Otherwise, emits an error message and returns false. */
398 fmt_check_output (const struct fmt_spec *spec)
400 return fmt_check (spec, FMT_FOR_OUTPUT);
403 /* Checks that FORMAT is appropriate for a variable of the given
404 VAR_TYPE and returns true if so. Otherwise returns false and
405 emits an error message. */
407 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
409 assert (val_type_is_valid (var_type));
410 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
412 char str[FMT_STRING_LEN_MAX + 1];
413 msg (SE, _("%s variables are not compatible with %s format %s."),
414 var_type == VAL_STRING ? _("String") : _("Numeric"),
415 var_type == VAL_STRING ? _("numeric") : _("string"),
416 fmt_to_string (format, str));
422 /* Checks that FORMAT is appropriate for a variable of the given
423 WIDTH and returns true if so. Otherwise returns false and
424 emits an error message. */
426 fmt_check_width_compat (const struct fmt_spec *format, int width)
428 if (!fmt_check_type_compat (format, val_type_from_width (width)))
430 if (fmt_var_width (format) != width)
432 char str[FMT_STRING_LEN_MAX + 1];
433 msg (SE, _("String variable with width %d is not compatible with "
435 width, fmt_to_string (format, str));
441 /* Returns the width corresponding to FORMAT. The return value
442 is the width of the `union value's required by FORMAT. */
444 fmt_var_width (const struct fmt_spec *format)
446 return (format->type == FMT_AHEX ? format->w / 2
447 : format->type == FMT_A ? format->w
451 /* Converts F to its string representation (for instance, "F8.2")
452 in BUFFER. Returns BUFFER.
454 If F has decimals, they are included in the output string,
455 even if F's format type does not allow decimals, to allow
456 accurately presenting incorrect formats to the user. */
458 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
460 if (fmt_takes_decimals (f->type) || f->d > 0)
461 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
462 "%s%d.%d", fmt_name (f->type), f->w, f->d);
464 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
465 "%s%d", fmt_name (f->type), f->w);
469 /* Returns true if A and B are identical formats,
472 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
474 return a->type == b->type && a->w == b->w && a->d == b->d;
477 /* Adjusts FMT to be valid for a value of the given WIDTH. */
479 fmt_resize (struct fmt_spec *fmt, int width)
481 if ((width > 0) != fmt_is_string (fmt->type))
483 /* Changed from numeric to string or vice versa. Set to
484 default format for new width. */
485 *fmt = fmt_default_for_width (width);
489 /* Changed width of string. Preserve format type, adjust
491 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
499 /* Adjusts FMT's width and decimal places to be valid for USE. */
501 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
503 /* Clamp width to those allowed by format. */
504 fmt_clamp_width (fmt, use);
506 /* If FMT has more decimal places than allowed, attempt to increase FMT's
507 width until that number of decimal places can be achieved. */
508 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
509 && fmt_takes_decimals (fmt->type))
511 int max_w = fmt_max_width (fmt->type, use);
512 for (; fmt->w < max_w; fmt->w++)
513 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
517 /* Clamp decimals to those allowed by format and width. */
518 fmt_clamp_decimals (fmt, use);
521 /* Adjusts FMT's width and decimal places to be valid for an
524 fmt_fix_input (struct fmt_spec *fmt)
526 fmt_fix (fmt, FMT_FOR_INPUT);
529 /* Adjusts FMT's width and decimal places to be valid for an
532 fmt_fix_output (struct fmt_spec *fmt)
534 fmt_fix (fmt, FMT_FOR_OUTPUT);
537 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
538 reduces its decimal places as necessary (if necessary) for that width. */
540 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
543 fmt_clamp_width (fmt, use);
544 fmt_clamp_decimals (fmt, use);
547 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
548 places allowed by FMT's type) and increases its width as necessary (if
549 necessary) for that number of decimal places. */
551 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
557 /* Describes a display format. */
561 int min_input_width, min_output_width;
563 enum fmt_category category;
566 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
568 /* Returns the name of the given format TYPE. */
570 fmt_name (enum fmt_type type)
572 return get_fmt_desc (type)->name;
575 /* Tries to parse NAME as a format type.
576 If successful, stores the type in *TYPE and returns true.
577 On failure, returns false. */
579 fmt_from_name (const char *name, enum fmt_type *type)
583 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
584 if (!strcasecmp (name, get_fmt_desc (i)->name))
592 /* Returns true if TYPE accepts decimal places,
595 fmt_takes_decimals (enum fmt_type type)
597 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
600 /* Returns the minimum width of the given format TYPE for the given USE. */
602 fmt_min_width (enum fmt_type type, enum fmt_use use)
604 return (use == FMT_FOR_INPUT
605 ? fmt_min_input_width (type)
606 : fmt_min_output_width (type));
609 /* Returns the maximum width of the given format TYPE,
610 for input if FOR_INPUT is true,
611 for output otherwise. */
613 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
615 /* Maximum width is actually invariant of whether the format is
616 for input or output, so FOR_INPUT is unused. */
617 assert (is_fmt_type (type));
635 return 2 * MAX_STRING;
642 /* Returns the maximum number of decimal places allowed for the
643 given format TYPE with a width of WIDTH places, for the given USE. */
645 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
654 max_d = use == FMT_FOR_INPUT ? width : width - 1;
659 max_d = use == FMT_FOR_INPUT ? width : width - 2;
663 max_d = use == FMT_FOR_INPUT ? width : width - 7;
671 assert (use == FMT_FOR_OUTPUT);
681 max_d = width * 2 - 1;
690 max_d = max_digits_for_bytes (width);
743 /* Returns the minimum acceptable width for an input field
744 formatted with the given TYPE. */
746 fmt_min_input_width (enum fmt_type type)
748 return get_fmt_desc (type)->min_input_width;
751 /* Returns the maximum acceptable width for an input field
752 formatted with the given TYPE. */
754 fmt_max_input_width (enum fmt_type type)
756 return fmt_max_width (type, FMT_FOR_INPUT);
759 /* Returns the maximum number of decimal places allowed in an
760 input field of the given TYPE and WIDTH. */
762 fmt_max_input_decimals (enum fmt_type type, int width)
764 assert (valid_width (type, width, true));
765 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
768 /* Returns the minimum acceptable width for an output field
769 formatted with the given TYPE. */
771 fmt_min_output_width (enum fmt_type type)
773 return get_fmt_desc (type)->min_output_width;
776 /* Returns the maximum acceptable width for an output field
777 formatted with the given TYPE. */
779 fmt_max_output_width (enum fmt_type type)
781 return fmt_max_width (type, FMT_FOR_OUTPUT);
784 /* Returns the maximum number of decimal places allowed in an
785 output field of the given TYPE and WIDTH. */
787 fmt_max_output_decimals (enum fmt_type type, int width)
789 assert (valid_width (type, width, false));
790 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
793 /* Returns the width step for a field formatted with the given
794 TYPE. Field width must be a multiple of the width step. */
796 fmt_step_width (enum fmt_type type)
798 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
802 /* Returns true if TYPE is used for string fields,
803 false if it is used for numeric fields. */
805 fmt_is_string (enum fmt_type type)
807 return fmt_get_category (type) == FMT_CAT_STRING;
810 /* Returns true if TYPE is used for numeric fields,
811 false if it is used for string fields. */
813 fmt_is_numeric (enum fmt_type type)
815 return !fmt_is_string (type);
818 /* Returns the format TYPE's category.
819 Each format type is in exactly one category,
820 and each category's value is bitwise disjoint from every other
821 category. Thus, the return value may be tested for equality
822 or compared bitwise against a mask of FMT_CAT_* values. */
824 fmt_get_category (enum fmt_type type)
826 return get_fmt_desc (type)->category;
829 /* Returns the output format selected by default when TYPE is
830 used as an input format. */
832 fmt_input_to_output (enum fmt_type type)
834 switch (fmt_get_category (type))
841 case FMT_CAT_HEXADECIMAL:
849 /* Returns the SPSS format type corresponding to the given PSPP
852 fmt_to_io (enum fmt_type type)
854 return get_fmt_desc (type)->io;
857 /* Determines the PSPP format corresponding to the given SPSS
858 format type. If successful, sets *FMT_TYPE to the PSPP format
859 and returns true. On failure, return false. */
861 fmt_from_io (int io, enum fmt_type *fmt_type)
865 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
867 *fmt_type = FMT_##NAME; \
869 #include "format.def"
875 /* Returns true if TYPE may be used as an input format,
878 fmt_usable_for_input (enum fmt_type type)
880 assert (is_fmt_type (type));
881 return fmt_get_category (type) != FMT_CAT_CUSTOM;
884 /* For time and date formats, returns a template used for input
887 fmt_date_template (enum fmt_type type)
908 return "dd-mmm-yyyy HH:MM";
918 /* Returns a string representing the format TYPE for use in a GUI dialog. */
920 fmt_gui_name (enum fmt_type type)
934 return _("Scientific");
965 return fmt_name (type);
969 /* Returns true if TYPE is a valid format type,
972 is_fmt_type (enum fmt_type type)
974 return type < FMT_NUMBER_OF_FORMATS;
977 /* Returns true if WIDTH is a valid width for the given format
978 TYPE, for the given USE. */
980 valid_width (enum fmt_type type, int width, enum fmt_use use)
982 return (width >= fmt_min_width (type, use)
983 && width <= fmt_max_width (type, use));
986 /* Returns the maximum number of decimal digits in an unsigned
987 binary number that is BYTES bytes long. */
989 max_digits_for_bytes (int bytes)
991 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
992 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
993 return map[bytes - 1];
996 /* Clamp FMT's width to the range and values allowed by FMT's type. */
998 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1003 min_w = fmt_min_width (fmt->type, use);
1004 max_w = fmt_max_width (fmt->type, use);
1007 else if (fmt->w > max_w)
1010 /* Round width to step. */
1011 step = fmt_step_width (fmt->type);
1012 fmt->w = ROUND_DOWN (fmt->w, step);
1015 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1017 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1021 max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1024 else if (fmt->d > max_d)
1028 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
1030 fmt_affix_set (struct fmt_affix *affix, const char *s)
1032 affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1033 affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1036 /* Frees data in AFFIX. */
1038 fmt_affix_free (struct fmt_affix *affix)
1045 fmt_number_style_init (struct fmt_number_style *style)
1047 fmt_affix_set (&style->neg_prefix, "");
1048 fmt_affix_set (&style->prefix, "");
1049 fmt_affix_set (&style->suffix, "");
1050 fmt_affix_set (&style->neg_suffix, "");
1051 style->decimal = '.';
1052 style->grouping = 0;
1056 fmt_number_style_clone (struct fmt_number_style *new,
1057 const struct fmt_number_style *old)
1059 fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1060 fmt_affix_set (&new->prefix, old->prefix.s);
1061 fmt_affix_set (&new->suffix, old->suffix.s);
1062 fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1063 new->decimal = old->decimal;
1064 new->grouping = old->grouping;
1065 new->extra_bytes = old->extra_bytes;
1068 /* Destroys a struct fmt_number_style. */
1070 fmt_number_style_destroy (struct fmt_number_style *style)
1074 fmt_affix_free (&style->neg_prefix);
1075 fmt_affix_free (&style->prefix);
1076 fmt_affix_free (&style->suffix);
1077 fmt_affix_free (&style->neg_suffix);
1081 /* Returns the total width of the standard prefix and suffix for STYLE, in
1082 display columns (e.g. as returned by u8_strwidth()). */
1084 fmt_affix_width (const struct fmt_number_style *style)
1086 return style->prefix.width + style->suffix.width;
1089 /* Returns the total width of the negative prefix and suffix for STYLE, in
1090 display columns (e.g. as returned by u8_strwidth()). */
1092 fmt_neg_affix_width (const struct fmt_number_style *style)
1094 return style->neg_prefix.width + style->neg_suffix.width;
1097 /* Returns the struct fmt_desc for the given format TYPE. */
1098 static const struct fmt_desc *
1099 get_fmt_desc (enum fmt_type type)
1101 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1103 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1104 {#NAME, IMIN, OMIN, IO, CATEGORY},
1105 #include "format.def"
1108 assert (is_fmt_type (type));
1109 return &formats[type];
1112 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};