1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 #include <data/identifier.h>
28 #include <data/settings.h>
29 #include <data/value.h>
30 #include <data/variable.h>
31 #include <libpspp/assertion.h>
32 #include <libpspp/compiler.h>
33 #include <libpspp/message.h>
34 #include <libpspp/misc.h>
35 #include <libpspp/str.h>
41 #define _(msgid) gettext (msgid)
43 static bool is_fmt_type (enum fmt_type);
45 static int min_width (enum fmt_type, bool for_input);
46 static int max_width (enum fmt_type);
47 static bool valid_width (enum fmt_type, int width, bool for_input);
48 static int max_decimals (enum fmt_type, int width, bool for_input);
50 static int max_digits_for_bytes (int bytes);
52 /* Initialize the format module. */
56 static bool inited = false;
60 fmt_set_decimal ('.');
64 static struct fmt_number_style *styles[FMT_NUMBER_OF_FORMATS];
66 /* Deinitialize the format module. */
71 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
72 fmt_number_style_destroy (styles[t]);
75 /* Returns an input format specification with type TYPE, width W,
78 fmt_for_input (enum fmt_type type, int w, int d)
84 assert (fmt_check_input (&f));
88 /* Returns an output format specification with type TYPE, width
91 fmt_for_output (enum fmt_type type, int w, int d)
97 assert (fmt_check_output (&f));
101 /* Returns the output format specifier corresponding to input
102 format specifier INPUT. */
104 fmt_for_output_from_input (const struct fmt_spec *input)
106 struct fmt_spec output;
108 assert (fmt_check_input (input));
110 output.type = fmt_input_to_output (input->type);
112 if (output.w > fmt_max_output_width (output.type))
113 output.w = fmt_max_output_width (output.type);
114 else if (output.w < fmt_min_output_width (output.type))
115 output.w = fmt_min_output_width (output.type);
132 const struct fmt_number_style *style = fmt_get_style (input->type);
133 output.w += fmt_affix_width (style);
134 if (style->grouping != 0 && input->w - input->d >= 3)
135 output.w += (input->w - input->d - 1) / 3;
147 output.d = MAX (input->d, 3);
148 output.w = MAX (input->w, output.d + 7);
152 output.w = max_digits_for_bytes (input->w / 2) + 1;
163 output.w = 2 * input->w + (input->d > 0);
168 output.w = max_digits_for_bytes (input->w) + 1;
184 output.w = input->w / 2;
206 if (output.w > fmt_max_output_width (output.type))
207 output.w = fmt_max_output_width (output.type);
209 assert (fmt_check_output (&output));
213 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
214 or an output format (otherwise) and returns nonzero if so.
215 Otherwise, emits an error message and returns zero. */
217 fmt_check (const struct fmt_spec *spec, bool for_input)
219 const char *io_fmt = for_input ? _("Input format") : _("Output format");
220 char str[FMT_STRING_LEN_MAX + 1];
221 int min_w, max_w, max_d;
223 assert (is_fmt_type (spec->type));
224 fmt_to_string (spec, str);
226 if (for_input && !fmt_usable_for_input (spec->type))
228 msg (SE, _("Format %s may not be used for input."), str);
232 if (spec->w % fmt_step_width (spec->type))
234 assert (fmt_step_width (spec->type) == 2);
235 msg (SE, _("%s specifies width %d, but %s requires an even width."),
236 str, spec->w, fmt_name (spec->type));
240 min_w = min_width (spec->type, for_input);
241 max_w = max_width (spec->type);
242 if (spec->w < min_w || spec->w > max_w)
244 msg (SE, _("%s %s specifies width %d, but "
245 "%s requires a width between %d and %d."),
246 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
250 max_d = max_decimals (spec->type, spec->w, for_input);
251 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
253 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
254 "%s does not allow any decimals.",
255 "%s %s specifies %d decimal places, but "
256 "%s does not allow any decimals.",
258 io_fmt, str, spec->d, fmt_name (spec->type));
261 else if (spec->d > max_d)
264 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
265 "the given width allows at most %d decimals.",
266 "%s %s specifies %d decimal places, but "
267 "the given width allows at most %d decimals.",
269 io_fmt, str, spec->d, max_d);
271 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
272 "the given width does not allow for any decimals.",
273 "%s %s specifies %d decimal places, but "
274 "the given width does not allow for any decimals.",
276 io_fmt, str, spec->d);
283 /* Checks whether SPEC is valid as an input format and returns
284 nonzero if so. Otherwise, emits an error message and returns
287 fmt_check_input (const struct fmt_spec *spec)
289 return fmt_check (spec, true);
292 /* Checks whether SPEC is valid as an output format and returns
293 true if so. Otherwise, emits an error message and returns false. */
295 fmt_check_output (const struct fmt_spec *spec)
297 return fmt_check (spec, false);
300 /* Checks that FORMAT is appropriate for a variable of the given
301 TYPE and returns true if so. Otherwise returns false and
302 emits an error message. */
304 fmt_check_type_compat (const struct fmt_spec *format, enum var_type var_type)
306 assert (var_type_is_valid (var_type));
307 if ((var_type == VAR_STRING) != (fmt_is_string (format->type) != 0))
309 char str[FMT_STRING_LEN_MAX + 1];
310 msg (SE, _("%s variables are not compatible with %s format %s."),
311 var_type == VAR_STRING ? _("String") : _("Numeric"),
312 var_type == VAR_STRING ? _("numeric") : _("string"),
313 fmt_to_string (format, str));
319 /* Checks that FORMAT is appropriate for a variable of the given
320 WIDTH and returns true if so. Otherwise returns false and
321 emits an error message. */
323 fmt_check_width_compat (const struct fmt_spec *format, int width)
325 if (!fmt_check_type_compat (format, var_type_from_width (width)))
327 if (fmt_var_width (format) != width)
329 char str[FMT_STRING_LEN_MAX + 1];
330 msg (SE, _("String variable with width %d is not compatible with "
332 width, fmt_to_string (format, str));
338 /* Returns the width corresponding to the format specifier. The
339 return value is the value of the `width' member of a `struct
340 variable' for such an input format. */
342 fmt_var_width (const struct fmt_spec *spec)
344 return (spec->type == FMT_AHEX ? spec->w / 2
345 : spec->type == FMT_A ? spec->w
349 /* Converts F to its string representation (for instance, "F8.2")
350 in BUFFER. Returns BUFFER.
352 If F has decimals, they are included in the output string,
353 even if F's format type does not allow decimals, to allow
354 accurately presenting incorrect formats to the user. */
356 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
358 if (fmt_takes_decimals (f->type) || f->d > 0)
359 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
360 "%s%d.%d", fmt_name (f->type), f->w, f->d);
362 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
363 "%s%d", fmt_name (f->type), f->w);
367 /* Returns true if A and B are identical formats,
370 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
372 return a->type == b->type && a->w == b->w && a->d == b->d;
375 /* Describes a display format. */
379 int min_input_width, min_output_width;
381 enum fmt_category category;
384 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
386 /* Returns the name of the given format TYPE. */
388 fmt_name (enum fmt_type type)
390 return get_fmt_desc (type)->name;
393 /* Tries to parse NAME as a format type.
394 If successful, stores the type in *TYPE and returns true.
395 On failure, returns false. */
397 fmt_from_name (const char *name, enum fmt_type *type)
401 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
402 if (!strcasecmp (name, get_fmt_desc (i)->name))
410 /* Returns true if TYPE accepts decimal places,
413 fmt_takes_decimals (enum fmt_type type)
415 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
418 /* Returns the minimum acceptable width for an input field
419 formatted with the given TYPE. */
421 fmt_min_input_width (enum fmt_type type)
423 return get_fmt_desc (type)->min_input_width;
426 /* Returns the maximum acceptable width for an input field
427 formatted with the given TYPE. */
429 fmt_max_input_width (enum fmt_type type)
431 return max_width (type);
434 /* Returns the maximum number of decimal places allowed in an
435 input field of the given TYPE and WIDTH. */
437 fmt_max_input_decimals (enum fmt_type type, int width)
439 assert (valid_width (type, width, true));
440 return max_decimals (type, width, true);
443 /* Returns the minimum acceptable width for an output field
444 formatted with the given TYPE. */
446 fmt_min_output_width (enum fmt_type type)
448 return get_fmt_desc (type)->min_output_width;
451 /* Returns the maximum acceptable width for an output field
452 formatted with the given TYPE. */
454 fmt_max_output_width (enum fmt_type type)
456 return max_width (type);
459 /* Returns the maximum number of decimal places allowed in an
460 output field of the given TYPE and WIDTH. */
462 fmt_max_output_decimals (enum fmt_type type, int width)
464 assert (valid_width (type, width, false));
465 return max_decimals (type, width, false);
468 /* Returns the width step for a field formatted with the given
469 TYPE. Field width must be a multiple of the width step. */
471 fmt_step_width (enum fmt_type type)
473 return fmt_get_category (type) == FMT_CAT_HEXADECIMAL ? 2 : 1;
476 /* Returns true if TYPE is used for string fields,
477 false if it is used for numeric fields. */
479 fmt_is_string (enum fmt_type type)
481 return fmt_get_category (type) == FMT_CAT_STRING;
484 /* Returns true if TYPE is used for numeric fields,
485 false if it is used for string fields. */
487 fmt_is_numeric (enum fmt_type type)
489 return !fmt_is_string (type);
492 /* Returns the format TYPE's category.
493 Each format type is in exactly one category,
494 and each category's value is bitwise disjoint from every other
495 category. Thus, the return value may be tested for equality
496 or compared bitwise against a mask of FMT_CAT_* values. */
498 fmt_get_category (enum fmt_type type)
500 return get_fmt_desc (type)->category;
503 /* Returns the output format selected by default when TYPE is
504 used as an input format. */
506 fmt_input_to_output (enum fmt_type type)
508 switch (fmt_get_category (type))
515 case FMT_CAT_HEXADECIMAL:
523 /* Returns the SPSS format type corresponding to the given PSPP
526 fmt_to_io (enum fmt_type type)
528 return get_fmt_desc (type)->io;
531 /* Determines the PSPP format corresponding to the given SPSS
532 format type. If successful, sets *FMT_TYPE to the PSPP format
533 and returns true. On failure, return false. */
535 fmt_from_io (int io, enum fmt_type *fmt_type)
539 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
540 if (get_fmt_desc (type)->io == io)
548 /* Returns true if TYPE may be used as an input format,
551 fmt_usable_for_input (enum fmt_type type)
553 assert (is_fmt_type (type));
554 return fmt_get_category (type) != FMT_CAT_CUSTOM;
557 /* For time and date formats, returns a template used for input
560 fmt_date_template (enum fmt_type type)
581 return "dd-mmm-yyyy HH:MM";
591 /* Returns a string of the form "$#,###.##" according to FMT,
592 which must be of type FMT_DOLLAR. The caller must free the
595 fmt_dollar_template (const struct fmt_spec *fmt)
597 struct string s = DS_EMPTY_INITIALIZER;
600 assert (fmt->type == FMT_DOLLAR);
602 ds_put_char (&s, '$');
603 for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
605 ds_put_char (&s, '#');
606 if (--c % 4 == 0 && c > 0)
608 ds_put_char (&s, fmt_grouping_char (fmt->type));
614 ds_put_char (&s, fmt_decimal_char (fmt->type));
615 ds_put_char_multiple (&s, '#', fmt->d);
621 /* Returns true if TYPE is a valid format type,
624 is_fmt_type (enum fmt_type type)
626 return type < FMT_NUMBER_OF_FORMATS;
629 /* Returns the minimum width of the given format TYPE,
630 for input if FOR_INPUT is true,
631 for output otherwise. */
633 min_width (enum fmt_type type, bool for_input)
635 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
638 /* Returns the maximum width of the given format TYPE,
639 which is invariant between input and output. */
641 max_width (enum fmt_type type)
643 assert (is_fmt_type (type));
661 return 2 * MAX_STRING;
668 /* Returns true if WIDTH is a valid width for the given format
670 for input if FOR_INPUT is true,
671 for output otherwise. */
673 valid_width (enum fmt_type type, int width, bool for_input)
675 return (width >= min_width (type, for_input)
676 && width <= max_width (type));
679 /* Returns the maximum number of decimal places allowed for the
680 given format TYPE with a width of WIDTH places,
681 for input if FOR_INPUT is true,
682 for output otherwise. */
684 max_decimals (enum fmt_type type, int width, bool for_input)
693 max_d = for_input ? width : width - 1;
698 max_d = for_input ? width : width - 2;
702 max_d = for_input ? width : width - 7;
720 max_d = width * 2 - 1;
729 max_d = max_digits_for_bytes (width);
782 /* Returns the maximum number of decimal digits in an unsigned
783 binary number that is BYTES bytes long. */
785 max_digits_for_bytes (int bytes)
787 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
788 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
789 return map[bytes - 1];
793 /* Creates and returns a new struct fmt_number_style,
794 initializing all affixes to empty strings. */
795 struct fmt_number_style *
796 fmt_number_style_create (void)
798 struct fmt_number_style *style = xmalloc (sizeof *style);
799 style->neg_prefix = ss_empty ();
800 style->prefix = ss_empty ();
801 style->suffix = ss_empty ();
802 style->neg_suffix = ss_empty ();
803 style->decimal = '.';
808 /* Destroys a struct fmt_number_style. */
810 fmt_number_style_destroy (struct fmt_number_style *style)
814 ss_dealloc (&style->neg_prefix);
815 ss_dealloc (&style->prefix);
816 ss_dealloc (&style->suffix);
817 ss_dealloc (&style->neg_suffix);
822 /* Returns the number formatting style associated with the given
824 const struct fmt_number_style *
825 fmt_get_style (enum fmt_type type)
827 assert (is_fmt_type (type));
828 assert (styles[type] != NULL);
832 /* Sets STYLE as the number formatting style associated with the
833 given format TYPE, transferring ownership of STYLE. */
835 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
837 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
838 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
839 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
840 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
841 assert (style->decimal == '.' || style->decimal == ',');
842 assert (style->grouping != style->decimal
843 && (style->grouping == '.' || style->grouping == ','
844 || style->grouping == 0));
846 assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
847 assert (styles[type] != NULL);
849 fmt_number_style_destroy (styles[type]);
850 styles[type] = style;
853 /* Returns the total width of the standard prefix and suffix for
856 fmt_affix_width (const struct fmt_number_style *style)
858 return ss_length (style->prefix) + ss_length (style->suffix);
861 /* Returns the total width of the negative prefix and suffix for
864 fmt_neg_affix_width (const struct fmt_number_style *style)
866 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
869 /* Returns the decimal point character for the given format
872 fmt_decimal_char (enum fmt_type type)
874 return fmt_get_style (type)->decimal;
877 /* Returns the grouping character for the given format TYPE, or 0
878 if the format type does not group digits. */
880 fmt_grouping_char (enum fmt_type type)
882 return fmt_get_style (type)->grouping;
885 /* Sets the number style for TYPE to have the given standard
886 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
887 suffix, DECIMAL as the decimal point character, and GROUPING
888 as the grouping character. */
890 set_style (enum fmt_type type,
891 const char *prefix, const char *suffix,
892 char decimal, char grouping)
894 struct fmt_number_style *style;
896 assert (is_fmt_type (type));
898 fmt_number_style_destroy (styles[type]);
900 style = styles[type] = fmt_number_style_create ();
901 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
902 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
903 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
904 style->decimal = decimal;
905 style->grouping = grouping;
908 /* Sets the number style for TYPE as with set_style, but only if
909 TYPE has not already been initialized. */
911 init_style (enum fmt_type type,
912 const char *prefix, const char *suffix,
913 char decimal, char grouping)
915 assert (is_fmt_type (type));
916 if (styles[type] == NULL)
917 set_style (type, prefix, suffix, decimal, grouping);
920 /* Sets the decimal point character to DECIMAL. */
922 fmt_set_decimal (char decimal)
924 int grouping = decimal == '.' ? ',' : '.';
925 assert (decimal == '.' || decimal == ',');
927 set_style (FMT_F, "", "", decimal, 0);
928 set_style (FMT_E, "", "", decimal, 0);
929 set_style (FMT_COMMA, "", "", decimal, grouping);
930 set_style (FMT_DOT, "", "", grouping, decimal);
931 set_style (FMT_DOLLAR, "$", "", decimal, grouping);
932 set_style (FMT_PCT, "", "%", decimal, 0);
934 init_style (FMT_CCA, "", "", decimal, grouping);
935 init_style (FMT_CCB, "", "", decimal, grouping);
936 init_style (FMT_CCC, "", "", decimal, grouping);
937 init_style (FMT_CCD, "", "", decimal, grouping);
938 init_style (FMT_CCE, "", "", decimal, grouping);
941 /* Returns the struct fmt_desc for the given format TYPE. */
942 static const struct fmt_desc *
943 get_fmt_desc (enum fmt_type type)
945 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
947 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
948 {#NAME, IMIN, OMIN, IO, CATEGORY},
949 #include "format.def"
952 assert (is_fmt_type (type));
953 return &formats[type];