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 /* Adjusts FMT's width and decimal places to be valid for an
413 input format (if FOR_INPUT) or an output format (if
416 fmt_fix (struct fmt_spec *fmt, bool for_input)
421 /* Clamp width to those allowed by format. */
422 min_w = fmt_min_width (fmt->type, for_input);
423 max_w = fmt_max_width (fmt->type, for_input);
426 else if (fmt->w > max_w)
429 /* First, if FMT has more decimal places than allowed, attempt
430 to increase FMT's width until that number of decimal places
432 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
435 for (w = fmt->w; w <= max_w; w++)
436 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
443 /* Clamp decimals to those allowed by format and width. */
444 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
447 else if (fmt->d > max_d)
451 /* Adjusts FMT's width and decimal places to be valid for an
454 fmt_fix_input (struct fmt_spec *fmt)
459 /* Adjusts FMT's width and decimal places to be valid for an
462 fmt_fix_output (struct fmt_spec *fmt)
464 fmt_fix (fmt, false);
467 /* Describes a display format. */
471 int min_input_width, min_output_width;
473 enum fmt_category category;
476 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
478 /* Returns the name of the given format TYPE. */
480 fmt_name (enum fmt_type type)
482 return get_fmt_desc (type)->name;
485 /* Tries to parse NAME as a format type.
486 If successful, stores the type in *TYPE and returns true.
487 On failure, returns false. */
489 fmt_from_name (const char *name, enum fmt_type *type)
493 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
494 if (!strcasecmp (name, get_fmt_desc (i)->name))
502 /* Returns true if TYPE accepts decimal places,
505 fmt_takes_decimals (enum fmt_type type)
507 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
510 /* Returns the minimum width of the given format TYPE,
511 for input if FOR_INPUT is true,
512 for output otherwise. */
514 fmt_min_width (enum fmt_type type, bool for_input)
516 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
519 /* Returns the maximum width of the given format TYPE,
520 for input if FOR_INPUT is true,
521 for output otherwise. */
523 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
525 /* Maximum width is actually invariant of whether the format is
526 for input or output, so FOR_INPUT is unused. */
527 assert (is_fmt_type (type));
545 return 2 * MAX_STRING;
552 /* Returns the maximum number of decimal places allowed for the
553 given format TYPE with a width of WIDTH places,
554 for input if FOR_INPUT is true,
555 for output otherwise. */
557 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
566 max_d = for_input ? width : width - 1;
571 max_d = for_input ? width : width - 2;
575 max_d = for_input ? width : width - 7;
593 max_d = width * 2 - 1;
602 max_d = max_digits_for_bytes (width);
655 /* Returns the minimum acceptable width for an input field
656 formatted with the given TYPE. */
658 fmt_min_input_width (enum fmt_type type)
660 return get_fmt_desc (type)->min_input_width;
663 /* Returns the maximum acceptable width for an input field
664 formatted with the given TYPE. */
666 fmt_max_input_width (enum fmt_type type)
668 return fmt_max_width (type, true);
671 /* Returns the maximum number of decimal places allowed in an
672 input field of the given TYPE and WIDTH. */
674 fmt_max_input_decimals (enum fmt_type type, int width)
676 assert (valid_width (type, width, true));
677 return fmt_max_decimals (type, width, true);
680 /* Returns the minimum acceptable width for an output field
681 formatted with the given TYPE. */
683 fmt_min_output_width (enum fmt_type type)
685 return get_fmt_desc (type)->min_output_width;
688 /* Returns the maximum acceptable width for an output field
689 formatted with the given TYPE. */
691 fmt_max_output_width (enum fmt_type type)
693 return fmt_max_width (type, false);
696 /* Returns the maximum number of decimal places allowed in an
697 output field of the given TYPE and WIDTH. */
699 fmt_max_output_decimals (enum fmt_type type, int width)
701 assert (valid_width (type, width, false));
702 return fmt_max_decimals (type, width, false);
705 /* Returns the width step for a field formatted with the given
706 TYPE. Field width must be a multiple of the width step. */
708 fmt_step_width (enum fmt_type type)
710 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
714 /* Returns true if TYPE is used for string fields,
715 false if it is used for numeric fields. */
717 fmt_is_string (enum fmt_type type)
719 return fmt_get_category (type) == FMT_CAT_STRING;
722 /* Returns true if TYPE is used for numeric fields,
723 false if it is used for string fields. */
725 fmt_is_numeric (enum fmt_type type)
727 return !fmt_is_string (type);
730 /* Returns the format TYPE's category.
731 Each format type is in exactly one category,
732 and each category's value is bitwise disjoint from every other
733 category. Thus, the return value may be tested for equality
734 or compared bitwise against a mask of FMT_CAT_* values. */
736 fmt_get_category (enum fmt_type type)
738 return get_fmt_desc (type)->category;
741 /* Returns the output format selected by default when TYPE is
742 used as an input format. */
744 fmt_input_to_output (enum fmt_type type)
746 switch (fmt_get_category (type))
753 case FMT_CAT_HEXADECIMAL:
761 /* Returns the SPSS format type corresponding to the given PSPP
764 fmt_to_io (enum fmt_type type)
766 return get_fmt_desc (type)->io;
769 /* Determines the PSPP format corresponding to the given SPSS
770 format type. If successful, sets *FMT_TYPE to the PSPP format
771 and returns true. On failure, return false. */
773 fmt_from_io (int io, enum fmt_type *fmt_type)
777 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
778 if (get_fmt_desc (type)->io == io)
786 /* Returns true if TYPE may be used as an input format,
789 fmt_usable_for_input (enum fmt_type type)
791 assert (is_fmt_type (type));
792 return fmt_get_category (type) != FMT_CAT_CUSTOM;
795 /* For time and date formats, returns a template used for input
798 fmt_date_template (enum fmt_type type)
819 return "dd-mmm-yyyy HH:MM";
830 /* Returns true if TYPE is a valid format type,
833 is_fmt_type (enum fmt_type type)
835 return type < FMT_NUMBER_OF_FORMATS;
838 /* Returns true if WIDTH is a valid width for the given format
840 for input if FOR_INPUT is true,
841 for output otherwise. */
843 valid_width (enum fmt_type type, int width, bool for_input)
845 return (width >= fmt_min_width (type, for_input)
846 && width <= fmt_max_width (type, for_input));
849 /* Returns the maximum number of decimal digits in an unsigned
850 binary number that is BYTES bytes long. */
852 max_digits_for_bytes (int bytes)
854 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
855 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
856 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];