1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006 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 void fmt_number_style_init (struct fmt_number_style *style);
51 /* Initialize the format module. */
52 struct fmt_number_style *
55 struct fmt_number_style *styles =
56 xcalloc (FMT_NUMBER_OF_FORMATS, sizeof (*styles));
59 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
60 fmt_number_style_init (&styles[t]);
62 fmt_set_decimal (styles, '.');
68 /* Deinitialize the format module. */
70 fmt_done (struct fmt_number_style *styles)
73 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
74 fmt_number_style_destroy (&styles[t]);
79 /* Returns an input format specification with type TYPE, width W,
82 fmt_for_input (enum fmt_type type, int w, int d)
88 assert (fmt_check_input (&f));
92 /* Returns an output format specification with type TYPE, width
95 fmt_for_output (enum fmt_type type, int w, int d)
101 assert (fmt_check_output (&f));
105 /* Returns the output format specifier corresponding to input
106 format specifier INPUT. */
108 fmt_for_output_from_input (const struct fmt_spec *input)
110 struct fmt_spec output;
112 assert (fmt_check_input (input));
114 output.type = fmt_input_to_output (input->type);
116 if (output.w > fmt_max_output_width (output.type))
117 output.w = fmt_max_output_width (output.type);
118 else if (output.w < fmt_min_output_width (output.type))
119 output.w = fmt_min_output_width (output.type);
136 const struct fmt_number_style *style =
137 settings_get_style (input->type);
139 output.w += fmt_affix_width (style);
140 if (style->grouping != 0 && input->w - input->d >= 3)
141 output.w += (input->w - input->d - 1) / 3;
153 output.d = MAX (input->d, 3);
154 output.w = MAX (input->w, output.d + 7);
158 output.w = max_digits_for_bytes (input->w / 2) + 1;
169 output.w = 2 * input->w + (input->d > 0);
174 output.w = max_digits_for_bytes (input->w) + 1;
190 output.w = input->w / 2;
212 if (output.w > fmt_max_output_width (output.type))
213 output.w = fmt_max_output_width (output.type);
215 assert (fmt_check_output (&output));
219 /* Returns the default format for the given WIDTH: F8.2 format
220 for a numeric value, A format for a string value. */
222 fmt_default_for_width (int width)
225 ? fmt_for_output (FMT_F, 8, 2)
226 : fmt_for_output (FMT_A, width, 0));
229 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
230 or an output format (otherwise) and returns nonzero if so.
231 Otherwise, emits an error message and returns zero. */
233 fmt_check (const struct fmt_spec *spec, bool for_input)
235 const char *io_fmt = for_input ? _("Input format") : _("Output format");
236 char str[FMT_STRING_LEN_MAX + 1];
237 int min_w, max_w, max_d;
239 assert (is_fmt_type (spec->type));
240 fmt_to_string (spec, str);
242 if (for_input && !fmt_usable_for_input (spec->type))
244 msg (SE, _("Format %s may not be used for input."), str);
248 if (spec->w % fmt_step_width (spec->type))
250 assert (fmt_step_width (spec->type) == 2);
251 msg (SE, _("%s specifies width %d, but %s requires an even width."),
252 str, spec->w, fmt_name (spec->type));
256 min_w = fmt_min_width (spec->type, for_input);
257 max_w = fmt_max_width (spec->type, for_input);
258 if (spec->w < min_w || spec->w > max_w)
260 msg (SE, _("%s %s specifies width %d, but "
261 "%s requires a width between %d and %d."),
262 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
266 max_d = fmt_max_decimals (spec->type, spec->w, for_input);
267 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
269 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
270 "%s does not allow any decimals.",
271 "%s %s specifies %d decimal places, but "
272 "%s does not allow any decimals.",
274 io_fmt, str, spec->d, fmt_name (spec->type));
277 else if (spec->d > max_d)
280 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
281 "the given width allows at most %d decimals.",
282 "%s %s specifies %d decimal places, but "
283 "the given width allows at most %d decimals.",
285 io_fmt, str, spec->d, max_d);
287 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
288 "the given width does not allow for any decimals.",
289 "%s %s specifies %d decimal places, but "
290 "the given width does not allow for any decimals.",
292 io_fmt, str, spec->d);
299 /* Checks whether SPEC is valid as an input format and returns
300 nonzero if so. Otherwise, emits an error message and returns
303 fmt_check_input (const struct fmt_spec *spec)
305 return fmt_check (spec, true);
308 /* Checks whether SPEC is valid as an output format and returns
309 true if so. Otherwise, emits an error message and returns false. */
311 fmt_check_output (const struct fmt_spec *spec)
313 return fmt_check (spec, false);
316 /* Checks that FORMAT is appropriate for a variable of the given
317 VAR_TYPE and returns true if so. Otherwise returns false and
318 emits an error message. */
320 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
322 assert (val_type_is_valid (var_type));
323 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
325 char str[FMT_STRING_LEN_MAX + 1];
326 msg (SE, _("%s variables are not compatible with %s format %s."),
327 var_type == VAL_STRING ? _("String") : _("Numeric"),
328 var_type == VAL_STRING ? _("numeric") : _("string"),
329 fmt_to_string (format, str));
335 /* Checks that FORMAT is appropriate for a variable of the given
336 WIDTH and returns true if so. Otherwise returns false and
337 emits an error message. */
339 fmt_check_width_compat (const struct fmt_spec *format, int width)
341 if (!fmt_check_type_compat (format, val_type_from_width (width)))
343 if (fmt_var_width (format) != width)
345 char str[FMT_STRING_LEN_MAX + 1];
346 msg (SE, _("String variable with width %d is not compatible with "
348 width, fmt_to_string (format, str));
354 /* Returns the width corresponding to FORMAT. The return value
355 is the width of the `union value's required by FORMAT. */
357 fmt_var_width (const struct fmt_spec *format)
359 return (format->type == FMT_AHEX ? format->w / 2
360 : format->type == FMT_A ? format->w
364 /* Converts F to its string representation (for instance, "F8.2")
365 in BUFFER. Returns BUFFER.
367 If F has decimals, they are included in the output string,
368 even if F's format type does not allow decimals, to allow
369 accurately presenting incorrect formats to the user. */
371 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
373 if (fmt_takes_decimals (f->type) || f->d > 0)
374 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
375 "%s%d.%d", fmt_name (f->type), f->w, f->d);
377 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
378 "%s%d", fmt_name (f->type), f->w);
382 /* Returns true if A and B are identical formats,
385 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
387 return a->type == b->type && a->w == b->w && a->d == b->d;
390 /* Adjusts FMT to be valid for a value of the given WIDTH. */
392 fmt_resize (struct fmt_spec *fmt, int width)
394 if ((width > 0) != fmt_is_string (fmt->type))
396 /* Changed from numeric to string or vice versa. Set to
397 default format for new width. */
398 *fmt = fmt_default_for_width (width);
402 /* Changed width of string. Preserve format type, adjust
404 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
412 /* Describes a display format. */
416 int min_input_width, min_output_width;
418 enum fmt_category category;
421 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
423 /* Returns the name of the given format TYPE. */
425 fmt_name (enum fmt_type type)
427 return get_fmt_desc (type)->name;
430 /* Tries to parse NAME as a format type.
431 If successful, stores the type in *TYPE and returns true.
432 On failure, returns false. */
434 fmt_from_name (const char *name, enum fmt_type *type)
438 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
439 if (!strcasecmp (name, get_fmt_desc (i)->name))
447 /* Returns true if TYPE accepts decimal places,
450 fmt_takes_decimals (enum fmt_type type)
452 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
455 /* Returns the minimum width of the given format TYPE,
456 for input if FOR_INPUT is true,
457 for output otherwise. */
459 fmt_min_width (enum fmt_type type, bool for_input)
461 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
464 /* Returns the maximum width of the given format TYPE,
465 for input if FOR_INPUT is true,
466 for output otherwise. */
468 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
470 /* Maximum width is actually invariant of whether the format is
471 for input or output, so FOR_INPUT is unused. */
472 assert (is_fmt_type (type));
490 return 2 * MAX_STRING;
497 /* Returns the maximum number of decimal places allowed for the
498 given format TYPE with a width of WIDTH places,
499 for input if FOR_INPUT is true,
500 for output otherwise. */
502 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
511 max_d = for_input ? width : width - 1;
516 max_d = for_input ? width : width - 2;
520 max_d = for_input ? width : width - 7;
538 max_d = width * 2 - 1;
547 max_d = max_digits_for_bytes (width);
600 /* Returns the minimum acceptable width for an input field
601 formatted with the given TYPE. */
603 fmt_min_input_width (enum fmt_type type)
605 return get_fmt_desc (type)->min_input_width;
608 /* Returns the maximum acceptable width for an input field
609 formatted with the given TYPE. */
611 fmt_max_input_width (enum fmt_type type)
613 return fmt_max_width (type, true);
616 /* Returns the maximum number of decimal places allowed in an
617 input field of the given TYPE and WIDTH. */
619 fmt_max_input_decimals (enum fmt_type type, int width)
621 assert (valid_width (type, width, true));
622 return fmt_max_decimals (type, width, true);
625 /* Returns the minimum acceptable width for an output field
626 formatted with the given TYPE. */
628 fmt_min_output_width (enum fmt_type type)
630 return get_fmt_desc (type)->min_output_width;
633 /* Returns the maximum acceptable width for an output field
634 formatted with the given TYPE. */
636 fmt_max_output_width (enum fmt_type type)
638 return fmt_max_width (type, false);
641 /* Returns the maximum number of decimal places allowed in an
642 output field of the given TYPE and WIDTH. */
644 fmt_max_output_decimals (enum fmt_type type, int width)
646 assert (valid_width (type, width, false));
647 return fmt_max_decimals (type, width, false);
650 /* Returns the width step for a field formatted with the given
651 TYPE. Field width must be a multiple of the width step. */
653 fmt_step_width (enum fmt_type type)
655 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
659 /* Returns true if TYPE is used for string fields,
660 false if it is used for numeric fields. */
662 fmt_is_string (enum fmt_type type)
664 return fmt_get_category (type) == FMT_CAT_STRING;
667 /* Returns true if TYPE is used for numeric fields,
668 false if it is used for string fields. */
670 fmt_is_numeric (enum fmt_type type)
672 return !fmt_is_string (type);
675 /* Returns the format TYPE's category.
676 Each format type is in exactly one category,
677 and each category's value is bitwise disjoint from every other
678 category. Thus, the return value may be tested for equality
679 or compared bitwise against a mask of FMT_CAT_* values. */
681 fmt_get_category (enum fmt_type type)
683 return get_fmt_desc (type)->category;
686 /* Returns the output format selected by default when TYPE is
687 used as an input format. */
689 fmt_input_to_output (enum fmt_type type)
691 switch (fmt_get_category (type))
698 case FMT_CAT_HEXADECIMAL:
706 /* Returns the SPSS format type corresponding to the given PSPP
709 fmt_to_io (enum fmt_type type)
711 return get_fmt_desc (type)->io;
714 /* Determines the PSPP format corresponding to the given SPSS
715 format type. If successful, sets *FMT_TYPE to the PSPP format
716 and returns true. On failure, return false. */
718 fmt_from_io (int io, enum fmt_type *fmt_type)
722 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
723 if (get_fmt_desc (type)->io == io)
731 /* Returns true if TYPE may be used as an input format,
734 fmt_usable_for_input (enum fmt_type type)
736 assert (is_fmt_type (type));
737 return fmt_get_category (type) != FMT_CAT_CUSTOM;
740 /* For time and date formats, returns a template used for input
743 fmt_date_template (enum fmt_type type)
764 return "dd-mmm-yyyy HH:MM";
775 /* Returns true if TYPE is a valid format type,
778 is_fmt_type (enum fmt_type type)
780 return type < FMT_NUMBER_OF_FORMATS;
783 /* Returns true if WIDTH is a valid width for the given format
785 for input if FOR_INPUT is true,
786 for output otherwise. */
788 valid_width (enum fmt_type type, int width, bool for_input)
790 return (width >= fmt_min_width (type, for_input)
791 && width <= fmt_max_width (type, for_input));
794 /* Returns the maximum number of decimal digits in an unsigned
795 binary number that is BYTES bytes long. */
797 max_digits_for_bytes (int bytes)
799 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
800 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
801 return map[bytes - 1];
807 fmt_number_style_init (struct fmt_number_style *style)
809 style->neg_prefix = ss_empty ();
810 style->prefix = ss_empty ();
811 style->suffix = ss_empty ();
812 style->neg_suffix = ss_empty ();
813 style->decimal = '.';
818 /* Destroys a struct fmt_number_style. */
820 fmt_number_style_destroy (struct fmt_number_style *style)
824 ss_dealloc (&style->neg_prefix);
825 ss_dealloc (&style->prefix);
826 ss_dealloc (&style->suffix);
827 ss_dealloc (&style->neg_suffix);
831 /* Returns the number formatting style associated with the given
833 const struct fmt_number_style *
834 fmt_get_style (const struct fmt_number_style *styles, enum fmt_type type)
836 assert (is_fmt_type (type));
837 return &styles[type];
841 /* Checks that style is STYLE sane */
843 fmt_check_style (const struct fmt_number_style *style)
845 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
846 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
847 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
848 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
849 assert (style->decimal == '.' || style->decimal == ',');
850 assert (style->grouping == '.' || style->grouping == ','
851 || style->grouping == 0);
852 assert (style->grouping != style->decimal);
856 /* Returns the total width of the standard prefix and suffix for
859 fmt_affix_width (const struct fmt_number_style *style)
861 return ss_length (style->prefix) + ss_length (style->suffix);
864 /* Returns the total width of the negative prefix and suffix for
867 fmt_neg_affix_width (const struct fmt_number_style *style)
869 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
873 /* Sets the number style for TYPE to have the given standard
874 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
875 suffix, DECIMAL as the decimal point character, and GROUPING
876 as the grouping character. */
878 set_style (struct fmt_number_style *styles, enum fmt_type type,
879 const char *prefix, const char *suffix,
880 char decimal, char grouping)
882 struct fmt_number_style *style;
884 assert (is_fmt_type (type));
886 style = &styles[type] ;
888 fmt_number_style_destroy (style);
890 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
891 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
892 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
893 style->decimal = decimal;
894 style->grouping = grouping;
897 /* Sets the decimal point character to DECIMAL. */
899 fmt_set_decimal (struct fmt_number_style *styles, char decimal)
901 int grouping = decimal == '.' ? ',' : '.';
902 assert (decimal == '.' || decimal == ',');
904 set_style (styles, FMT_F, "", "", decimal, 0);
905 set_style (styles, FMT_E, "", "", decimal, 0);
906 set_style (styles, FMT_COMMA, "", "", decimal, grouping);
907 set_style (styles, FMT_DOT, "", "", grouping, decimal);
908 set_style (styles, FMT_DOLLAR, "$", "", decimal, grouping);
909 set_style (styles, FMT_PCT, "", "%", decimal, 0);
912 /* Returns the struct fmt_desc for the given format TYPE. */
913 static const struct fmt_desc *
914 get_fmt_desc (enum fmt_type type)
916 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
918 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
919 {#NAME, IMIN, OMIN, IO, CATEGORY},
920 #include "format.def"
923 assert (is_fmt_type (type));
924 return &formats[type];