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/cast.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/message.h"
32 #include "libpspp/misc.h"
33 #include "libpspp/str.h"
35 #include "gl/minmax.h"
36 #include "gl/xalloc.h"
39 #define _(msgid) gettext (msgid)
43 struct fmt_number_style styles[FMT_NUMBER_OF_FORMATS];
46 bool is_fmt_type (enum fmt_type);
48 static bool valid_width (enum fmt_type, int width, bool for_input);
50 static int max_digits_for_bytes (int bytes);
52 static void fmt_affix_set (struct fmt_affix *, const char *);
53 static void fmt_affix_free (struct fmt_affix *);
55 static void fmt_number_style_init (struct fmt_number_style *);
56 static void fmt_number_style_clone (struct fmt_number_style *,
57 const struct fmt_number_style *);
58 static void fmt_number_style_destroy (struct fmt_number_style *);
60 /* Creates and returns a new struct fmt_settings with default format styles. */
62 fmt_settings_create (void)
64 struct fmt_settings *settings;
67 settings = xzalloc (sizeof *settings);
68 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
69 fmt_number_style_init (&settings->styles[t]);
70 fmt_settings_set_decimal (settings, '.');
75 /* Destroys SETTINGS. */
77 fmt_settings_destroy (struct fmt_settings *settings)
83 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
84 fmt_number_style_destroy (&settings->styles[t]);
86 free (settings->styles);
90 /* Returns a copy of SETTINGS. */
92 fmt_settings_clone (const struct fmt_settings *old)
94 struct fmt_settings *new;
97 new = xmalloc (sizeof *new);
98 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
99 fmt_number_style_clone (&new->styles[t], &old->styles[t]);
104 /* Returns the number formatting style associated with the given
106 const struct fmt_number_style *
107 fmt_settings_get_style (const struct fmt_settings *settings,
110 assert (is_fmt_type (type));
111 return &settings->styles[type];
114 /* Sets the number style for TYPE to have the given DECIMAL and GROUPING
115 characters, negative prefix NEG_PREFIX, prefix PREFIX, suffix SUFFIX, and
116 negative suffix NEG_SUFFIX. */
118 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
119 char decimal, char grouping,
120 const char *neg_prefix, const char *prefix,
121 const char *suffix, const char *neg_suffix)
123 struct fmt_number_style *style = &settings->styles[type];
125 assert (grouping == '.' || grouping == ',' || grouping == 0);
126 assert (decimal == '.' || decimal == ',');
127 assert (decimal != grouping);
129 fmt_number_style_destroy (style);
131 fmt_affix_set (&style->neg_prefix, neg_prefix);
132 fmt_affix_set (&style->prefix, prefix);
133 fmt_affix_set (&style->suffix, suffix);
134 fmt_affix_set (&style->neg_suffix, neg_suffix);
135 style->decimal = decimal;
136 style->grouping = grouping;
139 /* Sets the decimal point character for the settings in S to DECIMAL.
141 This has no effect on custom currency formats. */
143 fmt_settings_set_decimal (struct fmt_settings *s, char decimal)
145 int grouping = decimal == '.' ? ',' : '.';
146 assert (decimal == '.' || decimal == ',');
148 fmt_settings_set_style (s, FMT_F, decimal, 0, "-", "", "", "");
149 fmt_settings_set_style (s, FMT_E, decimal, 0, "-", "", "", "");
150 fmt_settings_set_style (s, FMT_COMMA, decimal, grouping, "-", "", "", "");
151 fmt_settings_set_style (s, FMT_DOT, grouping, decimal, "-", "", "", "");
152 fmt_settings_set_style (s, FMT_DOLLAR, decimal, grouping, "-", "$", "", "");
153 fmt_settings_set_style (s, FMT_PCT, decimal, 0, "-", "", "%", "");
156 /* Returns an input format specification with type TYPE, width W,
159 fmt_for_input (enum fmt_type type, int w, int d)
165 assert (fmt_check_input (&f));
169 /* Returns an output format specification with type TYPE, width
170 W, and D decimals. */
172 fmt_for_output (enum fmt_type type, int w, int d)
178 assert (fmt_check_output (&f));
182 /* Returns the output format specifier corresponding to input
183 format specifier INPUT. */
185 fmt_for_output_from_input (const struct fmt_spec *input)
187 struct fmt_spec output;
189 assert (fmt_check_input (input));
191 output.type = fmt_input_to_output (input->type);
193 if (output.w > fmt_max_output_width (output.type))
194 output.w = fmt_max_output_width (output.type);
195 else if (output.w < fmt_min_output_width (output.type))
196 output.w = fmt_min_output_width (output.type);
213 const struct fmt_number_style *style =
214 settings_get_style (input->type);
216 output.w += fmt_affix_width (style);
217 if (style->grouping != 0 && input->w - input->d >= 3)
218 output.w += (input->w - input->d - 1) / 3;
230 output.d = MAX (input->d, 3);
231 output.w = MAX (input->w, output.d + 7);
235 output.w = max_digits_for_bytes (input->w / 2) + 1;
246 output.w = 2 * input->w + (input->d > 0);
251 output.w = max_digits_for_bytes (input->w) + 1;
267 output.w = input->w / 2;
289 if (output.w > fmt_max_output_width (output.type))
290 output.w = fmt_max_output_width (output.type);
292 assert (fmt_check_output (&output));
296 /* Returns the default format for the given WIDTH: F8.2 format
297 for a numeric value, A format for a string value. */
299 fmt_default_for_width (int width)
302 ? fmt_for_output (FMT_F, 8, 2)
303 : fmt_for_output (FMT_A, width, 0));
306 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
307 or an output format (otherwise) and returns nonzero if so.
308 Otherwise, emits an error message and returns zero. */
310 fmt_check (const struct fmt_spec *spec, bool for_input)
312 const char *io_fmt = for_input ? _("Input format") : _("Output format");
313 char str[FMT_STRING_LEN_MAX + 1];
314 int min_w, max_w, max_d;
316 assert (is_fmt_type (spec->type));
317 fmt_to_string (spec, str);
319 if (for_input && !fmt_usable_for_input (spec->type))
321 msg (SE, _("Format %s may not be used for input."), str);
325 if (spec->w % fmt_step_width (spec->type))
327 assert (fmt_step_width (spec->type) == 2);
328 msg (SE, _("%s specifies width %d, but %s requires an even width."),
329 str, spec->w, fmt_name (spec->type));
333 min_w = fmt_min_width (spec->type, for_input);
334 max_w = fmt_max_width (spec->type, for_input);
335 if (spec->w < min_w || spec->w > max_w)
337 msg (SE, _("%s %s specifies width %d, but "
338 "%s requires a width between %d and %d."),
339 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
343 max_d = fmt_max_decimals (spec->type, spec->w, for_input);
344 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
346 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
347 "%s does not allow any decimals.",
348 "%s %s specifies %d decimal places, but "
349 "%s does not allow any decimals.",
351 io_fmt, str, spec->d, fmt_name (spec->type));
354 else if (spec->d > max_d)
357 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
358 "the given width allows at most %d decimals.",
359 "%s %s specifies %d decimal places, but "
360 "the given width allows at most %d decimals.",
362 io_fmt, str, spec->d, max_d);
364 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
365 "the given width does not allow for any decimals.",
366 "%s %s specifies %d decimal places, but "
367 "the given width does not allow for any decimals.",
369 io_fmt, str, spec->d);
376 /* Checks whether SPEC is valid as an input format and returns
377 nonzero if so. Otherwise, emits an error message and returns
380 fmt_check_input (const struct fmt_spec *spec)
382 return fmt_check (spec, true);
385 /* Checks whether SPEC is valid as an output format and returns
386 true if so. Otherwise, emits an error message and returns false. */
388 fmt_check_output (const struct fmt_spec *spec)
390 return fmt_check (spec, false);
393 /* Checks that FORMAT is appropriate for a variable of the given
394 VAR_TYPE and returns true if so. Otherwise returns false and
395 emits an error message. */
397 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
399 assert (val_type_is_valid (var_type));
400 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
402 char str[FMT_STRING_LEN_MAX + 1];
403 msg (SE, _("%s variables are not compatible with %s format %s."),
404 var_type == VAL_STRING ? _("String") : _("Numeric"),
405 var_type == VAL_STRING ? _("numeric") : _("string"),
406 fmt_to_string (format, str));
412 /* Checks that FORMAT is appropriate for a variable of the given
413 WIDTH and returns true if so. Otherwise returns false and
414 emits an error message. */
416 fmt_check_width_compat (const struct fmt_spec *format, int width)
418 if (!fmt_check_type_compat (format, val_type_from_width (width)))
420 if (fmt_var_width (format) != width)
422 char str[FMT_STRING_LEN_MAX + 1];
423 msg (SE, _("String variable with width %d is not compatible with "
425 width, fmt_to_string (format, str));
431 /* Returns the width corresponding to FORMAT. The return value
432 is the width of the `union value's required by FORMAT. */
434 fmt_var_width (const struct fmt_spec *format)
436 return (format->type == FMT_AHEX ? format->w / 2
437 : format->type == FMT_A ? format->w
441 /* Converts F to its string representation (for instance, "F8.2")
442 in BUFFER. Returns BUFFER.
444 If F has decimals, they are included in the output string,
445 even if F's format type does not allow decimals, to allow
446 accurately presenting incorrect formats to the user. */
448 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
450 if (fmt_takes_decimals (f->type) || f->d > 0)
451 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
452 "%s%d.%d", fmt_name (f->type), f->w, f->d);
454 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
455 "%s%d", fmt_name (f->type), f->w);
459 /* Returns true if A and B are identical formats,
462 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
464 return a->type == b->type && a->w == b->w && a->d == b->d;
467 /* Adjusts FMT to be valid for a value of the given WIDTH. */
469 fmt_resize (struct fmt_spec *fmt, int width)
471 if ((width > 0) != fmt_is_string (fmt->type))
473 /* Changed from numeric to string or vice versa. Set to
474 default format for new width. */
475 *fmt = fmt_default_for_width (width);
479 /* Changed width of string. Preserve format type, adjust
481 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
489 /* Adjusts FMT's width and decimal places to be valid for an
490 input format (if FOR_INPUT) or an output format (if
493 fmt_fix (struct fmt_spec *fmt, bool for_input)
498 /* Clamp width to those allowed by format. */
499 min_w = fmt_min_width (fmt->type, for_input);
500 max_w = fmt_max_width (fmt->type, for_input);
503 else if (fmt->w > max_w)
506 /* First, if FMT has more decimal places than allowed, attempt
507 to increase FMT's width until that number of decimal places
509 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
512 for (w = fmt->w; w <= max_w; w++)
513 if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
520 /* Clamp decimals to those allowed by format and width. */
521 max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
524 else if (fmt->d > max_d)
528 /* Adjusts FMT's width and decimal places to be valid for an
531 fmt_fix_input (struct fmt_spec *fmt)
536 /* Adjusts FMT's width and decimal places to be valid for an
539 fmt_fix_output (struct fmt_spec *fmt)
541 fmt_fix (fmt, false);
544 /* Describes a display format. */
548 int min_input_width, min_output_width;
550 enum fmt_category category;
553 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
555 /* Returns the name of the given format TYPE. */
557 fmt_name (enum fmt_type type)
559 return get_fmt_desc (type)->name;
562 /* Tries to parse NAME as a format type.
563 If successful, stores the type in *TYPE and returns true.
564 On failure, returns false. */
566 fmt_from_name (const char *name, enum fmt_type *type)
570 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
571 if (!strcasecmp (name, get_fmt_desc (i)->name))
579 /* Returns true if TYPE accepts decimal places,
582 fmt_takes_decimals (enum fmt_type type)
584 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
587 /* Returns the minimum width of the given format TYPE,
588 for input if FOR_INPUT is true,
589 for output otherwise. */
591 fmt_min_width (enum fmt_type type, bool for_input)
593 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
596 /* Returns the maximum width of the given format TYPE,
597 for input if FOR_INPUT is true,
598 for output otherwise. */
600 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
602 /* Maximum width is actually invariant of whether the format is
603 for input or output, so FOR_INPUT is unused. */
604 assert (is_fmt_type (type));
622 return 2 * MAX_STRING;
629 /* Returns the maximum number of decimal places allowed for the
630 given format TYPE with a width of WIDTH places,
631 for input if FOR_INPUT is true,
632 for output otherwise. */
634 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
643 max_d = for_input ? width : width - 1;
648 max_d = for_input ? width : width - 2;
652 max_d = for_input ? width : width - 7;
670 max_d = width * 2 - 1;
679 max_d = max_digits_for_bytes (width);
732 /* Returns the minimum acceptable width for an input field
733 formatted with the given TYPE. */
735 fmt_min_input_width (enum fmt_type type)
737 return get_fmt_desc (type)->min_input_width;
740 /* Returns the maximum acceptable width for an input field
741 formatted with the given TYPE. */
743 fmt_max_input_width (enum fmt_type type)
745 return fmt_max_width (type, true);
748 /* Returns the maximum number of decimal places allowed in an
749 input field of the given TYPE and WIDTH. */
751 fmt_max_input_decimals (enum fmt_type type, int width)
753 assert (valid_width (type, width, true));
754 return fmt_max_decimals (type, width, true);
757 /* Returns the minimum acceptable width for an output field
758 formatted with the given TYPE. */
760 fmt_min_output_width (enum fmt_type type)
762 return get_fmt_desc (type)->min_output_width;
765 /* Returns the maximum acceptable width for an output field
766 formatted with the given TYPE. */
768 fmt_max_output_width (enum fmt_type type)
770 return fmt_max_width (type, false);
773 /* Returns the maximum number of decimal places allowed in an
774 output field of the given TYPE and WIDTH. */
776 fmt_max_output_decimals (enum fmt_type type, int width)
778 assert (valid_width (type, width, false));
779 return fmt_max_decimals (type, width, false);
782 /* Returns the width step for a field formatted with the given
783 TYPE. Field width must be a multiple of the width step. */
785 fmt_step_width (enum fmt_type type)
787 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
791 /* Returns true if TYPE is used for string fields,
792 false if it is used for numeric fields. */
794 fmt_is_string (enum fmt_type type)
796 return fmt_get_category (type) == FMT_CAT_STRING;
799 /* Returns true if TYPE is used for numeric fields,
800 false if it is used for string fields. */
802 fmt_is_numeric (enum fmt_type type)
804 return !fmt_is_string (type);
807 /* Returns the format TYPE's category.
808 Each format type is in exactly one category,
809 and each category's value is bitwise disjoint from every other
810 category. Thus, the return value may be tested for equality
811 or compared bitwise against a mask of FMT_CAT_* values. */
813 fmt_get_category (enum fmt_type type)
815 return get_fmt_desc (type)->category;
818 /* Returns the output format selected by default when TYPE is
819 used as an input format. */
821 fmt_input_to_output (enum fmt_type type)
823 switch (fmt_get_category (type))
830 case FMT_CAT_HEXADECIMAL:
838 /* Returns the SPSS format type corresponding to the given PSPP
841 fmt_to_io (enum fmt_type type)
843 return get_fmt_desc (type)->io;
846 /* Determines the PSPP format corresponding to the given SPSS
847 format type. If successful, sets *FMT_TYPE to the PSPP format
848 and returns true. On failure, return false. */
850 fmt_from_io (int io, enum fmt_type *fmt_type)
854 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
856 *fmt_type = FMT_##NAME; \
858 #include "format.def"
864 /* Returns true if TYPE may be used as an input format,
867 fmt_usable_for_input (enum fmt_type type)
869 assert (is_fmt_type (type));
870 return fmt_get_category (type) != FMT_CAT_CUSTOM;
873 /* For time and date formats, returns a template used for input
876 fmt_date_template (enum fmt_type type)
897 return "dd-mmm-yyyy HH:MM";
908 /* Returns true if TYPE is a valid format type,
911 is_fmt_type (enum fmt_type type)
913 return type < FMT_NUMBER_OF_FORMATS;
916 /* Returns true if WIDTH is a valid width for the given format
918 for input if FOR_INPUT is true,
919 for output otherwise. */
921 valid_width (enum fmt_type type, int width, bool for_input)
923 return (width >= fmt_min_width (type, for_input)
924 && width <= fmt_max_width (type, for_input));
927 /* Returns the maximum number of decimal digits in an unsigned
928 binary number that is BYTES bytes long. */
930 max_digits_for_bytes (int bytes)
932 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
933 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
934 return map[bytes - 1];
937 /* Sets AFFIX's string value to S. */
939 fmt_affix_set (struct fmt_affix *affix, const char *s)
941 affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
944 /* Frees data in AFFIX. */
946 fmt_affix_free (struct fmt_affix *affix)
953 fmt_number_style_init (struct fmt_number_style *style)
955 fmt_affix_set (&style->neg_prefix, "");
956 fmt_affix_set (&style->prefix, "");
957 fmt_affix_set (&style->suffix, "");
958 fmt_affix_set (&style->neg_suffix, "");
959 style->decimal = '.';
964 fmt_number_style_clone (struct fmt_number_style *new,
965 const struct fmt_number_style *old)
967 fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
968 fmt_affix_set (&new->prefix, old->prefix.s);
969 fmt_affix_set (&new->suffix, old->suffix.s);
970 fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
971 new->decimal = old->decimal;
972 new->grouping = old->grouping;
975 /* Destroys a struct fmt_number_style. */
977 fmt_number_style_destroy (struct fmt_number_style *style)
981 fmt_affix_free (&style->neg_prefix);
982 fmt_affix_free (&style->prefix);
983 fmt_affix_free (&style->suffix);
984 fmt_affix_free (&style->neg_suffix);
988 /* Returns the total width of the standard prefix and suffix for
991 fmt_affix_width (const struct fmt_number_style *style)
993 return strlen (style->prefix.s) + strlen (style->suffix.s);
996 /* Returns the total width of the negative prefix and suffix for
999 fmt_neg_affix_width (const struct fmt_number_style *style)
1001 return strlen (style->neg_prefix.s) + strlen (style->neg_suffix.s);
1004 /* Returns the struct fmt_desc for the given format TYPE. */
1005 static const struct fmt_desc *
1006 get_fmt_desc (enum fmt_type type)
1008 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1010 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1011 {#NAME, IMIN, OMIN, IO, CATEGORY},
1012 #include "format.def"
1015 assert (is_fmt_type (type));
1016 return &formats[type];
1019 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};