1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2010 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/>. */
24 #include <data/identifier.h>
25 #include <data/settings.h>
26 #include <data/value.h>
27 #include <data/variable.h>
28 #include <libpspp/assertion.h>
29 #include <libpspp/compiler.h>
30 #include <libpspp/message.h>
31 #include <libpspp/misc.h>
32 #include <libpspp/str.h>
38 #define _(msgid) gettext (msgid)
42 struct fmt_number_style styles[FMT_NUMBER_OF_FORMATS];
45 bool is_fmt_type (enum fmt_type);
47 static bool valid_width (enum fmt_type, int width, bool for_input);
49 static int max_digits_for_bytes (int bytes);
51 static void fmt_number_style_init (struct fmt_number_style *);
52 static void fmt_number_style_clone (struct fmt_number_style *,
53 const struct fmt_number_style *);
54 static void fmt_number_style_destroy (struct fmt_number_style *);
56 /* Creates and returns a new struct fmt_settings with default format styles. */
58 fmt_settings_create (void)
60 struct fmt_settings *settings;
63 settings = xzalloc (sizeof *settings);
64 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
65 fmt_number_style_init (&settings->styles[t]);
66 fmt_settings_set_decimal (settings, '.');
71 /* Destroys SETTINGS. */
73 fmt_settings_destroy (struct fmt_settings *settings)
79 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
80 fmt_number_style_destroy (&settings->styles[t]);
82 free (settings->styles);
86 /* Returns a copy of SETTINGS. */
88 fmt_settings_clone (const struct fmt_settings *old)
90 struct fmt_settings *new;
93 new = xmalloc (sizeof *new);
94 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
95 fmt_number_style_clone (&new->styles[t], &old->styles[t]);
100 /* Returns the number formatting style associated with the given
102 const struct fmt_number_style *
103 fmt_settings_get_style (const struct fmt_settings *settings,
106 assert (is_fmt_type (type));
107 return &settings->styles[type];
111 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
112 const struct fmt_number_style *style)
114 fmt_check_style (style);
115 fmt_number_style_destroy (&settings->styles[type]);
116 fmt_number_style_clone (&settings->styles[type], style);
119 /* Sets the number style for TYPE to have the given standard
120 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
121 suffix, DECIMAL as the decimal point character, and GROUPING
122 as the grouping character. */
124 set_style (struct fmt_settings *settings, enum fmt_type type,
125 const char *prefix, const char *suffix,
126 char decimal, char grouping)
128 struct fmt_number_style *style;
130 assert (is_fmt_type (type));
132 style = &settings->styles[type];
134 fmt_number_style_destroy (style);
136 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
137 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
138 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
139 style->decimal = decimal;
140 style->grouping = grouping;
143 /* Sets the decimal point character for SETTINGS to DECIMAL. */
145 fmt_settings_set_decimal (struct fmt_settings *settings, char decimal)
147 int grouping = decimal == '.' ? ',' : '.';
148 assert (decimal == '.' || decimal == ',');
150 set_style (settings, FMT_F, "", "", decimal, 0);
151 set_style (settings, FMT_E, "", "", decimal, 0);
152 set_style (settings, FMT_COMMA, "", "", decimal, grouping);
153 set_style (settings, FMT_DOT, "", "", grouping, decimal);
154 set_style (settings, FMT_DOLLAR, "$", "", decimal, grouping);
155 set_style (settings, FMT_PCT, "", "%", decimal, 0);
158 /* Returns an input format specification with type TYPE, width W,
161 fmt_for_input (enum fmt_type type, int w, int d)
167 assert (fmt_check_input (&f));
171 /* Returns an output format specification with type TYPE, width
172 W, and D decimals. */
174 fmt_for_output (enum fmt_type type, int w, int d)
180 assert (fmt_check_output (&f));
184 /* Returns the output format specifier corresponding to input
185 format specifier INPUT. */
187 fmt_for_output_from_input (const struct fmt_spec *input)
189 struct fmt_spec output;
191 assert (fmt_check_input (input));
193 output.type = fmt_input_to_output (input->type);
195 if (output.w > fmt_max_output_width (output.type))
196 output.w = fmt_max_output_width (output.type);
197 else if (output.w < fmt_min_output_width (output.type))
198 output.w = fmt_min_output_width (output.type);
215 const struct fmt_number_style *style =
216 settings_get_style (input->type);
218 output.w += fmt_affix_width (style);
219 if (style->grouping != 0 && input->w - input->d >= 3)
220 output.w += (input->w - input->d - 1) / 3;
232 output.d = MAX (input->d, 3);
233 output.w = MAX (input->w, output.d + 7);
237 output.w = max_digits_for_bytes (input->w / 2) + 1;
248 output.w = 2 * input->w + (input->d > 0);
253 output.w = max_digits_for_bytes (input->w) + 1;
269 output.w = input->w / 2;
291 if (output.w > fmt_max_output_width (output.type))
292 output.w = fmt_max_output_width (output.type);
294 assert (fmt_check_output (&output));
298 /* Returns the default format for the given WIDTH: F8.2 format
299 for a numeric value, A format for a string value. */
301 fmt_default_for_width (int width)
304 ? fmt_for_output (FMT_F, 8, 2)
305 : fmt_for_output (FMT_A, width, 0));
308 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
309 or an output format (otherwise) and returns nonzero if so.
310 Otherwise, emits an error message and returns zero. */
312 fmt_check (const struct fmt_spec *spec, bool for_input)
314 const char *io_fmt = for_input ? _("Input format") : _("Output format");
315 char str[FMT_STRING_LEN_MAX + 1];
316 int min_w, max_w, max_d;
318 assert (is_fmt_type (spec->type));
319 fmt_to_string (spec, str);
321 if (for_input && !fmt_usable_for_input (spec->type))
323 msg (SE, _("Format %s may not be used for input."), str);
327 if (spec->w % fmt_step_width (spec->type))
329 assert (fmt_step_width (spec->type) == 2);
330 msg (SE, _("%s specifies width %d, but %s requires an even width."),
331 str, spec->w, fmt_name (spec->type));
335 min_w = fmt_min_width (spec->type, for_input);
336 max_w = fmt_max_width (spec->type, for_input);
337 if (spec->w < min_w || spec->w > max_w)
339 msg (SE, _("%s %s specifies width %d, but "
340 "%s requires a width between %d and %d."),
341 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
345 max_d = fmt_max_decimals (spec->type, spec->w, for_input);
346 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
348 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
349 "%s does not allow any decimals.",
350 "%s %s specifies %d decimal places, but "
351 "%s does not allow any decimals.",
353 io_fmt, str, spec->d, fmt_name (spec->type));
356 else if (spec->d > max_d)
359 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
360 "the given width allows at most %d decimals.",
361 "%s %s specifies %d decimal places, but "
362 "the given width allows at most %d decimals.",
364 io_fmt, str, spec->d, max_d);
366 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
367 "the given width does not allow for any decimals.",
368 "%s %s specifies %d decimal places, but "
369 "the given width does not allow for any decimals.",
371 io_fmt, str, spec->d);
378 /* Checks whether SPEC is valid as an input format and returns
379 nonzero if so. Otherwise, emits an error message and returns
382 fmt_check_input (const struct fmt_spec *spec)
384 return fmt_check (spec, true);
387 /* Checks whether SPEC is valid as an output format and returns
388 true if so. Otherwise, emits an error message and returns false. */
390 fmt_check_output (const struct fmt_spec *spec)
392 return fmt_check (spec, false);
395 /* Checks that FORMAT is appropriate for a variable of the given
396 VAR_TYPE and returns true if so. Otherwise returns false and
397 emits an error message. */
399 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
401 assert (val_type_is_valid (var_type));
402 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
404 char str[FMT_STRING_LEN_MAX + 1];
405 msg (SE, _("%s variables are not compatible with %s format %s."),
406 var_type == VAL_STRING ? _("String") : _("Numeric"),
407 var_type == VAL_STRING ? _("numeric") : _("string"),
408 fmt_to_string (format, str));
414 /* Checks that FORMAT is appropriate for a variable of the given
415 WIDTH and returns true if so. Otherwise returns false and
416 emits an error message. */
418 fmt_check_width_compat (const struct fmt_spec *format, int width)
420 if (!fmt_check_type_compat (format, val_type_from_width (width)))
422 if (fmt_var_width (format) != width)
424 char str[FMT_STRING_LEN_MAX + 1];
425 msg (SE, _("String variable with width %d is not compatible with "
427 width, fmt_to_string (format, str));
433 /* Returns the width corresponding to FORMAT. The return value
434 is the width of the `union value's required by FORMAT. */
436 fmt_var_width (const struct fmt_spec *format)
438 return (format->type == FMT_AHEX ? format->w / 2
439 : format->type == FMT_A ? format->w
443 /* Converts F to its string representation (for instance, "F8.2")
444 in BUFFER. Returns BUFFER.
446 If F has decimals, they are included in the output string,
447 even if F's format type does not allow decimals, to allow
448 accurately presenting incorrect formats to the user. */
450 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
452 if (fmt_takes_decimals (f->type) || f->d > 0)
453 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
454 "%s%d.%d", fmt_name (f->type), f->w, f->d);
456 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
457 "%s%d", fmt_name (f->type), f->w);
461 /* Returns true if A and B are identical formats,
464 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
466 return a->type == b->type && a->w == b->w && a->d == b->d;
469 /* Adjusts FMT to be valid for a value of the given WIDTH. */
471 fmt_resize (struct fmt_spec *fmt, int width)
473 if ((width > 0) != fmt_is_string (fmt->type))
475 /* Changed from numeric to string or vice versa. Set to
476 default format for new width. */
477 *fmt = fmt_default_for_width (width);
481 /* Changed width of string. Preserve format type, adjust
483 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
491 /* Adjusts FMT's width and decimal places to be valid for an
492 input format (if FOR_INPUT) or an output format (if
495 fmt_fix (struct fmt_spec *fmt, bool for_input)
500 /* Clamp width to those allowed by format. */
501 min_w = fmt_min_width (fmt->type, for_input);
502 max_w = fmt_max_width (fmt->type, for_input);
505 else if (fmt->w > max_w)
508 /* First, if FMT has more decimal places than allowed, attempt
509 to increase FMT's width until that number of decimal places
511 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
514 for (w = fmt->w; w <= max_w; w++)
515 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
522 /* Clamp decimals to those allowed by format and width. */
523 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
526 else if (fmt->d > max_d)
530 /* Adjusts FMT's width and decimal places to be valid for an
533 fmt_fix_input (struct fmt_spec *fmt)
538 /* Adjusts FMT's width and decimal places to be valid for an
541 fmt_fix_output (struct fmt_spec *fmt)
543 fmt_fix (fmt, false);
546 /* Describes a display format. */
550 int min_input_width, min_output_width;
552 enum fmt_category category;
555 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
557 /* Returns the name of the given format TYPE. */
559 fmt_name (enum fmt_type type)
561 return get_fmt_desc (type)->name;
564 /* Tries to parse NAME as a format type.
565 If successful, stores the type in *TYPE and returns true.
566 On failure, returns false. */
568 fmt_from_name (const char *name, enum fmt_type *type)
572 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
573 if (!strcasecmp (name, get_fmt_desc (i)->name))
581 /* Returns true if TYPE accepts decimal places,
584 fmt_takes_decimals (enum fmt_type type)
586 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
589 /* Returns the minimum width of the given format TYPE,
590 for input if FOR_INPUT is true,
591 for output otherwise. */
593 fmt_min_width (enum fmt_type type, bool for_input)
595 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
598 /* Returns the maximum width of the given format TYPE,
599 for input if FOR_INPUT is true,
600 for output otherwise. */
602 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
604 /* Maximum width is actually invariant of whether the format is
605 for input or output, so FOR_INPUT is unused. */
606 assert (is_fmt_type (type));
624 return 2 * MAX_STRING;
631 /* Returns the maximum number of decimal places allowed for the
632 given format TYPE with a width of WIDTH places,
633 for input if FOR_INPUT is true,
634 for output otherwise. */
636 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
645 max_d = for_input ? width : width - 1;
650 max_d = for_input ? width : width - 2;
654 max_d = for_input ? width : width - 7;
672 max_d = width * 2 - 1;
681 max_d = max_digits_for_bytes (width);
734 /* Returns the minimum acceptable width for an input field
735 formatted with the given TYPE. */
737 fmt_min_input_width (enum fmt_type type)
739 return get_fmt_desc (type)->min_input_width;
742 /* Returns the maximum acceptable width for an input field
743 formatted with the given TYPE. */
745 fmt_max_input_width (enum fmt_type type)
747 return fmt_max_width (type, true);
750 /* Returns the maximum number of decimal places allowed in an
751 input field of the given TYPE and WIDTH. */
753 fmt_max_input_decimals (enum fmt_type type, int width)
755 assert (valid_width (type, width, true));
756 return fmt_max_decimals (type, width, true);
759 /* Returns the minimum acceptable width for an output field
760 formatted with the given TYPE. */
762 fmt_min_output_width (enum fmt_type type)
764 return get_fmt_desc (type)->min_output_width;
767 /* Returns the maximum acceptable width for an output field
768 formatted with the given TYPE. */
770 fmt_max_output_width (enum fmt_type type)
772 return fmt_max_width (type, false);
775 /* Returns the maximum number of decimal places allowed in an
776 output field of the given TYPE and WIDTH. */
778 fmt_max_output_decimals (enum fmt_type type, int width)
780 assert (valid_width (type, width, false));
781 return fmt_max_decimals (type, width, false);
784 /* Returns the width step for a field formatted with the given
785 TYPE. Field width must be a multiple of the width step. */
787 fmt_step_width (enum fmt_type type)
789 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
793 /* Returns true if TYPE is used for string fields,
794 false if it is used for numeric fields. */
796 fmt_is_string (enum fmt_type type)
798 return fmt_get_category (type) == FMT_CAT_STRING;
801 /* Returns true if TYPE is used for numeric fields,
802 false if it is used for string fields. */
804 fmt_is_numeric (enum fmt_type type)
806 return !fmt_is_string (type);
809 /* Returns the format TYPE's category.
810 Each format type is in exactly one category,
811 and each category's value is bitwise disjoint from every other
812 category. Thus, the return value may be tested for equality
813 or compared bitwise against a mask of FMT_CAT_* values. */
815 fmt_get_category (enum fmt_type type)
817 return get_fmt_desc (type)->category;
820 /* Returns the output format selected by default when TYPE is
821 used as an input format. */
823 fmt_input_to_output (enum fmt_type type)
825 switch (fmt_get_category (type))
832 case FMT_CAT_HEXADECIMAL:
840 /* Returns the SPSS format type corresponding to the given PSPP
843 fmt_to_io (enum fmt_type type)
845 return get_fmt_desc (type)->io;
848 /* Determines the PSPP format corresponding to the given SPSS
849 format type. If successful, sets *FMT_TYPE to the PSPP format
850 and returns true. On failure, return false. */
852 fmt_from_io (int io, enum fmt_type *fmt_type)
856 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
858 *fmt_type = FMT_##NAME; \
860 #include "format.def"
866 /* Returns true if TYPE may be used as an input format,
869 fmt_usable_for_input (enum fmt_type type)
871 assert (is_fmt_type (type));
872 return fmt_get_category (type) != FMT_CAT_CUSTOM;
875 /* For time and date formats, returns a template used for input
878 fmt_date_template (enum fmt_type type)
899 return "dd-mmm-yyyy HH:MM";
910 /* Returns true if TYPE is a valid format type,
913 is_fmt_type (enum fmt_type type)
915 return type < FMT_NUMBER_OF_FORMATS;
918 /* Returns true if WIDTH is a valid width for the given format
920 for input if FOR_INPUT is true,
921 for output otherwise. */
923 valid_width (enum fmt_type type, int width, bool for_input)
925 return (width >= fmt_min_width (type, for_input)
926 && width <= fmt_max_width (type, for_input));
929 /* Returns the maximum number of decimal digits in an unsigned
930 binary number that is BYTES bytes long. */
932 max_digits_for_bytes (int bytes)
934 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
935 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
936 return map[bytes - 1];
940 fmt_number_style_init (struct fmt_number_style *style)
942 style->neg_prefix = ss_empty ();
943 style->prefix = ss_empty ();
944 style->suffix = ss_empty ();
945 style->neg_suffix = ss_empty ();
946 style->decimal = '.';
951 fmt_number_style_clone (struct fmt_number_style *new,
952 const struct fmt_number_style *old)
954 ss_alloc_substring (&new->neg_prefix, old->neg_prefix);
955 ss_alloc_substring (&new->prefix, old->prefix);
956 ss_alloc_substring (&new->suffix, old->suffix);
957 ss_alloc_substring (&new->neg_suffix, old->neg_suffix);
958 new->decimal = old->decimal;
959 new->grouping = old->grouping;
962 /* Destroys a struct fmt_number_style. */
964 fmt_number_style_destroy (struct fmt_number_style *style)
968 ss_dealloc (&style->neg_prefix);
969 ss_dealloc (&style->prefix);
970 ss_dealloc (&style->suffix);
971 ss_dealloc (&style->neg_suffix);
975 /* Checks that style is STYLE sane */
977 fmt_check_style (const struct fmt_number_style *style)
979 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
980 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
981 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
982 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
983 assert (style->decimal == '.' || style->decimal == ',');
984 assert (style->grouping == '.' || style->grouping == ','
985 || style->grouping == 0);
986 assert (style->grouping != style->decimal);
990 /* Returns the total width of the standard prefix and suffix for
993 fmt_affix_width (const struct fmt_number_style *style)
995 return ss_length (style->prefix) + ss_length (style->suffix);
998 /* Returns the total width of the negative prefix and suffix for
1001 fmt_neg_affix_width (const struct fmt_number_style *style)
1003 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
1006 /* Returns the struct fmt_desc for the given format TYPE. */
1007 static const struct fmt_desc *
1008 get_fmt_desc (enum fmt_type type)
1010 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1012 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1013 {#NAME, IMIN, OMIN, IO, CATEGORY},
1014 #include "format.def"
1017 assert (is_fmt_type (type));
1018 return &formats[type];
1021 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};