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)
506 /* Clamp width to those allowed by format. */
507 min_w = fmt_min_width (fmt->type, for_input);
508 max_w = fmt_max_width (fmt->type, for_input);
511 else if (fmt->w > max_w)
514 /* First, if FMT has more decimal places than allowed, attempt
515 to increase FMT's width until that number of decimal places
517 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
520 for (w = fmt->w; w <= max_w; w++)
521 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
528 /* Clamp decimals to those allowed by format and width. */
529 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
532 else if (fmt->d > max_d)
536 /* Adjusts FMT's width and decimal places to be valid for an
539 fmt_fix_input (struct fmt_spec *fmt)
544 /* Adjusts FMT's width and decimal places to be valid for an
547 fmt_fix_output (struct fmt_spec *fmt)
549 fmt_fix (fmt, false);
552 /* Describes a display format. */
556 int min_input_width, min_output_width;
558 enum fmt_category category;
561 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
563 /* Returns the name of the given format TYPE. */
565 fmt_name (enum fmt_type type)
567 return get_fmt_desc (type)->name;
570 /* Tries to parse NAME as a format type.
571 If successful, stores the type in *TYPE and returns true.
572 On failure, returns false. */
574 fmt_from_name (const char *name, enum fmt_type *type)
578 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
579 if (!strcasecmp (name, get_fmt_desc (i)->name))
587 /* Returns true if TYPE accepts decimal places,
590 fmt_takes_decimals (enum fmt_type type)
592 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
595 /* Returns the minimum width of the given format TYPE,
596 for input if FOR_INPUT is true,
597 for output otherwise. */
599 fmt_min_width (enum fmt_type type, bool for_input)
601 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
604 /* Returns the maximum width of the given format TYPE,
605 for input if FOR_INPUT is true,
606 for output otherwise. */
608 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
610 /* Maximum width is actually invariant of whether the format is
611 for input or output, so FOR_INPUT is unused. */
612 assert (is_fmt_type (type));
630 return 2 * MAX_STRING;
637 /* Returns the maximum number of decimal places allowed for the
638 given format TYPE with a width of WIDTH places,
639 for input if FOR_INPUT is true,
640 for output otherwise. */
642 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
651 max_d = for_input ? width : width - 1;
656 max_d = for_input ? width : width - 2;
660 max_d = for_input ? width : width - 7;
678 max_d = width * 2 - 1;
687 max_d = max_digits_for_bytes (width);
740 /* Returns the minimum acceptable width for an input field
741 formatted with the given TYPE. */
743 fmt_min_input_width (enum fmt_type type)
745 return get_fmt_desc (type)->min_input_width;
748 /* Returns the maximum acceptable width for an input field
749 formatted with the given TYPE. */
751 fmt_max_input_width (enum fmt_type type)
753 return fmt_max_width (type, true);
756 /* Returns the maximum number of decimal places allowed in an
757 input field of the given TYPE and WIDTH. */
759 fmt_max_input_decimals (enum fmt_type type, int width)
761 assert (valid_width (type, width, true));
762 return fmt_max_decimals (type, width, true);
765 /* Returns the minimum acceptable width for an output field
766 formatted with the given TYPE. */
768 fmt_min_output_width (enum fmt_type type)
770 return get_fmt_desc (type)->min_output_width;
773 /* Returns the maximum acceptable width for an output field
774 formatted with the given TYPE. */
776 fmt_max_output_width (enum fmt_type type)
778 return fmt_max_width (type, false);
781 /* Returns the maximum number of decimal places allowed in an
782 output field of the given TYPE and WIDTH. */
784 fmt_max_output_decimals (enum fmt_type type, int width)
786 assert (valid_width (type, width, false));
787 return fmt_max_decimals (type, width, false);
790 /* Returns the width step for a field formatted with the given
791 TYPE. Field width must be a multiple of the width step. */
793 fmt_step_width (enum fmt_type type)
795 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
799 /* Returns true if TYPE is used for string fields,
800 false if it is used for numeric fields. */
802 fmt_is_string (enum fmt_type type)
804 return fmt_get_category (type) == FMT_CAT_STRING;
807 /* Returns true if TYPE is used for numeric fields,
808 false if it is used for string fields. */
810 fmt_is_numeric (enum fmt_type type)
812 return !fmt_is_string (type);
815 /* Returns the format TYPE's category.
816 Each format type is in exactly one category,
817 and each category's value is bitwise disjoint from every other
818 category. Thus, the return value may be tested for equality
819 or compared bitwise against a mask of FMT_CAT_* values. */
821 fmt_get_category (enum fmt_type type)
823 return get_fmt_desc (type)->category;
826 /* Returns the output format selected by default when TYPE is
827 used as an input format. */
829 fmt_input_to_output (enum fmt_type type)
831 switch (fmt_get_category (type))
838 case FMT_CAT_HEXADECIMAL:
846 /* Returns the SPSS format type corresponding to the given PSPP
849 fmt_to_io (enum fmt_type type)
851 return get_fmt_desc (type)->io;
854 /* Determines the PSPP format corresponding to the given SPSS
855 format type. If successful, sets *FMT_TYPE to the PSPP format
856 and returns true. On failure, return false. */
858 fmt_from_io (int io, enum fmt_type *fmt_type)
862 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
864 *fmt_type = FMT_##NAME; \
866 #include "format.def"
872 /* Returns true if TYPE may be used as an input format,
875 fmt_usable_for_input (enum fmt_type type)
877 assert (is_fmt_type (type));
878 return fmt_get_category (type) != FMT_CAT_CUSTOM;
881 /* For time and date formats, returns a template used for input
884 fmt_date_template (enum fmt_type type)
905 return "dd-mmm-yyyy HH:MM";
916 /* Returns true if TYPE is a valid format type,
919 is_fmt_type (enum fmt_type type)
921 return type < FMT_NUMBER_OF_FORMATS;
924 /* Returns true if WIDTH is a valid width for the given format
926 for input if FOR_INPUT is true,
927 for output otherwise. */
929 valid_width (enum fmt_type type, int width, bool for_input)
931 return (width >= fmt_min_width (type, for_input)
932 && width <= fmt_max_width (type, for_input));
935 /* Returns the maximum number of decimal digits in an unsigned
936 binary number that is BYTES bytes long. */
938 max_digits_for_bytes (int bytes)
940 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
941 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
942 return map[bytes - 1];
945 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
947 fmt_affix_set (struct fmt_affix *affix, const char *s)
949 affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
950 affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
953 /* Frees data in AFFIX. */
955 fmt_affix_free (struct fmt_affix *affix)
962 fmt_number_style_init (struct fmt_number_style *style)
964 fmt_affix_set (&style->neg_prefix, "");
965 fmt_affix_set (&style->prefix, "");
966 fmt_affix_set (&style->suffix, "");
967 fmt_affix_set (&style->neg_suffix, "");
968 style->decimal = '.';
973 fmt_number_style_clone (struct fmt_number_style *new,
974 const struct fmt_number_style *old)
976 fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
977 fmt_affix_set (&new->prefix, old->prefix.s);
978 fmt_affix_set (&new->suffix, old->suffix.s);
979 fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
980 new->decimal = old->decimal;
981 new->grouping = old->grouping;
982 new->extra_bytes = old->extra_bytes;
985 /* Destroys a struct fmt_number_style. */
987 fmt_number_style_destroy (struct fmt_number_style *style)
991 fmt_affix_free (&style->neg_prefix);
992 fmt_affix_free (&style->prefix);
993 fmt_affix_free (&style->suffix);
994 fmt_affix_free (&style->neg_suffix);
998 /* Returns the total width of the standard prefix and suffix for STYLE, in
999 display columns (e.g. as returned by u8_strwidth()). */
1001 fmt_affix_width (const struct fmt_number_style *style)
1003 return style->prefix.width + style->suffix.width;
1006 /* Returns the total width of the negative prefix and suffix for STYLE, in
1007 display columns (e.g. as returned by u8_strwidth()). */
1009 fmt_neg_affix_width (const struct fmt_number_style *style)
1011 return style->neg_prefix.width + style->neg_suffix.width;
1014 /* Returns the struct fmt_desc for the given format TYPE. */
1015 static const struct fmt_desc *
1016 get_fmt_desc (enum fmt_type type)
1018 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1020 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1021 {#NAME, IMIN, OMIN, IO, CATEGORY},
1022 #include "format.def"
1025 assert (is_fmt_type (type));
1026 return &formats[type];
1029 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};