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)
40 static bool is_fmt_type (enum fmt_type);
42 static int min_width (enum fmt_type, bool for_input);
43 static int max_width (enum fmt_type);
44 static bool valid_width (enum fmt_type, int width, bool for_input);
45 static int max_decimals (enum fmt_type, int width, bool for_input);
47 static int max_digits_for_bytes (int bytes);
49 /* Initialize the format module. */
53 static bool inited = false;
57 fmt_set_decimal ('.');
61 static struct fmt_number_style *styles[FMT_NUMBER_OF_FORMATS];
63 /* Deinitialize the format module. */
68 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
69 fmt_number_style_destroy (styles[t]);
72 /* Returns an input format specification with type TYPE, width W,
75 fmt_for_input (enum fmt_type type, int w, int d)
81 assert (fmt_check_input (&f));
85 /* Returns an output format specification with type TYPE, width
88 fmt_for_output (enum fmt_type type, int w, int d)
94 assert (fmt_check_output (&f));
98 /* Returns the output format specifier corresponding to input
99 format specifier INPUT. */
101 fmt_for_output_from_input (const struct fmt_spec *input)
103 struct fmt_spec output;
105 assert (fmt_check_input (input));
107 output.type = fmt_input_to_output (input->type);
109 if (output.w > fmt_max_output_width (output.type))
110 output.w = fmt_max_output_width (output.type);
111 else if (output.w < fmt_min_output_width (output.type))
112 output.w = fmt_min_output_width (output.type);
129 const struct fmt_number_style *style = fmt_get_style (input->type);
130 output.w += fmt_affix_width (style);
131 if (style->grouping != 0 && input->w - input->d >= 3)
132 output.w += (input->w - input->d - 1) / 3;
144 output.d = MAX (input->d, 3);
145 output.w = MAX (input->w, output.d + 7);
149 output.w = max_digits_for_bytes (input->w / 2) + 1;
160 output.w = 2 * input->w + (input->d > 0);
165 output.w = max_digits_for_bytes (input->w) + 1;
181 output.w = input->w / 2;
203 if (output.w > fmt_max_output_width (output.type))
204 output.w = fmt_max_output_width (output.type);
206 assert (fmt_check_output (&output));
210 /* Returns the default format for the given WIDTH: F8.2 format
211 for a numeric value, A format for a string value. */
213 fmt_default_for_width (int width)
216 ? fmt_for_output (FMT_F, 8, 2)
217 : fmt_for_output (FMT_A, width, 0));
220 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
221 or an output format (otherwise) and returns nonzero if so.
222 Otherwise, emits an error message and returns zero. */
224 fmt_check (const struct fmt_spec *spec, bool for_input)
226 const char *io_fmt = for_input ? _("Input format") : _("Output format");
227 char str[FMT_STRING_LEN_MAX + 1];
228 int min_w, max_w, max_d;
230 assert (is_fmt_type (spec->type));
231 fmt_to_string (spec, str);
233 if (for_input && !fmt_usable_for_input (spec->type))
235 msg (SE, _("Format %s may not be used for input."), str);
239 if (spec->w % fmt_step_width (spec->type))
241 assert (fmt_step_width (spec->type) == 2);
242 msg (SE, _("%s specifies width %d, but %s requires an even width."),
243 str, spec->w, fmt_name (spec->type));
247 min_w = min_width (spec->type, for_input);
248 max_w = max_width (spec->type);
249 if (spec->w < min_w || spec->w > max_w)
251 msg (SE, _("%s %s specifies width %d, but "
252 "%s requires a width between %d and %d."),
253 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
257 max_d = max_decimals (spec->type, spec->w, for_input);
258 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
260 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
261 "%s does not allow any decimals.",
262 "%s %s specifies %d decimal places, but "
263 "%s does not allow any decimals.",
265 io_fmt, str, spec->d, fmt_name (spec->type));
268 else if (spec->d > max_d)
271 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
272 "the given width allows at most %d decimals.",
273 "%s %s specifies %d decimal places, but "
274 "the given width allows at most %d decimals.",
276 io_fmt, str, spec->d, max_d);
278 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
279 "the given width does not allow for any decimals.",
280 "%s %s specifies %d decimal places, but "
281 "the given width does not allow for any decimals.",
283 io_fmt, str, spec->d);
290 /* Checks whether SPEC is valid as an input format and returns
291 nonzero if so. Otherwise, emits an error message and returns
294 fmt_check_input (const struct fmt_spec *spec)
296 return fmt_check (spec, true);
299 /* Checks whether SPEC is valid as an output format and returns
300 true if so. Otherwise, emits an error message and returns false. */
302 fmt_check_output (const struct fmt_spec *spec)
304 return fmt_check (spec, false);
307 /* Checks that FORMAT is appropriate for a variable of the given
308 TYPE and returns true if so. Otherwise returns false and
309 emits an error message. */
311 fmt_check_type_compat (const struct fmt_spec *format, enum var_type var_type)
313 assert (var_type_is_valid (var_type));
314 if ((var_type == VAR_STRING) != (fmt_is_string (format->type) != 0))
316 char str[FMT_STRING_LEN_MAX + 1];
317 msg (SE, _("%s variables are not compatible with %s format %s."),
318 var_type == VAR_STRING ? _("String") : _("Numeric"),
319 var_type == VAR_STRING ? _("numeric") : _("string"),
320 fmt_to_string (format, str));
326 /* Checks that FORMAT is appropriate for a variable of the given
327 WIDTH and returns true if so. Otherwise returns false and
328 emits an error message. */
330 fmt_check_width_compat (const struct fmt_spec *format, int width)
332 if (!fmt_check_type_compat (format, var_type_from_width (width)))
334 if (fmt_var_width (format) != width)
336 char str[FMT_STRING_LEN_MAX + 1];
337 msg (SE, _("String variable with width %d is not compatible with "
339 width, fmt_to_string (format, str));
345 /* Returns the width corresponding to the format specifier. The
346 return value is the value of the `width' member of a `struct
347 variable' for such an input format. */
349 fmt_var_width (const struct fmt_spec *spec)
351 return (spec->type == FMT_AHEX ? spec->w / 2
352 : spec->type == FMT_A ? spec->w
356 /* Converts F to its string representation (for instance, "F8.2")
357 in BUFFER. Returns BUFFER.
359 If F has decimals, they are included in the output string,
360 even if F's format type does not allow decimals, to allow
361 accurately presenting incorrect formats to the user. */
363 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
365 if (fmt_takes_decimals (f->type) || f->d > 0)
366 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
367 "%s%d.%d", fmt_name (f->type), f->w, f->d);
369 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
370 "%s%d", fmt_name (f->type), f->w);
374 /* Returns true if A and B are identical formats,
377 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
379 return a->type == b->type && a->w == b->w && a->d == b->d;
382 /* Adjusts FMT to be valid for a value of the given WIDTH. */
384 fmt_resize (struct fmt_spec *fmt, int width)
386 if ((width > 0) != fmt_is_string (fmt->type))
388 /* Changed from numeric to string or vice versa. Set to
389 default format for new width. */
390 *fmt = fmt_default_for_width (width);
394 /* Changed width of string. Preserve format type, adjust
396 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
404 /* Describes a display format. */
408 int min_input_width, min_output_width;
410 enum fmt_category category;
413 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
415 /* Returns the name of the given format TYPE. */
417 fmt_name (enum fmt_type type)
419 return get_fmt_desc (type)->name;
422 /* Tries to parse NAME as a format type.
423 If successful, stores the type in *TYPE and returns true.
424 On failure, returns false. */
426 fmt_from_name (const char *name, enum fmt_type *type)
430 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
431 if (!strcasecmp (name, get_fmt_desc (i)->name))
439 /* Returns true if TYPE accepts decimal places,
442 fmt_takes_decimals (enum fmt_type type)
444 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
447 /* Returns the minimum acceptable width for an input field
448 formatted with the given TYPE. */
450 fmt_min_input_width (enum fmt_type type)
452 return get_fmt_desc (type)->min_input_width;
455 /* Returns the maximum acceptable width for an input field
456 formatted with the given TYPE. */
458 fmt_max_input_width (enum fmt_type type)
460 return max_width (type);
463 /* Returns the maximum number of decimal places allowed in an
464 input field of the given TYPE and WIDTH. */
466 fmt_max_input_decimals (enum fmt_type type, int width)
468 assert (valid_width (type, width, true));
469 return max_decimals (type, width, true);
472 /* Returns the minimum acceptable width for an output field
473 formatted with the given TYPE. */
475 fmt_min_output_width (enum fmt_type type)
477 return get_fmt_desc (type)->min_output_width;
480 /* Returns the maximum acceptable width for an output field
481 formatted with the given TYPE. */
483 fmt_max_output_width (enum fmt_type type)
485 return max_width (type);
488 /* Returns the maximum number of decimal places allowed in an
489 output field of the given TYPE and WIDTH. */
491 fmt_max_output_decimals (enum fmt_type type, int width)
493 assert (valid_width (type, width, false));
494 return max_decimals (type, width, false);
497 /* Returns the width step for a field formatted with the given
498 TYPE. Field width must be a multiple of the width step. */
500 fmt_step_width (enum fmt_type type)
502 return fmt_get_category (type) == FMT_CAT_HEXADECIMAL ? 2 : 1;
505 /* Returns true if TYPE is used for string fields,
506 false if it is used for numeric fields. */
508 fmt_is_string (enum fmt_type type)
510 return fmt_get_category (type) == FMT_CAT_STRING;
513 /* Returns true if TYPE is used for numeric fields,
514 false if it is used for string fields. */
516 fmt_is_numeric (enum fmt_type type)
518 return !fmt_is_string (type);
521 /* Returns the format TYPE's category.
522 Each format type is in exactly one category,
523 and each category's value is bitwise disjoint from every other
524 category. Thus, the return value may be tested for equality
525 or compared bitwise against a mask of FMT_CAT_* values. */
527 fmt_get_category (enum fmt_type type)
529 return get_fmt_desc (type)->category;
532 /* Returns the output format selected by default when TYPE is
533 used as an input format. */
535 fmt_input_to_output (enum fmt_type type)
537 switch (fmt_get_category (type))
544 case FMT_CAT_HEXADECIMAL:
552 /* Returns the SPSS format type corresponding to the given PSPP
555 fmt_to_io (enum fmt_type type)
557 return get_fmt_desc (type)->io;
560 /* Determines the PSPP format corresponding to the given SPSS
561 format type. If successful, sets *FMT_TYPE to the PSPP format
562 and returns true. On failure, return false. */
564 fmt_from_io (int io, enum fmt_type *fmt_type)
568 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
569 if (get_fmt_desc (type)->io == io)
577 /* Returns true if TYPE may be used as an input format,
580 fmt_usable_for_input (enum fmt_type type)
582 assert (is_fmt_type (type));
583 return fmt_get_category (type) != FMT_CAT_CUSTOM;
586 /* For time and date formats, returns a template used for input
589 fmt_date_template (enum fmt_type type)
610 return "dd-mmm-yyyy HH:MM";
620 /* Returns a string of the form "$#,###.##" according to FMT,
621 which must be of type FMT_DOLLAR. The caller must free the
624 fmt_dollar_template (const struct fmt_spec *fmt)
626 struct string s = DS_EMPTY_INITIALIZER;
629 assert (fmt->type == FMT_DOLLAR);
631 ds_put_char (&s, '$');
632 for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
634 ds_put_char (&s, '#');
635 if (--c % 4 == 0 && c > 0)
637 ds_put_char (&s, fmt_grouping_char (fmt->type));
643 ds_put_char (&s, fmt_decimal_char (fmt->type));
644 ds_put_char_multiple (&s, '#', fmt->d);
650 /* Returns true if TYPE is a valid format type,
653 is_fmt_type (enum fmt_type type)
655 return type < FMT_NUMBER_OF_FORMATS;
658 /* Returns the minimum width of the given format TYPE,
659 for input if FOR_INPUT is true,
660 for output otherwise. */
662 min_width (enum fmt_type type, bool for_input)
664 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
667 /* Returns the maximum width of the given format TYPE,
668 which is invariant between input and output. */
670 max_width (enum fmt_type type)
672 assert (is_fmt_type (type));
690 return 2 * MAX_STRING;
697 /* Returns true if WIDTH is a valid width for the given format
699 for input if FOR_INPUT is true,
700 for output otherwise. */
702 valid_width (enum fmt_type type, int width, bool for_input)
704 return (width >= min_width (type, for_input)
705 && width <= max_width (type));
708 /* Returns the maximum number of decimal places allowed for the
709 given format TYPE with a width of WIDTH places,
710 for input if FOR_INPUT is true,
711 for output otherwise. */
713 max_decimals (enum fmt_type type, int width, bool for_input)
722 max_d = for_input ? width : width - 1;
727 max_d = for_input ? width : width - 2;
731 max_d = for_input ? width : width - 7;
749 max_d = width * 2 - 1;
758 max_d = max_digits_for_bytes (width);
811 /* Returns the maximum number of decimal digits in an unsigned
812 binary number that is BYTES bytes long. */
814 max_digits_for_bytes (int bytes)
816 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
817 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
818 return map[bytes - 1];
822 /* Creates and returns a new struct fmt_number_style,
823 initializing all affixes to empty strings. */
824 struct fmt_number_style *
825 fmt_number_style_create (void)
827 struct fmt_number_style *style = xmalloc (sizeof *style);
828 style->neg_prefix = ss_empty ();
829 style->prefix = ss_empty ();
830 style->suffix = ss_empty ();
831 style->neg_suffix = ss_empty ();
832 style->decimal = '.';
837 /* Destroys a struct fmt_number_style. */
839 fmt_number_style_destroy (struct fmt_number_style *style)
843 ss_dealloc (&style->neg_prefix);
844 ss_dealloc (&style->prefix);
845 ss_dealloc (&style->suffix);
846 ss_dealloc (&style->neg_suffix);
851 /* Returns the number formatting style associated with the given
853 const struct fmt_number_style *
854 fmt_get_style (enum fmt_type type)
856 assert (is_fmt_type (type));
857 assert (styles[type] != NULL);
861 /* Sets STYLE as the number formatting style associated with the
862 given format TYPE, transferring ownership of STYLE. */
864 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
866 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
867 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
868 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
869 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
870 assert (style->decimal == '.' || style->decimal == ',');
871 assert (style->grouping != style->decimal
872 && (style->grouping == '.' || style->grouping == ','
873 || style->grouping == 0));
875 assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
876 assert (styles[type] != NULL);
878 fmt_number_style_destroy (styles[type]);
879 styles[type] = style;
882 /* Returns the total width of the standard prefix and suffix for
885 fmt_affix_width (const struct fmt_number_style *style)
887 return ss_length (style->prefix) + ss_length (style->suffix);
890 /* Returns the total width of the negative prefix and suffix for
893 fmt_neg_affix_width (const struct fmt_number_style *style)
895 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
898 /* Returns the decimal point character for the given format
901 fmt_decimal_char (enum fmt_type type)
903 return fmt_get_style (type)->decimal;
906 /* Returns the grouping character for the given format TYPE, or 0
907 if the format type does not group digits. */
909 fmt_grouping_char (enum fmt_type type)
911 return fmt_get_style (type)->grouping;
914 /* Sets the number style for TYPE to have the given standard
915 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
916 suffix, DECIMAL as the decimal point character, and GROUPING
917 as the grouping character. */
919 set_style (enum fmt_type type,
920 const char *prefix, const char *suffix,
921 char decimal, char grouping)
923 struct fmt_number_style *style;
925 assert (is_fmt_type (type));
927 fmt_number_style_destroy (styles[type]);
929 style = styles[type] = fmt_number_style_create ();
930 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
931 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
932 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
933 style->decimal = decimal;
934 style->grouping = grouping;
937 /* Sets the number style for TYPE as with set_style, but only if
938 TYPE has not already been initialized. */
940 init_style (enum fmt_type type,
941 const char *prefix, const char *suffix,
942 char decimal, char grouping)
944 assert (is_fmt_type (type));
945 if (styles[type] == NULL)
946 set_style (type, prefix, suffix, decimal, grouping);
949 /* Sets the decimal point character to DECIMAL. */
951 fmt_set_decimal (char decimal)
953 int grouping = decimal == '.' ? ',' : '.';
954 assert (decimal == '.' || decimal == ',');
956 set_style (FMT_F, "", "", decimal, 0);
957 set_style (FMT_E, "", "", decimal, 0);
958 set_style (FMT_COMMA, "", "", decimal, grouping);
959 set_style (FMT_DOT, "", "", grouping, decimal);
960 set_style (FMT_DOLLAR, "$", "", decimal, grouping);
961 set_style (FMT_PCT, "", "%", decimal, 0);
963 init_style (FMT_CCA, "", "", decimal, grouping);
964 init_style (FMT_CCB, "", "", decimal, grouping);
965 init_style (FMT_CCC, "", "", decimal, grouping);
966 init_style (FMT_CCD, "", "", decimal, grouping);
967 init_style (FMT_CCE, "", "", decimal, grouping);
970 /* Returns the struct fmt_desc for the given format TYPE. */
971 static const struct fmt_desc *
972 get_fmt_desc (enum fmt_type type)
974 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
976 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
977 {#NAME, IMIN, OMIN, IO, CATEGORY},
978 #include "format.def"
981 assert (is_fmt_type (type));
982 return &formats[type];