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, bool for_input);
51 static int max_digits_for_bytes (int bytes);
53 static void fmt_affix_set (struct fmt_affix *, const char *);
54 static void fmt_affix_free (struct fmt_affix *);
56 static void fmt_number_style_init (struct fmt_number_style *);
57 static void fmt_number_style_clone (struct fmt_number_style *,
58 const struct fmt_number_style *);
59 static void fmt_number_style_destroy (struct fmt_number_style *);
61 /* Creates and returns a new struct fmt_settings with default format styles. */
63 fmt_settings_create (void)
65 struct fmt_settings *settings;
68 settings = xzalloc (sizeof *settings);
69 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
70 fmt_number_style_init (&settings->styles[t]);
71 fmt_settings_set_decimal (settings, '.');
76 /* Destroys SETTINGS. */
78 fmt_settings_destroy (struct fmt_settings *settings)
84 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
85 fmt_number_style_destroy (&settings->styles[t]);
87 free (settings->styles);
91 /* Returns a copy of SETTINGS. */
93 fmt_settings_clone (const struct fmt_settings *old)
95 struct fmt_settings *new;
98 new = xmalloc (sizeof *new);
99 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
100 fmt_number_style_clone (&new->styles[t], &old->styles[t]);
105 /* Returns the number formatting style associated with the given
107 const struct fmt_number_style *
108 fmt_settings_get_style (const struct fmt_settings *settings,
111 assert (is_fmt_type (type));
112 return &settings->styles[type];
115 /* Sets the number style for TYPE to have the given DECIMAL and GROUPING
116 characters, negative prefix NEG_PREFIX, prefix PREFIX, suffix SUFFIX, and
117 negative suffix NEG_SUFFIX. All of the strings are UTF-8 encoded. */
119 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
120 char decimal, char grouping,
121 const char *neg_prefix, const char *prefix,
122 const char *suffix, const char *neg_suffix)
124 struct fmt_number_style *style = &settings->styles[type];
125 int total_bytes, total_width;
127 assert (grouping == '.' || grouping == ',' || grouping == 0);
128 assert (decimal == '.' || decimal == ',');
129 assert (decimal != grouping);
131 fmt_number_style_destroy (style);
133 fmt_affix_set (&style->neg_prefix, neg_prefix);
134 fmt_affix_set (&style->prefix, prefix);
135 fmt_affix_set (&style->suffix, suffix);
136 fmt_affix_set (&style->neg_suffix, neg_suffix);
137 style->decimal = decimal;
138 style->grouping = grouping;
140 total_bytes = (strlen (neg_prefix) + strlen (prefix)
141 + strlen (suffix) + strlen (neg_suffix));
142 total_width = (style->neg_prefix.width + style->prefix.width
143 + style->suffix.width + style->neg_suffix.width);
144 style->extra_bytes = MAX (0, total_bytes - total_width);
147 /* Sets the decimal point character for the settings in S to DECIMAL.
149 This has no effect on custom currency formats. */
151 fmt_settings_set_decimal (struct fmt_settings *s, char decimal)
153 int grouping = decimal == '.' ? ',' : '.';
154 assert (decimal == '.' || decimal == ',');
156 fmt_settings_set_style (s, FMT_F, decimal, 0, "-", "", "", "");
157 fmt_settings_set_style (s, FMT_E, decimal, 0, "-", "", "", "");
158 fmt_settings_set_style (s, FMT_COMMA, decimal, grouping, "-", "", "", "");
159 fmt_settings_set_style (s, FMT_DOT, grouping, decimal, "-", "", "", "");
160 fmt_settings_set_style (s, FMT_DOLLAR, decimal, grouping, "-", "$", "", "");
161 fmt_settings_set_style (s, FMT_PCT, decimal, 0, "-", "", "%", "");
164 /* Returns an input format specification with type TYPE, width W,
167 fmt_for_input (enum fmt_type type, int w, int d)
173 assert (fmt_check_input (&f));
177 /* Returns an output format specification with type TYPE, width
178 W, and D decimals. */
180 fmt_for_output (enum fmt_type type, int w, int d)
186 assert (fmt_check_output (&f));
190 /* Returns the output format specifier corresponding to input
191 format specifier INPUT. */
193 fmt_for_output_from_input (const struct fmt_spec *input)
195 struct fmt_spec output;
197 assert (fmt_check_input (input));
199 output.type = fmt_input_to_output (input->type);
201 if (output.w > fmt_max_output_width (output.type))
202 output.w = fmt_max_output_width (output.type);
203 else if (output.w < fmt_min_output_width (output.type))
204 output.w = fmt_min_output_width (output.type);
221 const struct fmt_number_style *style =
222 settings_get_style (input->type);
224 output.w += fmt_affix_width (style);
225 if (style->grouping != 0 && input->w - input->d >= 3)
226 output.w += (input->w - input->d - 1) / 3;
238 output.d = MAX (input->d, 3);
239 output.w = MAX (input->w, output.d + 7);
243 output.w = max_digits_for_bytes (input->w / 2) + 1;
254 output.w = 2 * input->w + (input->d > 0);
259 output.w = max_digits_for_bytes (input->w) + 1;
275 output.w = input->w / 2;
297 if (output.w > fmt_max_output_width (output.type))
298 output.w = fmt_max_output_width (output.type);
300 assert (fmt_check_output (&output));
304 /* Returns the default format for the given WIDTH: F8.2 format
305 for a numeric value, A format for a string value. */
307 fmt_default_for_width (int width)
310 ? fmt_for_output (FMT_F, 8, 2)
311 : fmt_for_output (FMT_A, width, 0));
314 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
315 or an output format (otherwise) and returns nonzero if so.
316 Otherwise, emits an error message and returns zero. */
318 fmt_check (const struct fmt_spec *spec, bool for_input)
320 const char *io_fmt = for_input ? _("Input format") : _("Output format");
321 char str[FMT_STRING_LEN_MAX + 1];
322 int min_w, max_w, max_d;
324 assert (is_fmt_type (spec->type));
325 fmt_to_string (spec, str);
327 if (for_input && !fmt_usable_for_input (spec->type))
329 msg (SE, _("Format %s may not be used for input."), str);
333 if (spec->w % fmt_step_width (spec->type))
335 assert (fmt_step_width (spec->type) == 2);
336 msg (SE, _("%s specifies width %d, but %s requires an even width."),
337 str, spec->w, fmt_name (spec->type));
341 min_w = fmt_min_width (spec->type, for_input);
342 max_w = fmt_max_width (spec->type, for_input);
343 if (spec->w < min_w || spec->w > max_w)
345 msg (SE, _("%s %s specifies width %d, but "
346 "%s requires a width between %d and %d."),
347 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
351 max_d = fmt_max_decimals (spec->type, spec->w, for_input);
352 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
354 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
355 "%s does not allow any decimals.",
356 "%s %s specifies %d decimal places, but "
357 "%s does not allow any decimals.",
359 io_fmt, str, spec->d, fmt_name (spec->type));
362 else if (spec->d > max_d)
365 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
366 "the given width allows at most %d decimals.",
367 "%s %s specifies %d decimal places, but "
368 "the given width allows at most %d decimals.",
370 io_fmt, str, spec->d, max_d);
372 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
373 "the given width does not allow for any decimals.",
374 "%s %s specifies %d decimal places, but "
375 "the given width does not allow for any decimals.",
377 io_fmt, str, spec->d);
384 /* Checks whether SPEC is valid as an input format and returns
385 nonzero if so. Otherwise, emits an error message and returns
388 fmt_check_input (const struct fmt_spec *spec)
390 return fmt_check (spec, true);
393 /* Checks whether SPEC is valid as an output format and returns
394 true if so. Otherwise, emits an error message and returns false. */
396 fmt_check_output (const struct fmt_spec *spec)
398 return fmt_check (spec, false);
401 /* Checks that FORMAT is appropriate for a variable of the given
402 VAR_TYPE and returns true if so. Otherwise returns false and
403 emits an error message. */
405 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
407 assert (val_type_is_valid (var_type));
408 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
410 char str[FMT_STRING_LEN_MAX + 1];
411 msg (SE, _("%s variables are not compatible with %s format %s."),
412 var_type == VAL_STRING ? _("String") : _("Numeric"),
413 var_type == VAL_STRING ? _("numeric") : _("string"),
414 fmt_to_string (format, str));
420 /* Checks that FORMAT is appropriate for a variable of the given
421 WIDTH and returns true if so. Otherwise returns false and
422 emits an error message. */
424 fmt_check_width_compat (const struct fmt_spec *format, int width)
426 if (!fmt_check_type_compat (format, val_type_from_width (width)))
428 if (fmt_var_width (format) != width)
430 char str[FMT_STRING_LEN_MAX + 1];
431 msg (SE, _("String variable with width %d is not compatible with "
433 width, fmt_to_string (format, str));
439 /* Returns the width corresponding to FORMAT. The return value
440 is the width of the `union value's required by FORMAT. */
442 fmt_var_width (const struct fmt_spec *format)
444 return (format->type == FMT_AHEX ? format->w / 2
445 : format->type == FMT_A ? format->w
449 /* Converts F to its string representation (for instance, "F8.2")
450 in BUFFER. Returns BUFFER.
452 If F has decimals, they are included in the output string,
453 even if F's format type does not allow decimals, to allow
454 accurately presenting incorrect formats to the user. */
456 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
458 if (fmt_takes_decimals (f->type) || f->d > 0)
459 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
460 "%s%d.%d", fmt_name (f->type), f->w, f->d);
462 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
463 "%s%d", fmt_name (f->type), f->w);
467 /* Returns true if A and B are identical formats,
470 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
472 return a->type == b->type && a->w == b->w && a->d == b->d;
475 /* Adjusts FMT to be valid for a value of the given WIDTH. */
477 fmt_resize (struct fmt_spec *fmt, int width)
479 if ((width > 0) != fmt_is_string (fmt->type))
481 /* Changed from numeric to string or vice versa. Set to
482 default format for new width. */
483 *fmt = fmt_default_for_width (width);
487 /* Changed width of string. Preserve format type, adjust
489 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
497 /* Adjusts FMT's width and decimal places to be valid for an
498 input format (if FOR_INPUT) or an output format (if
501 fmt_fix (struct fmt_spec *fmt, bool for_input)
507 /* Clamp width to those allowed by format. */
508 min_w = fmt_min_width (fmt->type, for_input);
509 max_w = fmt_max_width (fmt->type, for_input);
512 else if (fmt->w > max_w)
515 /* Round width to step. */
516 step = fmt_step_width (fmt->type);
517 fmt->w = ROUND_DOWN (fmt->w, step);
519 /* First, if FMT has more decimal places than allowed, attempt
520 to increase FMT's width until that number of decimal places
522 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
525 for (w = fmt->w; w <= max_w; w++)
526 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
533 /* Clamp decimals to those allowed by format and width. */
534 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
537 else if (fmt->d > max_d)
541 /* Adjusts FMT's width and decimal places to be valid for an
544 fmt_fix_input (struct fmt_spec *fmt)
549 /* Adjusts FMT's width and decimal places to be valid for an
552 fmt_fix_output (struct fmt_spec *fmt)
554 fmt_fix (fmt, false);
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,
601 for input if FOR_INPUT is true,
602 for output otherwise. */
604 fmt_min_width (enum fmt_type type, bool for_input)
606 return for_input ? fmt_min_input_width (type) : 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, bool for_input 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,
644 for input if FOR_INPUT is true,
645 for output otherwise. */
647 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
656 max_d = for_input ? width : width - 1;
661 max_d = for_input ? width : width - 2;
665 max_d = for_input ? width : width - 7;
683 max_d = width * 2 - 1;
692 max_d = max_digits_for_bytes (width);
745 /* Returns the minimum acceptable width for an input field
746 formatted with the given TYPE. */
748 fmt_min_input_width (enum fmt_type type)
750 return get_fmt_desc (type)->min_input_width;
753 /* Returns the maximum acceptable width for an input field
754 formatted with the given TYPE. */
756 fmt_max_input_width (enum fmt_type type)
758 return fmt_max_width (type, true);
761 /* Returns the maximum number of decimal places allowed in an
762 input field of the given TYPE and WIDTH. */
764 fmt_max_input_decimals (enum fmt_type type, int width)
766 assert (valid_width (type, width, true));
767 return fmt_max_decimals (type, width, true);
770 /* Returns the minimum acceptable width for an output field
771 formatted with the given TYPE. */
773 fmt_min_output_width (enum fmt_type type)
775 return get_fmt_desc (type)->min_output_width;
778 /* Returns the maximum acceptable width for an output field
779 formatted with the given TYPE. */
781 fmt_max_output_width (enum fmt_type type)
783 return fmt_max_width (type, false);
786 /* Returns the maximum number of decimal places allowed in an
787 output field of the given TYPE and WIDTH. */
789 fmt_max_output_decimals (enum fmt_type type, int width)
791 assert (valid_width (type, width, false));
792 return fmt_max_decimals (type, width, false);
795 /* Returns the width step for a field formatted with the given
796 TYPE. Field width must be a multiple of the width step. */
798 fmt_step_width (enum fmt_type type)
800 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
804 /* Returns true if TYPE is used for string fields,
805 false if it is used for numeric fields. */
807 fmt_is_string (enum fmt_type type)
809 return fmt_get_category (type) == FMT_CAT_STRING;
812 /* Returns true if TYPE is used for numeric fields,
813 false if it is used for string fields. */
815 fmt_is_numeric (enum fmt_type type)
817 return !fmt_is_string (type);
820 /* Returns the format TYPE's category.
821 Each format type is in exactly one category,
822 and each category's value is bitwise disjoint from every other
823 category. Thus, the return value may be tested for equality
824 or compared bitwise against a mask of FMT_CAT_* values. */
826 fmt_get_category (enum fmt_type type)
828 return get_fmt_desc (type)->category;
831 /* Returns the output format selected by default when TYPE is
832 used as an input format. */
834 fmt_input_to_output (enum fmt_type type)
836 switch (fmt_get_category (type))
843 case FMT_CAT_HEXADECIMAL:
851 /* Returns the SPSS format type corresponding to the given PSPP
854 fmt_to_io (enum fmt_type type)
856 return get_fmt_desc (type)->io;
859 /* Determines the PSPP format corresponding to the given SPSS
860 format type. If successful, sets *FMT_TYPE to the PSPP format
861 and returns true. On failure, return false. */
863 fmt_from_io (int io, enum fmt_type *fmt_type)
867 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
869 *fmt_type = FMT_##NAME; \
871 #include "format.def"
877 /* Returns true if TYPE may be used as an input format,
880 fmt_usable_for_input (enum fmt_type type)
882 assert (is_fmt_type (type));
883 return fmt_get_category (type) != FMT_CAT_CUSTOM;
886 /* For time and date formats, returns a template used for input
889 fmt_date_template (enum fmt_type type)
910 return "dd-mmm-yyyy HH:MM";
920 /* Returns a string representing the format TYPE for use in a GUI dialog. */
922 fmt_gui_name (enum fmt_type type)
936 return _("Scientific");
967 return fmt_name (type);
971 /* Returns true if TYPE is a valid format type,
974 is_fmt_type (enum fmt_type type)
976 return type < FMT_NUMBER_OF_FORMATS;
979 /* Returns true if WIDTH is a valid width for the given format
981 for input if FOR_INPUT is true,
982 for output otherwise. */
984 valid_width (enum fmt_type type, int width, bool for_input)
986 return (width >= fmt_min_width (type, for_input)
987 && width <= fmt_max_width (type, for_input));
990 /* Returns the maximum number of decimal digits in an unsigned
991 binary number that is BYTES bytes long. */
993 max_digits_for_bytes (int bytes)
995 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
996 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
997 return map[bytes - 1];
1000 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
1002 fmt_affix_set (struct fmt_affix *affix, const char *s)
1004 affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1005 affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1008 /* Frees data in AFFIX. */
1010 fmt_affix_free (struct fmt_affix *affix)
1017 fmt_number_style_init (struct fmt_number_style *style)
1019 fmt_affix_set (&style->neg_prefix, "");
1020 fmt_affix_set (&style->prefix, "");
1021 fmt_affix_set (&style->suffix, "");
1022 fmt_affix_set (&style->neg_suffix, "");
1023 style->decimal = '.';
1024 style->grouping = 0;
1028 fmt_number_style_clone (struct fmt_number_style *new,
1029 const struct fmt_number_style *old)
1031 fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1032 fmt_affix_set (&new->prefix, old->prefix.s);
1033 fmt_affix_set (&new->suffix, old->suffix.s);
1034 fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1035 new->decimal = old->decimal;
1036 new->grouping = old->grouping;
1037 new->extra_bytes = old->extra_bytes;
1040 /* Destroys a struct fmt_number_style. */
1042 fmt_number_style_destroy (struct fmt_number_style *style)
1046 fmt_affix_free (&style->neg_prefix);
1047 fmt_affix_free (&style->prefix);
1048 fmt_affix_free (&style->suffix);
1049 fmt_affix_free (&style->neg_suffix);
1053 /* Returns the total width of the standard prefix and suffix for STYLE, in
1054 display columns (e.g. as returned by u8_strwidth()). */
1056 fmt_affix_width (const struct fmt_number_style *style)
1058 return style->prefix.width + style->suffix.width;
1061 /* Returns the total width of the negative prefix and suffix for STYLE, in
1062 display columns (e.g. as returned by u8_strwidth()). */
1064 fmt_neg_affix_width (const struct fmt_number_style *style)
1066 return style->neg_prefix.width + style->neg_suffix.width;
1069 /* Returns the struct fmt_desc for the given format TYPE. */
1070 static const struct fmt_desc *
1071 get_fmt_desc (enum fmt_type type)
1073 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1075 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1076 {#NAME, IMIN, OMIN, IO, CATEGORY},
1077 #include "format.def"
1080 assert (is_fmt_type (type));
1081 return &formats[type];
1084 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};