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 bool is_fmt_type (enum fmt_type);
44 static bool valid_width (enum fmt_type, int width, bool for_input);
46 static int max_digits_for_bytes (int bytes);
48 static void fmt_number_style_init (struct fmt_number_style *);
49 static void fmt_number_style_destroy (struct fmt_number_style *);
52 /* Initialize the format module. */
53 struct fmt_number_style *
56 struct fmt_number_style *styles =
57 xcalloc (FMT_NUMBER_OF_FORMATS, sizeof (*styles));
60 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
61 fmt_number_style_init (&styles[t]);
63 fmt_set_decimal (styles, '.');
69 /* Deinitialize the format module. */
71 fmt_done (struct fmt_number_style *styles)
74 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
75 fmt_number_style_destroy (&styles[t]);
80 /* Returns an input format specification with type TYPE, width W,
83 fmt_for_input (enum fmt_type type, int w, int d)
89 assert (fmt_check_input (&f));
93 /* Returns an output format specification with type TYPE, width
96 fmt_for_output (enum fmt_type type, int w, int d)
102 assert (fmt_check_output (&f));
106 /* Returns the output format specifier corresponding to input
107 format specifier INPUT. */
109 fmt_for_output_from_input (const struct fmt_spec *input)
111 struct fmt_spec output;
113 assert (fmt_check_input (input));
115 output.type = fmt_input_to_output (input->type);
117 if (output.w > fmt_max_output_width (output.type))
118 output.w = fmt_max_output_width (output.type);
119 else if (output.w < fmt_min_output_width (output.type))
120 output.w = fmt_min_output_width (output.type);
137 const struct fmt_number_style *style =
138 settings_get_style (input->type);
140 output.w += fmt_affix_width (style);
141 if (style->grouping != 0 && input->w - input->d >= 3)
142 output.w += (input->w - input->d - 1) / 3;
154 output.d = MAX (input->d, 3);
155 output.w = MAX (input->w, output.d + 7);
159 output.w = max_digits_for_bytes (input->w / 2) + 1;
170 output.w = 2 * input->w + (input->d > 0);
175 output.w = max_digits_for_bytes (input->w) + 1;
191 output.w = input->w / 2;
213 if (output.w > fmt_max_output_width (output.type))
214 output.w = fmt_max_output_width (output.type);
216 assert (fmt_check_output (&output));
220 /* Returns the default format for the given WIDTH: F8.2 format
221 for a numeric value, A format for a string value. */
223 fmt_default_for_width (int width)
226 ? fmt_for_output (FMT_F, 8, 2)
227 : fmt_for_output (FMT_A, width, 0));
230 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
231 or an output format (otherwise) and returns nonzero if so.
232 Otherwise, emits an error message and returns zero. */
234 fmt_check (const struct fmt_spec *spec, bool for_input)
236 const char *io_fmt = for_input ? _("Input format") : _("Output format");
237 char str[FMT_STRING_LEN_MAX + 1];
238 int min_w, max_w, max_d;
240 assert (is_fmt_type (spec->type));
241 fmt_to_string (spec, str);
243 if (for_input && !fmt_usable_for_input (spec->type))
245 msg (SE, _("Format %s may not be used for input."), str);
249 if (spec->w % fmt_step_width (spec->type))
251 assert (fmt_step_width (spec->type) == 2);
252 msg (SE, _("%s specifies width %d, but %s requires an even width."),
253 str, spec->w, fmt_name (spec->type));
257 min_w = fmt_min_width (spec->type, for_input);
258 max_w = fmt_max_width (spec->type, for_input);
259 if (spec->w < min_w || spec->w > max_w)
261 msg (SE, _("%s %s specifies width %d, but "
262 "%s requires a width between %d and %d."),
263 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
267 max_d = fmt_max_decimals (spec->type, spec->w, for_input);
268 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
270 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
271 "%s does not allow any decimals.",
272 "%s %s specifies %d decimal places, but "
273 "%s does not allow any decimals.",
275 io_fmt, str, spec->d, fmt_name (spec->type));
278 else if (spec->d > max_d)
281 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
282 "the given width allows at most %d decimals.",
283 "%s %s specifies %d decimal places, but "
284 "the given width allows at most %d decimals.",
286 io_fmt, str, spec->d, max_d);
288 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
289 "the given width does not allow for any decimals.",
290 "%s %s specifies %d decimal places, but "
291 "the given width does not allow for any decimals.",
293 io_fmt, str, spec->d);
300 /* Checks whether SPEC is valid as an input format and returns
301 nonzero if so. Otherwise, emits an error message and returns
304 fmt_check_input (const struct fmt_spec *spec)
306 return fmt_check (spec, true);
309 /* Checks whether SPEC is valid as an output format and returns
310 true if so. Otherwise, emits an error message and returns false. */
312 fmt_check_output (const struct fmt_spec *spec)
314 return fmt_check (spec, false);
317 /* Checks that FORMAT is appropriate for a variable of the given
318 VAR_TYPE and returns true if so. Otherwise returns false and
319 emits an error message. */
321 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
323 assert (val_type_is_valid (var_type));
324 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
326 char str[FMT_STRING_LEN_MAX + 1];
327 msg (SE, _("%s variables are not compatible with %s format %s."),
328 var_type == VAL_STRING ? _("String") : _("Numeric"),
329 var_type == VAL_STRING ? _("numeric") : _("string"),
330 fmt_to_string (format, str));
336 /* Checks that FORMAT is appropriate for a variable of the given
337 WIDTH and returns true if so. Otherwise returns false and
338 emits an error message. */
340 fmt_check_width_compat (const struct fmt_spec *format, int width)
342 if (!fmt_check_type_compat (format, val_type_from_width (width)))
344 if (fmt_var_width (format) != width)
346 char str[FMT_STRING_LEN_MAX + 1];
347 msg (SE, _("String variable with width %d is not compatible with "
349 width, fmt_to_string (format, str));
355 /* Returns the width corresponding to FORMAT. The return value
356 is the width of the `union value's required by FORMAT. */
358 fmt_var_width (const struct fmt_spec *format)
360 return (format->type == FMT_AHEX ? format->w / 2
361 : format->type == FMT_A ? format->w
365 /* Converts F to its string representation (for instance, "F8.2")
366 in BUFFER. Returns BUFFER.
368 If F has decimals, they are included in the output string,
369 even if F's format type does not allow decimals, to allow
370 accurately presenting incorrect formats to the user. */
372 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
374 if (fmt_takes_decimals (f->type) || f->d > 0)
375 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
376 "%s%d.%d", fmt_name (f->type), f->w, f->d);
378 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
379 "%s%d", fmt_name (f->type), f->w);
383 /* Returns true if A and B are identical formats,
386 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
388 return a->type == b->type && a->w == b->w && a->d == b->d;
391 /* Adjusts FMT to be valid for a value of the given WIDTH. */
393 fmt_resize (struct fmt_spec *fmt, int width)
395 if ((width > 0) != fmt_is_string (fmt->type))
397 /* Changed from numeric to string or vice versa. Set to
398 default format for new width. */
399 *fmt = fmt_default_for_width (width);
403 /* Changed width of string. Preserve format type, adjust
405 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
413 /* Adjusts FMT's width and decimal places to be valid for an
414 input format (if FOR_INPUT) or an output format (if
417 fmt_fix (struct fmt_spec *fmt, bool for_input)
422 /* Clamp width to those allowed by format. */
423 min_w = fmt_min_width (fmt->type, for_input);
424 max_w = fmt_max_width (fmt->type, for_input);
427 else if (fmt->w > max_w)
430 /* First, if FMT has more decimal places than allowed, attempt
431 to increase FMT's width until that number of decimal places
433 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
436 for (w = fmt->w; w <= max_w; w++)
437 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
444 /* Clamp decimals to those allowed by format and width. */
445 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
448 else if (fmt->d > max_d)
452 /* Adjusts FMT's width and decimal places to be valid for an
455 fmt_fix_input (struct fmt_spec *fmt)
460 /* Adjusts FMT's width and decimal places to be valid for an
463 fmt_fix_output (struct fmt_spec *fmt)
465 fmt_fix (fmt, false);
468 /* Describes a display format. */
472 int min_input_width, min_output_width;
474 enum fmt_category category;
477 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
479 /* Returns the name of the given format TYPE. */
481 fmt_name (enum fmt_type type)
483 return get_fmt_desc (type)->name;
486 /* Tries to parse NAME as a format type.
487 If successful, stores the type in *TYPE and returns true.
488 On failure, returns false. */
490 fmt_from_name (const char *name, enum fmt_type *type)
494 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
495 if (!strcasecmp (name, get_fmt_desc (i)->name))
503 /* Returns true if TYPE accepts decimal places,
506 fmt_takes_decimals (enum fmt_type type)
508 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
511 /* Returns the minimum width of the given format TYPE,
512 for input if FOR_INPUT is true,
513 for output otherwise. */
515 fmt_min_width (enum fmt_type type, bool for_input)
517 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
520 /* Returns the maximum width of the given format TYPE,
521 for input if FOR_INPUT is true,
522 for output otherwise. */
524 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
526 /* Maximum width is actually invariant of whether the format is
527 for input or output, so FOR_INPUT is unused. */
528 assert (is_fmt_type (type));
546 return 2 * MAX_STRING;
553 /* Returns the maximum number of decimal places allowed for the
554 given format TYPE with a width of WIDTH places,
555 for input if FOR_INPUT is true,
556 for output otherwise. */
558 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
567 max_d = for_input ? width : width - 1;
572 max_d = for_input ? width : width - 2;
576 max_d = for_input ? width : width - 7;
594 max_d = width * 2 - 1;
603 max_d = max_digits_for_bytes (width);
656 /* Returns the minimum acceptable width for an input field
657 formatted with the given TYPE. */
659 fmt_min_input_width (enum fmt_type type)
661 return get_fmt_desc (type)->min_input_width;
664 /* Returns the maximum acceptable width for an input field
665 formatted with the given TYPE. */
667 fmt_max_input_width (enum fmt_type type)
669 return fmt_max_width (type, true);
672 /* Returns the maximum number of decimal places allowed in an
673 input field of the given TYPE and WIDTH. */
675 fmt_max_input_decimals (enum fmt_type type, int width)
677 assert (valid_width (type, width, true));
678 return fmt_max_decimals (type, width, true);
681 /* Returns the minimum acceptable width for an output field
682 formatted with the given TYPE. */
684 fmt_min_output_width (enum fmt_type type)
686 return get_fmt_desc (type)->min_output_width;
689 /* Returns the maximum acceptable width for an output field
690 formatted with the given TYPE. */
692 fmt_max_output_width (enum fmt_type type)
694 return fmt_max_width (type, false);
697 /* Returns the maximum number of decimal places allowed in an
698 output field of the given TYPE and WIDTH. */
700 fmt_max_output_decimals (enum fmt_type type, int width)
702 assert (valid_width (type, width, false));
703 return fmt_max_decimals (type, width, false);
706 /* Returns the width step for a field formatted with the given
707 TYPE. Field width must be a multiple of the width step. */
709 fmt_step_width (enum fmt_type type)
711 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
715 /* Returns true if TYPE is used for string fields,
716 false if it is used for numeric fields. */
718 fmt_is_string (enum fmt_type type)
720 return fmt_get_category (type) == FMT_CAT_STRING;
723 /* Returns true if TYPE is used for numeric fields,
724 false if it is used for string fields. */
726 fmt_is_numeric (enum fmt_type type)
728 return !fmt_is_string (type);
731 /* Returns the format TYPE's category.
732 Each format type is in exactly one category,
733 and each category's value is bitwise disjoint from every other
734 category. Thus, the return value may be tested for equality
735 or compared bitwise against a mask of FMT_CAT_* values. */
737 fmt_get_category (enum fmt_type type)
739 return get_fmt_desc (type)->category;
742 /* Returns the output format selected by default when TYPE is
743 used as an input format. */
745 fmt_input_to_output (enum fmt_type type)
747 switch (fmt_get_category (type))
754 case FMT_CAT_HEXADECIMAL:
762 /* Returns the SPSS format type corresponding to the given PSPP
765 fmt_to_io (enum fmt_type type)
767 return get_fmt_desc (type)->io;
770 /* Determines the PSPP format corresponding to the given SPSS
771 format type. If successful, sets *FMT_TYPE to the PSPP format
772 and returns true. On failure, return false. */
774 fmt_from_io (int io, enum fmt_type *fmt_type)
778 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
780 *fmt_type = FMT_##NAME; \
782 #include "format.def"
788 /* Returns true if TYPE may be used as an input format,
791 fmt_usable_for_input (enum fmt_type type)
793 assert (is_fmt_type (type));
794 return fmt_get_category (type) != FMT_CAT_CUSTOM;
797 /* For time and date formats, returns a template used for input
800 fmt_date_template (enum fmt_type type)
821 return "dd-mmm-yyyy HH:MM";
832 /* Returns true if TYPE is a valid format type,
835 is_fmt_type (enum fmt_type type)
837 return type < FMT_NUMBER_OF_FORMATS;
840 /* Returns true if WIDTH is a valid width for the given format
842 for input if FOR_INPUT is true,
843 for output otherwise. */
845 valid_width (enum fmt_type type, int width, bool for_input)
847 return (width >= fmt_min_width (type, for_input)
848 && width <= fmt_max_width (type, for_input));
851 /* Returns the maximum number of decimal digits in an unsigned
852 binary number that is BYTES bytes long. */
854 max_digits_for_bytes (int bytes)
856 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
857 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
858 return map[bytes - 1];
862 fmt_number_style_init (struct fmt_number_style *style)
864 style->neg_prefix = ss_empty ();
865 style->prefix = ss_empty ();
866 style->suffix = ss_empty ();
867 style->neg_suffix = ss_empty ();
868 style->decimal = '.';
873 /* Destroys a struct fmt_number_style. */
875 fmt_number_style_destroy (struct fmt_number_style *style)
879 ss_dealloc (&style->neg_prefix);
880 ss_dealloc (&style->prefix);
881 ss_dealloc (&style->suffix);
882 ss_dealloc (&style->neg_suffix);
886 /* Returns the number formatting style associated with the given
888 const struct fmt_number_style *
889 fmt_get_style (const struct fmt_number_style *styles, enum fmt_type type)
891 assert (is_fmt_type (type));
892 return &styles[type];
896 /* Checks that style is STYLE sane */
898 fmt_check_style (const struct fmt_number_style *style)
900 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
901 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
902 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
903 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
904 assert (style->decimal == '.' || style->decimal == ',');
905 assert (style->grouping == '.' || style->grouping == ','
906 || style->grouping == 0);
907 assert (style->grouping != style->decimal);
911 /* Returns the total width of the standard prefix and suffix for
914 fmt_affix_width (const struct fmt_number_style *style)
916 return ss_length (style->prefix) + ss_length (style->suffix);
919 /* Returns the total width of the negative prefix and suffix for
922 fmt_neg_affix_width (const struct fmt_number_style *style)
924 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
928 /* Sets the number style for TYPE to have the given standard
929 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
930 suffix, DECIMAL as the decimal point character, and GROUPING
931 as the grouping character. */
933 set_style (struct fmt_number_style *styles, enum fmt_type type,
934 const char *prefix, const char *suffix,
935 char decimal, char grouping)
937 struct fmt_number_style *style;
939 assert (is_fmt_type (type));
941 style = &styles[type] ;
943 fmt_number_style_destroy (style);
945 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
946 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
947 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
948 style->decimal = decimal;
949 style->grouping = grouping;
952 /* Sets the decimal point character to DECIMAL. */
954 fmt_set_decimal (struct fmt_number_style *styles, char decimal)
956 int grouping = decimal == '.' ? ',' : '.';
957 assert (decimal == '.' || decimal == ',');
959 set_style (styles, FMT_F, "", "", decimal, 0);
960 set_style (styles, FMT_E, "", "", decimal, 0);
961 set_style (styles, FMT_COMMA, "", "", decimal, grouping);
962 set_style (styles, FMT_DOT, "", "", grouping, decimal);
963 set_style (styles, FMT_DOLLAR, "$", "", decimal, grouping);
964 set_style (styles, FMT_PCT, "", "%", decimal, 0);
967 /* Returns the struct fmt_desc for the given format TYPE. */
968 static const struct fmt_desc *
969 get_fmt_desc (enum fmt_type type)
971 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
973 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
974 {#NAME, IMIN, OMIN, IO, CATEGORY},
975 #include "format.def"
978 assert (is_fmt_type (type));
979 return &formats[type];
982 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};