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 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
211 or an output format (otherwise) and returns nonzero if so.
212 Otherwise, emits an error message and returns zero. */
214 fmt_check (const struct fmt_spec *spec, bool for_input)
216 const char *io_fmt = for_input ? _("Input format") : _("Output format");
217 char str[FMT_STRING_LEN_MAX + 1];
218 int min_w, max_w, max_d;
220 assert (is_fmt_type (spec->type));
221 fmt_to_string (spec, str);
223 if (for_input && !fmt_usable_for_input (spec->type))
225 msg (SE, _("Format %s may not be used for input."), str);
229 if (spec->w % fmt_step_width (spec->type))
231 assert (fmt_step_width (spec->type) == 2);
232 msg (SE, _("%s specifies width %d, but %s requires an even width."),
233 str, spec->w, fmt_name (spec->type));
237 min_w = min_width (spec->type, for_input);
238 max_w = max_width (spec->type);
239 if (spec->w < min_w || spec->w > max_w)
241 msg (SE, _("%s %s specifies width %d, but "
242 "%s requires a width between %d and %d."),
243 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
247 max_d = max_decimals (spec->type, spec->w, for_input);
248 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
250 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
251 "%s does not allow any decimals.",
252 "%s %s specifies %d decimal places, but "
253 "%s does not allow any decimals.",
255 io_fmt, str, spec->d, fmt_name (spec->type));
258 else if (spec->d > max_d)
261 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
262 "the given width allows at most %d decimals.",
263 "%s %s specifies %d decimal places, but "
264 "the given width allows at most %d decimals.",
266 io_fmt, str, spec->d, max_d);
268 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
269 "the given width does not allow for any decimals.",
270 "%s %s specifies %d decimal places, but "
271 "the given width does not allow for any decimals.",
273 io_fmt, str, spec->d);
280 /* Checks whether SPEC is valid as an input format and returns
281 nonzero if so. Otherwise, emits an error message and returns
284 fmt_check_input (const struct fmt_spec *spec)
286 return fmt_check (spec, true);
289 /* Checks whether SPEC is valid as an output format and returns
290 true if so. Otherwise, emits an error message and returns false. */
292 fmt_check_output (const struct fmt_spec *spec)
294 return fmt_check (spec, false);
297 /* Checks that FORMAT is appropriate for a variable of the given
298 TYPE and returns true if so. Otherwise returns false and
299 emits an error message. */
301 fmt_check_type_compat (const struct fmt_spec *format, enum var_type var_type)
303 assert (var_type_is_valid (var_type));
304 if ((var_type == VAR_STRING) != (fmt_is_string (format->type) != 0))
306 char str[FMT_STRING_LEN_MAX + 1];
307 msg (SE, _("%s variables are not compatible with %s format %s."),
308 var_type == VAR_STRING ? _("String") : _("Numeric"),
309 var_type == VAR_STRING ? _("numeric") : _("string"),
310 fmt_to_string (format, str));
316 /* Checks that FORMAT is appropriate for a variable of the given
317 WIDTH and returns true if so. Otherwise returns false and
318 emits an error message. */
320 fmt_check_width_compat (const struct fmt_spec *format, int width)
322 if (!fmt_check_type_compat (format, var_type_from_width (width)))
324 if (fmt_var_width (format) != width)
326 char str[FMT_STRING_LEN_MAX + 1];
327 msg (SE, _("String variable with width %d is not compatible with "
329 width, fmt_to_string (format, str));
335 /* Returns the width corresponding to the format specifier. The
336 return value is the value of the `width' member of a `struct
337 variable' for such an input format. */
339 fmt_var_width (const struct fmt_spec *spec)
341 return (spec->type == FMT_AHEX ? spec->w / 2
342 : spec->type == FMT_A ? spec->w
346 /* Converts F to its string representation (for instance, "F8.2")
347 in BUFFER. Returns BUFFER.
349 If F has decimals, they are included in the output string,
350 even if F's format type does not allow decimals, to allow
351 accurately presenting incorrect formats to the user. */
353 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
355 if (fmt_takes_decimals (f->type) || f->d > 0)
356 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
357 "%s%d.%d", fmt_name (f->type), f->w, f->d);
359 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
360 "%s%d", fmt_name (f->type), f->w);
364 /* Returns true if A and B are identical formats,
367 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
369 return a->type == b->type && a->w == b->w && a->d == b->d;
372 /* Describes a display format. */
376 int min_input_width, min_output_width;
378 enum fmt_category category;
381 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
383 /* Returns the name of the given format TYPE. */
385 fmt_name (enum fmt_type type)
387 return get_fmt_desc (type)->name;
390 /* Tries to parse NAME as a format type.
391 If successful, stores the type in *TYPE and returns true.
392 On failure, returns false. */
394 fmt_from_name (const char *name, enum fmt_type *type)
398 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
399 if (!strcasecmp (name, get_fmt_desc (i)->name))
407 /* Returns true if TYPE accepts decimal places,
410 fmt_takes_decimals (enum fmt_type type)
412 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
415 /* Returns the minimum acceptable width for an input field
416 formatted with the given TYPE. */
418 fmt_min_input_width (enum fmt_type type)
420 return get_fmt_desc (type)->min_input_width;
423 /* Returns the maximum acceptable width for an input field
424 formatted with the given TYPE. */
426 fmt_max_input_width (enum fmt_type type)
428 return max_width (type);
431 /* Returns the maximum number of decimal places allowed in an
432 input field of the given TYPE and WIDTH. */
434 fmt_max_input_decimals (enum fmt_type type, int width)
436 assert (valid_width (type, width, true));
437 return max_decimals (type, width, true);
440 /* Returns the minimum acceptable width for an output field
441 formatted with the given TYPE. */
443 fmt_min_output_width (enum fmt_type type)
445 return get_fmt_desc (type)->min_output_width;
448 /* Returns the maximum acceptable width for an output field
449 formatted with the given TYPE. */
451 fmt_max_output_width (enum fmt_type type)
453 return max_width (type);
456 /* Returns the maximum number of decimal places allowed in an
457 output field of the given TYPE and WIDTH. */
459 fmt_max_output_decimals (enum fmt_type type, int width)
461 assert (valid_width (type, width, false));
462 return max_decimals (type, width, false);
465 /* Returns the width step for a field formatted with the given
466 TYPE. Field width must be a multiple of the width step. */
468 fmt_step_width (enum fmt_type type)
470 return fmt_get_category (type) == FMT_CAT_HEXADECIMAL ? 2 : 1;
473 /* Returns true if TYPE is used for string fields,
474 false if it is used for numeric fields. */
476 fmt_is_string (enum fmt_type type)
478 return fmt_get_category (type) == FMT_CAT_STRING;
481 /* Returns true if TYPE is used for numeric fields,
482 false if it is used for string fields. */
484 fmt_is_numeric (enum fmt_type type)
486 return !fmt_is_string (type);
489 /* Returns the format TYPE's category.
490 Each format type is in exactly one category,
491 and each category's value is bitwise disjoint from every other
492 category. Thus, the return value may be tested for equality
493 or compared bitwise against a mask of FMT_CAT_* values. */
495 fmt_get_category (enum fmt_type type)
497 return get_fmt_desc (type)->category;
500 /* Returns the output format selected by default when TYPE is
501 used as an input format. */
503 fmt_input_to_output (enum fmt_type type)
505 switch (fmt_get_category (type))
512 case FMT_CAT_HEXADECIMAL:
520 /* Returns the SPSS format type corresponding to the given PSPP
523 fmt_to_io (enum fmt_type type)
525 return get_fmt_desc (type)->io;
528 /* Determines the PSPP format corresponding to the given SPSS
529 format type. If successful, sets *FMT_TYPE to the PSPP format
530 and returns true. On failure, return false. */
532 fmt_from_io (int io, enum fmt_type *fmt_type)
536 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
537 if (get_fmt_desc (type)->io == io)
545 /* Returns true if TYPE may be used as an input format,
548 fmt_usable_for_input (enum fmt_type type)
550 assert (is_fmt_type (type));
551 return fmt_get_category (type) != FMT_CAT_CUSTOM;
554 /* For time and date formats, returns a template used for input
557 fmt_date_template (enum fmt_type type)
578 return "dd-mmm-yyyy HH:MM";
588 /* Returns a string of the form "$#,###.##" according to FMT,
589 which must be of type FMT_DOLLAR. The caller must free the
592 fmt_dollar_template (const struct fmt_spec *fmt)
594 struct string s = DS_EMPTY_INITIALIZER;
597 assert (fmt->type == FMT_DOLLAR);
599 ds_put_char (&s, '$');
600 for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
602 ds_put_char (&s, '#');
603 if (--c % 4 == 0 && c > 0)
605 ds_put_char (&s, fmt_grouping_char (fmt->type));
611 ds_put_char (&s, fmt_decimal_char (fmt->type));
612 ds_put_char_multiple (&s, '#', fmt->d);
618 /* Returns true if TYPE is a valid format type,
621 is_fmt_type (enum fmt_type type)
623 return type < FMT_NUMBER_OF_FORMATS;
626 /* Returns the minimum width of the given format TYPE,
627 for input if FOR_INPUT is true,
628 for output otherwise. */
630 min_width (enum fmt_type type, bool for_input)
632 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
635 /* Returns the maximum width of the given format TYPE,
636 which is invariant between input and output. */
638 max_width (enum fmt_type type)
640 assert (is_fmt_type (type));
658 return 2 * MAX_STRING;
665 /* Returns true if WIDTH is a valid width for the given format
667 for input if FOR_INPUT is true,
668 for output otherwise. */
670 valid_width (enum fmt_type type, int width, bool for_input)
672 return (width >= min_width (type, for_input)
673 && width <= max_width (type));
676 /* Returns the maximum number of decimal places allowed for the
677 given format TYPE with a width of WIDTH places,
678 for input if FOR_INPUT is true,
679 for output otherwise. */
681 max_decimals (enum fmt_type type, int width, bool for_input)
690 max_d = for_input ? width : width - 1;
695 max_d = for_input ? width : width - 2;
699 max_d = for_input ? width : width - 7;
717 max_d = width * 2 - 1;
726 max_d = max_digits_for_bytes (width);
779 /* Returns the maximum number of decimal digits in an unsigned
780 binary number that is BYTES bytes long. */
782 max_digits_for_bytes (int bytes)
784 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
785 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
786 return map[bytes - 1];
790 /* Creates and returns a new struct fmt_number_style,
791 initializing all affixes to empty strings. */
792 struct fmt_number_style *
793 fmt_number_style_create (void)
795 struct fmt_number_style *style = xmalloc (sizeof *style);
796 style->neg_prefix = ss_empty ();
797 style->prefix = ss_empty ();
798 style->suffix = ss_empty ();
799 style->neg_suffix = ss_empty ();
800 style->decimal = '.';
805 /* Destroys a struct fmt_number_style. */
807 fmt_number_style_destroy (struct fmt_number_style *style)
811 ss_dealloc (&style->neg_prefix);
812 ss_dealloc (&style->prefix);
813 ss_dealloc (&style->suffix);
814 ss_dealloc (&style->neg_suffix);
819 /* Returns the number formatting style associated with the given
821 const struct fmt_number_style *
822 fmt_get_style (enum fmt_type type)
824 assert (is_fmt_type (type));
825 assert (styles[type] != NULL);
829 /* Sets STYLE as the number formatting style associated with the
830 given format TYPE, transferring ownership of STYLE. */
832 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
834 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
835 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
836 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
837 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
838 assert (style->decimal == '.' || style->decimal == ',');
839 assert (style->grouping != style->decimal
840 && (style->grouping == '.' || style->grouping == ','
841 || style->grouping == 0));
843 assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
844 assert (styles[type] != NULL);
846 fmt_number_style_destroy (styles[type]);
847 styles[type] = style;
850 /* Returns the total width of the standard prefix and suffix for
853 fmt_affix_width (const struct fmt_number_style *style)
855 return ss_length (style->prefix) + ss_length (style->suffix);
858 /* Returns the total width of the negative prefix and suffix for
861 fmt_neg_affix_width (const struct fmt_number_style *style)
863 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
866 /* Returns the decimal point character for the given format
869 fmt_decimal_char (enum fmt_type type)
871 return fmt_get_style (type)->decimal;
874 /* Returns the grouping character for the given format TYPE, or 0
875 if the format type does not group digits. */
877 fmt_grouping_char (enum fmt_type type)
879 return fmt_get_style (type)->grouping;
882 /* Sets the number style for TYPE to have the given standard
883 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
884 suffix, DECIMAL as the decimal point character, and GROUPING
885 as the grouping character. */
887 set_style (enum fmt_type type,
888 const char *prefix, const char *suffix,
889 char decimal, char grouping)
891 struct fmt_number_style *style;
893 assert (is_fmt_type (type));
895 fmt_number_style_destroy (styles[type]);
897 style = styles[type] = fmt_number_style_create ();
898 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
899 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
900 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
901 style->decimal = decimal;
902 style->grouping = grouping;
905 /* Sets the number style for TYPE as with set_style, but only if
906 TYPE has not already been initialized. */
908 init_style (enum fmt_type type,
909 const char *prefix, const char *suffix,
910 char decimal, char grouping)
912 assert (is_fmt_type (type));
913 if (styles[type] == NULL)
914 set_style (type, prefix, suffix, decimal, grouping);
917 /* Sets the decimal point character to DECIMAL. */
919 fmt_set_decimal (char decimal)
921 int grouping = decimal == '.' ? ',' : '.';
922 assert (decimal == '.' || decimal == ',');
924 set_style (FMT_F, "", "", decimal, 0);
925 set_style (FMT_E, "", "", decimal, 0);
926 set_style (FMT_COMMA, "", "", decimal, grouping);
927 set_style (FMT_DOT, "", "", grouping, decimal);
928 set_style (FMT_DOLLAR, "$", "", decimal, grouping);
929 set_style (FMT_PCT, "", "%", decimal, 0);
931 init_style (FMT_CCA, "", "", decimal, grouping);
932 init_style (FMT_CCB, "", "", decimal, grouping);
933 init_style (FMT_CCC, "", "", decimal, grouping);
934 init_style (FMT_CCD, "", "", decimal, grouping);
935 init_style (FMT_CCE, "", "", decimal, grouping);
938 /* Returns the struct fmt_desc for the given format TYPE. */
939 static const struct fmt_desc *
940 get_fmt_desc (enum fmt_type type)
942 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
944 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
945 {#NAME, IMIN, OMIN, IO, CATEGORY},
946 #include "format.def"
949 assert (is_fmt_type (type));
950 return &formats[type];