1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 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, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 #include <data/identifier.h>
27 #include <data/settings.h>
28 #include <data/value.h>
29 #include <data/variable.h>
30 #include <libpspp/assertion.h>
31 #include <libpspp/compiler.h>
32 #include <libpspp/message.h>
33 #include <libpspp/misc.h>
34 #include <libpspp/str.h>
40 #define _(msgid) gettext (msgid)
42 static bool is_fmt_type (enum fmt_type);
44 static int min_width (enum fmt_type, bool for_input);
45 static int max_width (enum fmt_type);
46 static bool valid_width (enum fmt_type, int width, bool for_input);
47 static int max_decimals (enum fmt_type, int width, bool for_input);
49 static int max_digits_for_bytes (int bytes);
51 /* Initialize the format module. */
55 static bool inited = false;
59 fmt_set_decimal ('.');
63 static struct fmt_number_style *styles[FMT_NUMBER_OF_FORMATS];
65 /* Deinitialize the format module. */
70 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
71 fmt_number_style_destroy (styles[t]);
74 /* Returns an input format specification with type TYPE, width W,
77 fmt_for_input (enum fmt_type type, int w, int d)
83 assert (fmt_check_input (&f));
87 /* Returns an output format specification with type TYPE, width
90 fmt_for_output (enum fmt_type type, int w, int d)
96 assert (fmt_check_output (&f));
100 /* Returns the output format specifier corresponding to input
101 format specifier INPUT. */
103 fmt_for_output_from_input (const struct fmt_spec *input)
105 struct fmt_spec output;
107 assert (fmt_check_input (input));
109 output.type = fmt_input_to_output (input->type);
111 if (output.w > fmt_max_output_width (output.type))
112 output.w = fmt_max_output_width (output.type);
113 else if (output.w < fmt_min_output_width (output.type))
114 output.w = fmt_min_output_width (output.type);
131 const struct fmt_number_style *style = fmt_get_style (input->type);
132 output.w += fmt_affix_width (style);
133 if (style->grouping != 0 && input->w - input->d >= 3)
134 output.w += (input->w - input->d - 1) / 3;
146 output.d = MAX (input->d, 3);
147 output.w = MAX (input->w, output.d + 7);
151 output.w = max_digits_for_bytes (input->w / 2) + 1;
162 output.w = 2 * input->w + (input->d > 0);
167 output.w = max_digits_for_bytes (input->w) + 1;
183 output.w = input->w / 2;
205 if (output.w > fmt_max_output_width (output.type))
206 output.w = fmt_max_output_width (output.type);
208 assert (fmt_check_output (&output));
212 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
213 or an output format (otherwise) and returns nonzero if so.
214 Otherwise, emits an error message and returns zero. */
216 fmt_check (const struct fmt_spec *spec, bool for_input)
218 const char *io_fmt = for_input ? _("Input format") : _("Output format");
219 char str[FMT_STRING_LEN_MAX + 1];
220 int min_w, max_w, max_d;
222 assert (is_fmt_type (spec->type));
223 fmt_to_string (spec, str);
225 if (for_input && !fmt_usable_for_input (spec->type))
227 msg (SE, _("Format %s may not be used for input."), str);
231 if (spec->w % fmt_step_width (spec->type))
233 assert (fmt_step_width (spec->type) == 2);
234 msg (SE, _("%s specifies width %d, but %s requires an even width."),
235 str, spec->w, fmt_name (spec->type));
239 min_w = min_width (spec->type, for_input);
240 max_w = max_width (spec->type);
241 if (spec->w < min_w || spec->w > max_w)
243 msg (SE, _("%s %s specifies width %d, but "
244 "%s requires a width between %d and %d."),
245 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
249 max_d = max_decimals (spec->type, spec->w, for_input);
250 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
252 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
253 "%s does not allow any decimals.",
254 "%s %s specifies %d decimal places, but "
255 "%s does not allow any decimals.",
257 io_fmt, str, spec->d, fmt_name (spec->type));
260 else if (spec->d > max_d)
263 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
264 "the given width allows at most %d decimals.",
265 "%s %s specifies %d decimal places, but "
266 "the given width allows at most %d decimals.",
268 io_fmt, str, spec->d, max_d);
270 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
271 "the given width does not allow for any decimals.",
272 "%s %s specifies %d decimal places, but "
273 "the given width does not allow for any decimals.",
275 io_fmt, str, spec->d);
282 /* Checks whether SPEC is valid as an input format and returns
283 nonzero if so. Otherwise, emits an error message and returns
286 fmt_check_input (const struct fmt_spec *spec)
288 return fmt_check (spec, true);
291 /* Checks whether SPEC is valid as an output format and returns
292 true if so. Otherwise, emits an error message and returns false. */
294 fmt_check_output (const struct fmt_spec *spec)
296 return fmt_check (spec, false);
299 /* Checks that FORMAT is appropriate for a variable of the given
300 TYPE and returns true if so. Otherwise returns false and
301 emits an error message. */
303 fmt_check_type_compat (const struct fmt_spec *format, enum var_type var_type)
305 assert (var_type_is_valid (var_type));
306 if ((var_type == VAR_STRING) != (fmt_is_string (format->type) != 0))
308 char str[FMT_STRING_LEN_MAX + 1];
309 msg (SE, _("%s variables are not compatible with %s format %s."),
310 var_type == VAR_STRING ? _("String") : _("Numeric"),
311 var_type == VAR_STRING ? _("numeric") : _("string"),
312 fmt_to_string (format, str));
318 /* Checks that FORMAT is appropriate for a variable of the given
319 WIDTH and returns true if so. Otherwise returns false and
320 emits an error message. */
322 fmt_check_width_compat (const struct fmt_spec *format, int width)
324 if (!fmt_check_type_compat (format, var_type_from_width (width)))
326 if (fmt_var_width (format) != width)
328 char str[FMT_STRING_LEN_MAX + 1];
329 msg (SE, _("String variable with width %d is not compatible with "
331 width, fmt_to_string (format, str));
337 /* Returns the width corresponding to the format specifier. The
338 return value is the value of the `width' member of a `struct
339 variable' for such an input format. */
341 fmt_var_width (const struct fmt_spec *spec)
343 return (spec->type == FMT_AHEX ? spec->w / 2
344 : spec->type == FMT_A ? spec->w
348 /* Converts F to its string representation (for instance, "F8.2")
349 in BUFFER. Returns BUFFER.
351 If F has decimals, they are included in the output string,
352 even if F's format type does not allow decimals, to allow
353 accurately presenting incorrect formats to the user. */
355 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
357 if (fmt_takes_decimals (f->type) || f->d > 0)
358 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
359 "%s%d.%d", fmt_name (f->type), f->w, f->d);
361 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
362 "%s%d", fmt_name (f->type), f->w);
366 /* Returns true if A and B are identical formats,
369 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
371 return a->type == b->type && a->w == b->w && a->d == b->d;
374 /* Describes a display format. */
378 int min_input_width, min_output_width;
380 enum fmt_category category;
383 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
385 /* Returns the name of the given format TYPE. */
387 fmt_name (enum fmt_type type)
389 return get_fmt_desc (type)->name;
392 /* Tries to parse NAME as a format type.
393 If successful, stores the type in *TYPE and returns true.
394 On failure, returns false. */
396 fmt_from_name (const char *name, enum fmt_type *type)
400 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
401 if (!strcasecmp (name, get_fmt_desc (i)->name))
409 /* Returns true if TYPE accepts decimal places,
412 fmt_takes_decimals (enum fmt_type type)
414 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
417 /* Returns the minimum acceptable width for an input field
418 formatted with the given TYPE. */
420 fmt_min_input_width (enum fmt_type type)
422 return get_fmt_desc (type)->min_input_width;
425 /* Returns the maximum acceptable width for an input field
426 formatted with the given TYPE. */
428 fmt_max_input_width (enum fmt_type type)
430 return max_width (type);
433 /* Returns the maximum number of decimal places allowed in an
434 input field of the given TYPE and WIDTH. */
436 fmt_max_input_decimals (enum fmt_type type, int width)
438 assert (valid_width (type, width, true));
439 return max_decimals (type, width, true);
442 /* Returns the minimum acceptable width for an output field
443 formatted with the given TYPE. */
445 fmt_min_output_width (enum fmt_type type)
447 return get_fmt_desc (type)->min_output_width;
450 /* Returns the maximum acceptable width for an output field
451 formatted with the given TYPE. */
453 fmt_max_output_width (enum fmt_type type)
455 return max_width (type);
458 /* Returns the maximum number of decimal places allowed in an
459 output field of the given TYPE and WIDTH. */
461 fmt_max_output_decimals (enum fmt_type type, int width)
463 assert (valid_width (type, width, false));
464 return max_decimals (type, width, false);
467 /* Returns the width step for a field formatted with the given
468 TYPE. Field width must be a multiple of the width step. */
470 fmt_step_width (enum fmt_type type)
472 return fmt_get_category (type) == FMT_CAT_HEXADECIMAL ? 2 : 1;
475 /* Returns true if TYPE is used for string fields,
476 false if it is used for numeric fields. */
478 fmt_is_string (enum fmt_type type)
480 return fmt_get_category (type) == FMT_CAT_STRING;
483 /* Returns true if TYPE is used for numeric fields,
484 false if it is used for string fields. */
486 fmt_is_numeric (enum fmt_type type)
488 return !fmt_is_string (type);
491 /* Returns the format TYPE's category.
492 Each format type is in exactly one category,
493 and each category's value is bitwise disjoint from every other
494 category. Thus, the return value may be tested for equality
495 or compared bitwise against a mask of FMT_CAT_* values. */
497 fmt_get_category (enum fmt_type type)
499 return get_fmt_desc (type)->category;
502 /* Returns the output format selected by default when TYPE is
503 used as an input format. */
505 fmt_input_to_output (enum fmt_type type)
507 switch (fmt_get_category (type))
514 case FMT_CAT_HEXADECIMAL:
522 /* Returns the SPSS format type corresponding to the given PSPP
525 fmt_to_io (enum fmt_type type)
527 return get_fmt_desc (type)->io;
530 /* Determines the PSPP format corresponding to the given SPSS
531 format type. If successful, sets *FMT_TYPE to the PSPP format
532 and returns true. On failure, return false. */
534 fmt_from_io (int io, enum fmt_type *fmt_type)
538 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
539 if (get_fmt_desc (type)->io == io)
547 /* Returns true if TYPE may be used as an input format,
550 fmt_usable_for_input (enum fmt_type type)
552 assert (is_fmt_type (type));
553 return fmt_get_category (type) != FMT_CAT_CUSTOM;
556 /* For time and date formats, returns a template used for input
559 fmt_date_template (enum fmt_type type)
580 return "dd-mmm-yyyy HH:MM";
590 /* Returns a string of the form "$#,###.##" according to FMT,
591 which must be of type FMT_DOLLAR. The caller must free the
594 fmt_dollar_template (const struct fmt_spec *fmt)
596 struct string s = DS_EMPTY_INITIALIZER;
599 assert (fmt->type == FMT_DOLLAR);
601 ds_put_char (&s, '$');
602 for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
604 ds_put_char (&s, '#');
605 if (--c % 4 == 0 && c > 0)
607 ds_put_char (&s, fmt_grouping_char (fmt->type));
613 ds_put_char (&s, fmt_decimal_char (fmt->type));
614 ds_put_char_multiple (&s, '#', fmt->d);
620 /* Returns true if TYPE is a valid format type,
623 is_fmt_type (enum fmt_type type)
625 return type < FMT_NUMBER_OF_FORMATS;
628 /* Returns the minimum width of the given format TYPE,
629 for input if FOR_INPUT is true,
630 for output otherwise. */
632 min_width (enum fmt_type type, bool for_input)
634 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
637 /* Returns the maximum width of the given format TYPE,
638 which is invariant between input and output. */
640 max_width (enum fmt_type type)
642 assert (is_fmt_type (type));
660 return 2 * MAX_STRING;
667 /* Returns true if WIDTH is a valid width for the given format
669 for input if FOR_INPUT is true,
670 for output otherwise. */
672 valid_width (enum fmt_type type, int width, bool for_input)
674 return (width >= min_width (type, for_input)
675 && width <= max_width (type));
678 /* Returns the maximum number of decimal places allowed for the
679 given format TYPE with a width of WIDTH places,
680 for input if FOR_INPUT is true,
681 for output otherwise. */
683 max_decimals (enum fmt_type type, int width, bool for_input)
692 max_d = for_input ? width : width - 1;
697 max_d = for_input ? width : width - 2;
701 max_d = for_input ? width : width - 7;
719 max_d = width * 2 - 1;
728 max_d = max_digits_for_bytes (width);
781 /* Returns the maximum number of decimal digits in an unsigned
782 binary number that is BYTES bytes long. */
784 max_digits_for_bytes (int bytes)
786 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
787 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
788 return map[bytes - 1];
792 /* Creates and returns a new struct fmt_number_style,
793 initializing all affixes to empty strings. */
794 struct fmt_number_style *
795 fmt_number_style_create (void)
797 struct fmt_number_style *style = xmalloc (sizeof *style);
798 style->neg_prefix = ss_empty ();
799 style->prefix = ss_empty ();
800 style->suffix = ss_empty ();
801 style->neg_suffix = ss_empty ();
802 style->decimal = '.';
807 /* Destroys a struct fmt_number_style. */
809 fmt_number_style_destroy (struct fmt_number_style *style)
813 ss_dealloc (&style->neg_prefix);
814 ss_dealloc (&style->prefix);
815 ss_dealloc (&style->suffix);
816 ss_dealloc (&style->neg_suffix);
821 /* Returns the number formatting style associated with the given
823 const struct fmt_number_style *
824 fmt_get_style (enum fmt_type type)
826 assert (is_fmt_type (type));
827 assert (styles[type] != NULL);
831 /* Sets STYLE as the number formatting style associated with the
832 given format TYPE, transferring ownership of STYLE. */
834 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
836 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
837 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
838 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
839 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
840 assert (style->decimal == '.' || style->decimal == ',');
841 assert (style->grouping != style->decimal
842 && (style->grouping == '.' || style->grouping == ','
843 || style->grouping == 0));
845 assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
846 assert (styles[type] != NULL);
848 fmt_number_style_destroy (styles[type]);
849 styles[type] = style;
852 /* Returns the total width of the standard prefix and suffix for
855 fmt_affix_width (const struct fmt_number_style *style)
857 return ss_length (style->prefix) + ss_length (style->suffix);
860 /* Returns the total width of the negative prefix and suffix for
863 fmt_neg_affix_width (const struct fmt_number_style *style)
865 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
868 /* Returns the decimal point character for the given format
871 fmt_decimal_char (enum fmt_type type)
873 return fmt_get_style (type)->decimal;
876 /* Returns the grouping character for the given format TYPE, or 0
877 if the format type does not group digits. */
879 fmt_grouping_char (enum fmt_type type)
881 return fmt_get_style (type)->grouping;
884 /* Sets the number style for TYPE to have the given standard
885 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
886 suffix, DECIMAL as the decimal point character, and GROUPING
887 as the grouping character. */
889 set_style (enum fmt_type type,
890 const char *prefix, const char *suffix,
891 char decimal, char grouping)
893 struct fmt_number_style *style;
895 assert (is_fmt_type (type));
897 fmt_number_style_destroy (styles[type]);
899 style = styles[type] = fmt_number_style_create ();
900 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
901 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
902 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
903 style->decimal = decimal;
904 style->grouping = grouping;
907 /* Sets the number style for TYPE as with set_style, but only if
908 TYPE has not already been initialized. */
910 init_style (enum fmt_type type,
911 const char *prefix, const char *suffix,
912 char decimal, char grouping)
914 assert (is_fmt_type (type));
915 if (styles[type] == NULL)
916 set_style (type, prefix, suffix, decimal, grouping);
919 /* Sets the decimal point character to DECIMAL. */
921 fmt_set_decimal (char decimal)
923 int grouping = decimal == '.' ? ',' : '.';
924 assert (decimal == '.' || decimal == ',');
926 set_style (FMT_F, "", "", decimal, 0);
927 set_style (FMT_E, "", "", decimal, 0);
928 set_style (FMT_COMMA, "", "", decimal, grouping);
929 set_style (FMT_DOT, "", "", grouping, decimal);
930 set_style (FMT_DOLLAR, "$", "", decimal, grouping);
931 set_style (FMT_PCT, "", "%", decimal, 0);
933 init_style (FMT_CCA, "", "", decimal, grouping);
934 init_style (FMT_CCB, "", "", decimal, grouping);
935 init_style (FMT_CCC, "", "", decimal, grouping);
936 init_style (FMT_CCD, "", "", decimal, grouping);
937 init_style (FMT_CCE, "", "", decimal, grouping);
940 /* Returns the struct fmt_desc for the given format TYPE. */
941 static const struct fmt_desc *
942 get_fmt_desc (enum fmt_type type)
944 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
946 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
947 {#NAME, IMIN, OMIN, IO, CATEGORY},
948 #include "format.def"
951 assert (is_fmt_type (type));
952 return &formats[type];