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 /* If FMT has more decimal places than allowed, attempt to increase FMT's
520 width until that number of decimal places can be achieved. */
521 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input)
522 && fmt_takes_decimals (fmt->type))
524 int max_w = fmt_max_width (fmt->type, for_input);
525 for (; fmt->w < max_w; fmt->w++)
526 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, for_input))
530 /* Clamp decimals to those allowed by format and width. */
531 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
534 else if (fmt->d > max_d)
538 /* Adjusts FMT's width and decimal places to be valid for an
541 fmt_fix_input (struct fmt_spec *fmt)
546 /* Adjusts FMT's width and decimal places to be valid for an
549 fmt_fix_output (struct fmt_spec *fmt)
551 fmt_fix (fmt, false);
554 /* Describes a display format. */
558 int min_input_width, min_output_width;
560 enum fmt_category category;
563 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
565 /* Returns the name of the given format TYPE. */
567 fmt_name (enum fmt_type type)
569 return get_fmt_desc (type)->name;
572 /* Tries to parse NAME as a format type.
573 If successful, stores the type in *TYPE and returns true.
574 On failure, returns false. */
576 fmt_from_name (const char *name, enum fmt_type *type)
580 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
581 if (!strcasecmp (name, get_fmt_desc (i)->name))
589 /* Returns true if TYPE accepts decimal places,
592 fmt_takes_decimals (enum fmt_type type)
594 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
597 /* Returns the minimum width of the given format TYPE,
598 for input if FOR_INPUT is true,
599 for output otherwise. */
601 fmt_min_width (enum fmt_type type, bool for_input)
603 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
606 /* Returns the maximum width of the given format TYPE,
607 for input if FOR_INPUT is true,
608 for output otherwise. */
610 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
612 /* Maximum width is actually invariant of whether the format is
613 for input or output, so FOR_INPUT is unused. */
614 assert (is_fmt_type (type));
632 return 2 * MAX_STRING;
639 /* Returns the maximum number of decimal places allowed for the
640 given format TYPE with a width of WIDTH places,
641 for input if FOR_INPUT is true,
642 for output otherwise. */
644 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
653 max_d = for_input ? width : width - 1;
658 max_d = for_input ? width : width - 2;
662 max_d = for_input ? width : width - 7;
680 max_d = width * 2 - 1;
689 max_d = max_digits_for_bytes (width);
742 /* Returns the minimum acceptable width for an input field
743 formatted with the given TYPE. */
745 fmt_min_input_width (enum fmt_type type)
747 return get_fmt_desc (type)->min_input_width;
750 /* Returns the maximum acceptable width for an input field
751 formatted with the given TYPE. */
753 fmt_max_input_width (enum fmt_type type)
755 return fmt_max_width (type, true);
758 /* Returns the maximum number of decimal places allowed in an
759 input field of the given TYPE and WIDTH. */
761 fmt_max_input_decimals (enum fmt_type type, int width)
763 assert (valid_width (type, width, true));
764 return fmt_max_decimals (type, width, true);
767 /* Returns the minimum acceptable width for an output field
768 formatted with the given TYPE. */
770 fmt_min_output_width (enum fmt_type type)
772 return get_fmt_desc (type)->min_output_width;
775 /* Returns the maximum acceptable width for an output field
776 formatted with the given TYPE. */
778 fmt_max_output_width (enum fmt_type type)
780 return fmt_max_width (type, false);
783 /* Returns the maximum number of decimal places allowed in an
784 output field of the given TYPE and WIDTH. */
786 fmt_max_output_decimals (enum fmt_type type, int width)
788 assert (valid_width (type, width, false));
789 return fmt_max_decimals (type, width, false);
792 /* Returns the width step for a field formatted with the given
793 TYPE. Field width must be a multiple of the width step. */
795 fmt_step_width (enum fmt_type type)
797 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
801 /* Returns true if TYPE is used for string fields,
802 false if it is used for numeric fields. */
804 fmt_is_string (enum fmt_type type)
806 return fmt_get_category (type) == FMT_CAT_STRING;
809 /* Returns true if TYPE is used for numeric fields,
810 false if it is used for string fields. */
812 fmt_is_numeric (enum fmt_type type)
814 return !fmt_is_string (type);
817 /* Returns the format TYPE's category.
818 Each format type is in exactly one category,
819 and each category's value is bitwise disjoint from every other
820 category. Thus, the return value may be tested for equality
821 or compared bitwise against a mask of FMT_CAT_* values. */
823 fmt_get_category (enum fmt_type type)
825 return get_fmt_desc (type)->category;
828 /* Returns the output format selected by default when TYPE is
829 used as an input format. */
831 fmt_input_to_output (enum fmt_type type)
833 switch (fmt_get_category (type))
840 case FMT_CAT_HEXADECIMAL:
848 /* Returns the SPSS format type corresponding to the given PSPP
851 fmt_to_io (enum fmt_type type)
853 return get_fmt_desc (type)->io;
856 /* Determines the PSPP format corresponding to the given SPSS
857 format type. If successful, sets *FMT_TYPE to the PSPP format
858 and returns true. On failure, return false. */
860 fmt_from_io (int io, enum fmt_type *fmt_type)
864 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
866 *fmt_type = FMT_##NAME; \
868 #include "format.def"
874 /* Returns true if TYPE may be used as an input format,
877 fmt_usable_for_input (enum fmt_type type)
879 assert (is_fmt_type (type));
880 return fmt_get_category (type) != FMT_CAT_CUSTOM;
883 /* For time and date formats, returns a template used for input
886 fmt_date_template (enum fmt_type type)
907 return "dd-mmm-yyyy HH:MM";
917 /* Returns a string representing the format TYPE for use in a GUI dialog. */
919 fmt_gui_name (enum fmt_type type)
933 return _("Scientific");
964 return fmt_name (type);
968 /* Returns true if TYPE is a valid format type,
971 is_fmt_type (enum fmt_type type)
973 return type < FMT_NUMBER_OF_FORMATS;
976 /* Returns true if WIDTH is a valid width for the given format
978 for input if FOR_INPUT is true,
979 for output otherwise. */
981 valid_width (enum fmt_type type, int width, bool for_input)
983 return (width >= fmt_min_width (type, for_input)
984 && width <= fmt_max_width (type, for_input));
987 /* Returns the maximum number of decimal digits in an unsigned
988 binary number that is BYTES bytes long. */
990 max_digits_for_bytes (int bytes)
992 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
993 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
994 return map[bytes - 1];
997 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
999 fmt_affix_set (struct fmt_affix *affix, const char *s)
1001 affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1002 affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1005 /* Frees data in AFFIX. */
1007 fmt_affix_free (struct fmt_affix *affix)
1014 fmt_number_style_init (struct fmt_number_style *style)
1016 fmt_affix_set (&style->neg_prefix, "");
1017 fmt_affix_set (&style->prefix, "");
1018 fmt_affix_set (&style->suffix, "");
1019 fmt_affix_set (&style->neg_suffix, "");
1020 style->decimal = '.';
1021 style->grouping = 0;
1025 fmt_number_style_clone (struct fmt_number_style *new,
1026 const struct fmt_number_style *old)
1028 fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1029 fmt_affix_set (&new->prefix, old->prefix.s);
1030 fmt_affix_set (&new->suffix, old->suffix.s);
1031 fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1032 new->decimal = old->decimal;
1033 new->grouping = old->grouping;
1034 new->extra_bytes = old->extra_bytes;
1037 /* Destroys a struct fmt_number_style. */
1039 fmt_number_style_destroy (struct fmt_number_style *style)
1043 fmt_affix_free (&style->neg_prefix);
1044 fmt_affix_free (&style->prefix);
1045 fmt_affix_free (&style->suffix);
1046 fmt_affix_free (&style->neg_suffix);
1050 /* Returns the total width of the standard prefix and suffix for STYLE, in
1051 display columns (e.g. as returned by u8_strwidth()). */
1053 fmt_affix_width (const struct fmt_number_style *style)
1055 return style->prefix.width + style->suffix.width;
1058 /* Returns the total width of the negative prefix and suffix for STYLE, in
1059 display columns (e.g. as returned by u8_strwidth()). */
1061 fmt_neg_affix_width (const struct fmt_number_style *style)
1063 return style->neg_prefix.width + style->neg_suffix.width;
1066 /* Returns the struct fmt_desc for the given format TYPE. */
1067 static const struct fmt_desc *
1068 get_fmt_desc (enum fmt_type type)
1070 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1072 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1073 {#NAME, IMIN, OMIN, IO, CATEGORY},
1074 #include "format.def"
1077 assert (is_fmt_type (type));
1078 return &formats[type];
1081 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};