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/>. */
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];
110 /* Sets the number style for TYPE to have the given DECIMAL and GROUPING
111 characters, negative prefix NEG_PREFIX, prefix PREFIX, suffix SUFFIX, and
112 negative suffix NEG_SUFFIX. */
114 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
115 char decimal, char grouping,
116 const char *neg_prefix, const char *prefix,
117 const char *suffix, const char *neg_suffix)
119 struct fmt_number_style *style = &settings->styles[type];
121 assert (grouping == '.' || grouping == ',' || grouping == 0);
122 assert (decimal == '.' || decimal == ',');
123 assert (decimal != grouping);
125 fmt_number_style_destroy (style);
127 ss_alloc_substring (&style->neg_prefix, ss_cstr (neg_prefix));
128 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
129 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
130 ss_alloc_substring (&style->neg_suffix, ss_cstr (neg_suffix));
131 style->decimal = decimal;
132 style->grouping = grouping;
135 /* Sets the decimal point character for the settings in S to DECIMAL.
137 This has no effect on custom currency formats. */
139 fmt_settings_set_decimal (struct fmt_settings *s, char decimal)
141 int grouping = decimal == '.' ? ',' : '.';
142 assert (decimal == '.' || decimal == ',');
144 fmt_settings_set_style (s, FMT_F, decimal, 0, "-", "", "", "");
145 fmt_settings_set_style (s, FMT_E, decimal, 0, "-", "", "", "");
146 fmt_settings_set_style (s, FMT_COMMA, decimal, grouping, "-", "", "", "");
147 fmt_settings_set_style (s, FMT_DOT, grouping, decimal, "-", "", "", "");
148 fmt_settings_set_style (s, FMT_DOLLAR, decimal, grouping, "-", "$", "", "");
149 fmt_settings_set_style (s, FMT_PCT, decimal, 0, "-", "", "%", "");
152 /* Returns an input format specification with type TYPE, width W,
155 fmt_for_input (enum fmt_type type, int w, int d)
161 assert (fmt_check_input (&f));
165 /* Returns an output format specification with type TYPE, width
166 W, and D decimals. */
168 fmt_for_output (enum fmt_type type, int w, int d)
174 assert (fmt_check_output (&f));
178 /* Returns the output format specifier corresponding to input
179 format specifier INPUT. */
181 fmt_for_output_from_input (const struct fmt_spec *input)
183 struct fmt_spec output;
185 assert (fmt_check_input (input));
187 output.type = fmt_input_to_output (input->type);
189 if (output.w > fmt_max_output_width (output.type))
190 output.w = fmt_max_output_width (output.type);
191 else if (output.w < fmt_min_output_width (output.type))
192 output.w = fmt_min_output_width (output.type);
209 const struct fmt_number_style *style =
210 settings_get_style (input->type);
212 output.w += fmt_affix_width (style);
213 if (style->grouping != 0 && input->w - input->d >= 3)
214 output.w += (input->w - input->d - 1) / 3;
226 output.d = MAX (input->d, 3);
227 output.w = MAX (input->w, output.d + 7);
231 output.w = max_digits_for_bytes (input->w / 2) + 1;
242 output.w = 2 * input->w + (input->d > 0);
247 output.w = max_digits_for_bytes (input->w) + 1;
263 output.w = input->w / 2;
285 if (output.w > fmt_max_output_width (output.type))
286 output.w = fmt_max_output_width (output.type);
288 assert (fmt_check_output (&output));
292 /* Returns the default format for the given WIDTH: F8.2 format
293 for a numeric value, A format for a string value. */
295 fmt_default_for_width (int width)
298 ? fmt_for_output (FMT_F, 8, 2)
299 : fmt_for_output (FMT_A, width, 0));
302 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
303 or an output format (otherwise) and returns nonzero if so.
304 Otherwise, emits an error message and returns zero. */
306 fmt_check (const struct fmt_spec *spec, bool for_input)
308 const char *io_fmt = for_input ? _("Input format") : _("Output format");
309 char str[FMT_STRING_LEN_MAX + 1];
310 int min_w, max_w, max_d;
312 assert (is_fmt_type (spec->type));
313 fmt_to_string (spec, str);
315 if (for_input && !fmt_usable_for_input (spec->type))
317 msg (SE, _("Format %s may not be used for input."), str);
321 if (spec->w % fmt_step_width (spec->type))
323 assert (fmt_step_width (spec->type) == 2);
324 msg (SE, _("%s specifies width %d, but %s requires an even width."),
325 str, spec->w, fmt_name (spec->type));
329 min_w = fmt_min_width (spec->type, for_input);
330 max_w = fmt_max_width (spec->type, for_input);
331 if (spec->w < min_w || spec->w > max_w)
333 msg (SE, _("%s %s specifies width %d, but "
334 "%s requires a width between %d and %d."),
335 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
339 max_d = fmt_max_decimals (spec->type, spec->w, for_input);
340 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
342 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
343 "%s does not allow any decimals.",
344 "%s %s specifies %d decimal places, but "
345 "%s does not allow any decimals.",
347 io_fmt, str, spec->d, fmt_name (spec->type));
350 else if (spec->d > max_d)
353 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
354 "the given width allows at most %d decimals.",
355 "%s %s specifies %d decimal places, but "
356 "the given width allows at most %d decimals.",
358 io_fmt, str, spec->d, max_d);
360 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
361 "the given width does not allow for any decimals.",
362 "%s %s specifies %d decimal places, but "
363 "the given width does not allow for any decimals.",
365 io_fmt, str, spec->d);
372 /* Checks whether SPEC is valid as an input format and returns
373 nonzero if so. Otherwise, emits an error message and returns
376 fmt_check_input (const struct fmt_spec *spec)
378 return fmt_check (spec, true);
381 /* Checks whether SPEC is valid as an output format and returns
382 true if so. Otherwise, emits an error message and returns false. */
384 fmt_check_output (const struct fmt_spec *spec)
386 return fmt_check (spec, false);
389 /* Checks that FORMAT is appropriate for a variable of the given
390 VAR_TYPE and returns true if so. Otherwise returns false and
391 emits an error message. */
393 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
395 assert (val_type_is_valid (var_type));
396 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
398 char str[FMT_STRING_LEN_MAX + 1];
399 msg (SE, _("%s variables are not compatible with %s format %s."),
400 var_type == VAL_STRING ? _("String") : _("Numeric"),
401 var_type == VAL_STRING ? _("numeric") : _("string"),
402 fmt_to_string (format, str));
408 /* Checks that FORMAT is appropriate for a variable of the given
409 WIDTH and returns true if so. Otherwise returns false and
410 emits an error message. */
412 fmt_check_width_compat (const struct fmt_spec *format, int width)
414 if (!fmt_check_type_compat (format, val_type_from_width (width)))
416 if (fmt_var_width (format) != width)
418 char str[FMT_STRING_LEN_MAX + 1];
419 msg (SE, _("String variable with width %d is not compatible with "
421 width, fmt_to_string (format, str));
427 /* Returns the width corresponding to FORMAT. The return value
428 is the width of the `union value's required by FORMAT. */
430 fmt_var_width (const struct fmt_spec *format)
432 return (format->type == FMT_AHEX ? format->w / 2
433 : format->type == FMT_A ? format->w
437 /* Converts F to its string representation (for instance, "F8.2")
438 in BUFFER. Returns BUFFER.
440 If F has decimals, they are included in the output string,
441 even if F's format type does not allow decimals, to allow
442 accurately presenting incorrect formats to the user. */
444 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
446 if (fmt_takes_decimals (f->type) || f->d > 0)
447 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
448 "%s%d.%d", fmt_name (f->type), f->w, f->d);
450 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
451 "%s%d", fmt_name (f->type), f->w);
455 /* Returns true if A and B are identical formats,
458 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
460 return a->type == b->type && a->w == b->w && a->d == b->d;
463 /* Adjusts FMT to be valid for a value of the given WIDTH. */
465 fmt_resize (struct fmt_spec *fmt, int width)
467 if ((width > 0) != fmt_is_string (fmt->type))
469 /* Changed from numeric to string or vice versa. Set to
470 default format for new width. */
471 *fmt = fmt_default_for_width (width);
475 /* Changed width of string. Preserve format type, adjust
477 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
485 /* Adjusts FMT's width and decimal places to be valid for an
486 input format (if FOR_INPUT) or an output format (if
489 fmt_fix (struct fmt_spec *fmt, bool for_input)
494 /* Clamp width to those allowed by format. */
495 min_w = fmt_min_width (fmt->type, for_input);
496 max_w = fmt_max_width (fmt->type, for_input);
499 else if (fmt->w > max_w)
502 /* First, if FMT has more decimal places than allowed, attempt
503 to increase FMT's width until that number of decimal places
505 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
508 for (w = fmt->w; w <= max_w; w++)
509 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
516 /* Clamp decimals to those allowed by format and width. */
517 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
520 else if (fmt->d > max_d)
524 /* Adjusts FMT's width and decimal places to be valid for an
527 fmt_fix_input (struct fmt_spec *fmt)
532 /* Adjusts FMT's width and decimal places to be valid for an
535 fmt_fix_output (struct fmt_spec *fmt)
537 fmt_fix (fmt, false);
540 /* Describes a display format. */
544 int min_input_width, min_output_width;
546 enum fmt_category category;
549 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
551 /* Returns the name of the given format TYPE. */
553 fmt_name (enum fmt_type type)
555 return get_fmt_desc (type)->name;
558 /* Tries to parse NAME as a format type.
559 If successful, stores the type in *TYPE and returns true.
560 On failure, returns false. */
562 fmt_from_name (const char *name, enum fmt_type *type)
566 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
567 if (!strcasecmp (name, get_fmt_desc (i)->name))
575 /* Returns true if TYPE accepts decimal places,
578 fmt_takes_decimals (enum fmt_type type)
580 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
583 /* Returns the minimum width of the given format TYPE,
584 for input if FOR_INPUT is true,
585 for output otherwise. */
587 fmt_min_width (enum fmt_type type, bool for_input)
589 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
592 /* Returns the maximum width of the given format TYPE,
593 for input if FOR_INPUT is true,
594 for output otherwise. */
596 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
598 /* Maximum width is actually invariant of whether the format is
599 for input or output, so FOR_INPUT is unused. */
600 assert (is_fmt_type (type));
618 return 2 * MAX_STRING;
625 /* Returns the maximum number of decimal places allowed for the
626 given format TYPE with a width of WIDTH places,
627 for input if FOR_INPUT is true,
628 for output otherwise. */
630 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
639 max_d = for_input ? width : width - 1;
644 max_d = for_input ? width : width - 2;
648 max_d = for_input ? width : width - 7;
666 max_d = width * 2 - 1;
675 max_d = max_digits_for_bytes (width);
728 /* Returns the minimum acceptable width for an input field
729 formatted with the given TYPE. */
731 fmt_min_input_width (enum fmt_type type)
733 return get_fmt_desc (type)->min_input_width;
736 /* Returns the maximum acceptable width for an input field
737 formatted with the given TYPE. */
739 fmt_max_input_width (enum fmt_type type)
741 return fmt_max_width (type, true);
744 /* Returns the maximum number of decimal places allowed in an
745 input field of the given TYPE and WIDTH. */
747 fmt_max_input_decimals (enum fmt_type type, int width)
749 assert (valid_width (type, width, true));
750 return fmt_max_decimals (type, width, true);
753 /* Returns the minimum acceptable width for an output field
754 formatted with the given TYPE. */
756 fmt_min_output_width (enum fmt_type type)
758 return get_fmt_desc (type)->min_output_width;
761 /* Returns the maximum acceptable width for an output field
762 formatted with the given TYPE. */
764 fmt_max_output_width (enum fmt_type type)
766 return fmt_max_width (type, false);
769 /* Returns the maximum number of decimal places allowed in an
770 output field of the given TYPE and WIDTH. */
772 fmt_max_output_decimals (enum fmt_type type, int width)
774 assert (valid_width (type, width, false));
775 return fmt_max_decimals (type, width, false);
778 /* Returns the width step for a field formatted with the given
779 TYPE. Field width must be a multiple of the width step. */
781 fmt_step_width (enum fmt_type type)
783 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
787 /* Returns true if TYPE is used for string fields,
788 false if it is used for numeric fields. */
790 fmt_is_string (enum fmt_type type)
792 return fmt_get_category (type) == FMT_CAT_STRING;
795 /* Returns true if TYPE is used for numeric fields,
796 false if it is used for string fields. */
798 fmt_is_numeric (enum fmt_type type)
800 return !fmt_is_string (type);
803 /* Returns the format TYPE's category.
804 Each format type is in exactly one category,
805 and each category's value is bitwise disjoint from every other
806 category. Thus, the return value may be tested for equality
807 or compared bitwise against a mask of FMT_CAT_* values. */
809 fmt_get_category (enum fmt_type type)
811 return get_fmt_desc (type)->category;
814 /* Returns the output format selected by default when TYPE is
815 used as an input format. */
817 fmt_input_to_output (enum fmt_type type)
819 switch (fmt_get_category (type))
826 case FMT_CAT_HEXADECIMAL:
834 /* Returns the SPSS format type corresponding to the given PSPP
837 fmt_to_io (enum fmt_type type)
839 return get_fmt_desc (type)->io;
842 /* Determines the PSPP format corresponding to the given SPSS
843 format type. If successful, sets *FMT_TYPE to the PSPP format
844 and returns true. On failure, return false. */
846 fmt_from_io (int io, enum fmt_type *fmt_type)
850 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
852 *fmt_type = FMT_##NAME; \
854 #include "format.def"
860 /* Returns true if TYPE may be used as an input format,
863 fmt_usable_for_input (enum fmt_type type)
865 assert (is_fmt_type (type));
866 return fmt_get_category (type) != FMT_CAT_CUSTOM;
869 /* For time and date formats, returns a template used for input
872 fmt_date_template (enum fmt_type type)
893 return "dd-mmm-yyyy HH:MM";
904 /* Returns true if TYPE is a valid format type,
907 is_fmt_type (enum fmt_type type)
909 return type < FMT_NUMBER_OF_FORMATS;
912 /* Returns true if WIDTH is a valid width for the given format
914 for input if FOR_INPUT is true,
915 for output otherwise. */
917 valid_width (enum fmt_type type, int width, bool for_input)
919 return (width >= fmt_min_width (type, for_input)
920 && width <= fmt_max_width (type, for_input));
923 /* Returns the maximum number of decimal digits in an unsigned
924 binary number that is BYTES bytes long. */
926 max_digits_for_bytes (int bytes)
928 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
929 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
930 return map[bytes - 1];
934 fmt_number_style_init (struct fmt_number_style *style)
936 style->neg_prefix = ss_empty ();
937 style->prefix = ss_empty ();
938 style->suffix = ss_empty ();
939 style->neg_suffix = ss_empty ();
940 style->decimal = '.';
945 fmt_number_style_clone (struct fmt_number_style *new,
946 const struct fmt_number_style *old)
948 ss_alloc_substring (&new->neg_prefix, old->neg_prefix);
949 ss_alloc_substring (&new->prefix, old->prefix);
950 ss_alloc_substring (&new->suffix, old->suffix);
951 ss_alloc_substring (&new->neg_suffix, old->neg_suffix);
952 new->decimal = old->decimal;
953 new->grouping = old->grouping;
956 /* Destroys a struct fmt_number_style. */
958 fmt_number_style_destroy (struct fmt_number_style *style)
962 ss_dealloc (&style->neg_prefix);
963 ss_dealloc (&style->prefix);
964 ss_dealloc (&style->suffix);
965 ss_dealloc (&style->neg_suffix);
969 /* Returns the total width of the standard prefix and suffix for
972 fmt_affix_width (const struct fmt_number_style *style)
974 return ss_length (style->prefix) + ss_length (style->suffix);
977 /* Returns the total width of the negative prefix and suffix for
980 fmt_neg_affix_width (const struct fmt_number_style *style)
982 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
985 /* Returns the struct fmt_desc for the given format TYPE. */
986 static const struct fmt_desc *
987 get_fmt_desc (enum fmt_type type)
989 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
991 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
992 {#NAME, IMIN, OMIN, IO, CATEGORY},
993 #include "format.def"
996 assert (is_fmt_type (type));
997 return &formats[type];
1000 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};