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)
42 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 void fmt_number_style_init (struct fmt_number_style *style);
54 /* Initialize the format module. */
55 struct fmt_number_style *
58 struct fmt_number_style *styles =
59 xcalloc (FMT_NUMBER_OF_FORMATS, sizeof (*styles));
62 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
63 fmt_number_style_init (&styles[t]);
65 fmt_set_decimal (styles, '.');
71 /* Deinitialize the format module. */
73 fmt_done (struct fmt_number_style *styles)
76 for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
77 fmt_number_style_destroy (&styles[t]);
82 /* Returns an input format specification with type TYPE, width W,
85 fmt_for_input (enum fmt_type type, int w, int d)
91 assert (fmt_check_input (&f));
95 /* Returns an output format specification with type TYPE, width
98 fmt_for_output (enum fmt_type type, int w, int d)
104 assert (fmt_check_output (&f));
108 /* Returns the output format specifier corresponding to input
109 format specifier INPUT. */
111 fmt_for_output_from_input (const struct fmt_spec *input)
113 struct fmt_spec output;
115 assert (fmt_check_input (input));
117 output.type = fmt_input_to_output (input->type);
119 if (output.w > fmt_max_output_width (output.type))
120 output.w = fmt_max_output_width (output.type);
121 else if (output.w < fmt_min_output_width (output.type))
122 output.w = fmt_min_output_width (output.type);
139 const struct fmt_number_style *style =
140 settings_get_style (input->type);
142 output.w += fmt_affix_width (style);
143 if (style->grouping != 0 && input->w - input->d >= 3)
144 output.w += (input->w - input->d - 1) / 3;
156 output.d = MAX (input->d, 3);
157 output.w = MAX (input->w, output.d + 7);
161 output.w = max_digits_for_bytes (input->w / 2) + 1;
172 output.w = 2 * input->w + (input->d > 0);
177 output.w = max_digits_for_bytes (input->w) + 1;
193 output.w = input->w / 2;
215 if (output.w > fmt_max_output_width (output.type))
216 output.w = fmt_max_output_width (output.type);
218 assert (fmt_check_output (&output));
222 /* Returns the default format for the given WIDTH: F8.2 format
223 for a numeric value, A format for a string value. */
225 fmt_default_for_width (int width)
228 ? fmt_for_output (FMT_F, 8, 2)
229 : fmt_for_output (FMT_A, width, 0));
232 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
233 or an output format (otherwise) and returns nonzero if so.
234 Otherwise, emits an error message and returns zero. */
236 fmt_check (const struct fmt_spec *spec, bool for_input)
238 const char *io_fmt = for_input ? _("Input format") : _("Output format");
239 char str[FMT_STRING_LEN_MAX + 1];
240 int min_w, max_w, max_d;
242 assert (is_fmt_type (spec->type));
243 fmt_to_string (spec, str);
245 if (for_input && !fmt_usable_for_input (spec->type))
247 msg (SE, _("Format %s may not be used for input."), str);
251 if (spec->w % fmt_step_width (spec->type))
253 assert (fmt_step_width (spec->type) == 2);
254 msg (SE, _("%s specifies width %d, but %s requires an even width."),
255 str, spec->w, fmt_name (spec->type));
259 min_w = min_width (spec->type, for_input);
260 max_w = max_width (spec->type);
261 if (spec->w < min_w || spec->w > max_w)
263 msg (SE, _("%s %s specifies width %d, but "
264 "%s requires a width between %d and %d."),
265 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
269 max_d = max_decimals (spec->type, spec->w, for_input);
270 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
272 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
273 "%s does not allow any decimals.",
274 "%s %s specifies %d decimal places, but "
275 "%s does not allow any decimals.",
277 io_fmt, str, spec->d, fmt_name (spec->type));
280 else if (spec->d > max_d)
283 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
284 "the given width allows at most %d decimals.",
285 "%s %s specifies %d decimal places, but "
286 "the given width allows at most %d decimals.",
288 io_fmt, str, spec->d, max_d);
290 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
291 "the given width does not allow for any decimals.",
292 "%s %s specifies %d decimal places, but "
293 "the given width does not allow for any decimals.",
295 io_fmt, str, spec->d);
302 /* Checks whether SPEC is valid as an input format and returns
303 nonzero if so. Otherwise, emits an error message and returns
306 fmt_check_input (const struct fmt_spec *spec)
308 return fmt_check (spec, true);
311 /* Checks whether SPEC is valid as an output format and returns
312 true if so. Otherwise, emits an error message and returns false. */
314 fmt_check_output (const struct fmt_spec *spec)
316 return fmt_check (spec, false);
319 /* Checks that FORMAT is appropriate for a variable of the given
320 VAR_TYPE and returns true if so. Otherwise returns false and
321 emits an error message. */
323 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
325 assert (val_type_is_valid (var_type));
326 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
328 char str[FMT_STRING_LEN_MAX + 1];
329 msg (SE, _("%s variables are not compatible with %s format %s."),
330 var_type == VAL_STRING ? _("String") : _("Numeric"),
331 var_type == VAL_STRING ? _("numeric") : _("string"),
332 fmt_to_string (format, str));
338 /* Checks that FORMAT is appropriate for a variable of the given
339 WIDTH and returns true if so. Otherwise returns false and
340 emits an error message. */
342 fmt_check_width_compat (const struct fmt_spec *format, int width)
344 if (!fmt_check_type_compat (format, val_type_from_width (width)))
346 if (fmt_var_width (format) != width)
348 char str[FMT_STRING_LEN_MAX + 1];
349 msg (SE, _("String variable with width %d is not compatible with "
351 width, fmt_to_string (format, str));
357 /* Returns the width corresponding to FORMAT. The return value
358 is the width of the `union value's required by FORMAT. */
360 fmt_var_width (const struct fmt_spec *format)
362 return (format->type == FMT_AHEX ? format->w / 2
363 : format->type == FMT_A ? format->w
367 /* Converts F to its string representation (for instance, "F8.2")
368 in BUFFER. Returns BUFFER.
370 If F has decimals, they are included in the output string,
371 even if F's format type does not allow decimals, to allow
372 accurately presenting incorrect formats to the user. */
374 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
376 if (fmt_takes_decimals (f->type) || f->d > 0)
377 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
378 "%s%d.%d", fmt_name (f->type), f->w, f->d);
380 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
381 "%s%d", fmt_name (f->type), f->w);
385 /* Returns true if A and B are identical formats,
388 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
390 return a->type == b->type && a->w == b->w && a->d == b->d;
393 /* Adjusts FMT to be valid for a value of the given WIDTH. */
395 fmt_resize (struct fmt_spec *fmt, int width)
397 if ((width > 0) != fmt_is_string (fmt->type))
399 /* Changed from numeric to string or vice versa. Set to
400 default format for new width. */
401 *fmt = fmt_default_for_width (width);
405 /* Changed width of string. Preserve format type, adjust
407 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
415 /* Describes a display format. */
419 int min_input_width, min_output_width;
421 enum fmt_category category;
424 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
426 /* Returns the name of the given format TYPE. */
428 fmt_name (enum fmt_type type)
430 return get_fmt_desc (type)->name;
433 /* Tries to parse NAME as a format type.
434 If successful, stores the type in *TYPE and returns true.
435 On failure, returns false. */
437 fmt_from_name (const char *name, enum fmt_type *type)
441 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
442 if (!strcasecmp (name, get_fmt_desc (i)->name))
450 /* Returns true if TYPE accepts decimal places,
453 fmt_takes_decimals (enum fmt_type type)
455 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
458 /* Returns the minimum acceptable width for an input field
459 formatted with the given TYPE. */
461 fmt_min_input_width (enum fmt_type type)
463 return get_fmt_desc (type)->min_input_width;
466 /* Returns the maximum acceptable width for an input field
467 formatted with the given TYPE. */
469 fmt_max_input_width (enum fmt_type type)
471 return max_width (type);
474 /* Returns the maximum number of decimal places allowed in an
475 input field of the given TYPE and WIDTH. */
477 fmt_max_input_decimals (enum fmt_type type, int width)
479 assert (valid_width (type, width, true));
480 return max_decimals (type, width, true);
483 /* Returns the minimum acceptable width for an output field
484 formatted with the given TYPE. */
486 fmt_min_output_width (enum fmt_type type)
488 return get_fmt_desc (type)->min_output_width;
491 /* Returns the maximum acceptable width for an output field
492 formatted with the given TYPE. */
494 fmt_max_output_width (enum fmt_type type)
496 return max_width (type);
499 /* Returns the maximum number of decimal places allowed in an
500 output field of the given TYPE and WIDTH. */
502 fmt_max_output_decimals (enum fmt_type type, int width)
504 assert (valid_width (type, width, false));
505 return max_decimals (type, width, false);
508 /* Returns the width step for a field formatted with the given
509 TYPE. Field width must be a multiple of the width step. */
511 fmt_step_width (enum fmt_type type)
513 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
517 /* Returns true if TYPE is used for string fields,
518 false if it is used for numeric fields. */
520 fmt_is_string (enum fmt_type type)
522 return fmt_get_category (type) == FMT_CAT_STRING;
525 /* Returns true if TYPE is used for numeric fields,
526 false if it is used for string fields. */
528 fmt_is_numeric (enum fmt_type type)
530 return !fmt_is_string (type);
533 /* Returns the format TYPE's category.
534 Each format type is in exactly one category,
535 and each category's value is bitwise disjoint from every other
536 category. Thus, the return value may be tested for equality
537 or compared bitwise against a mask of FMT_CAT_* values. */
539 fmt_get_category (enum fmt_type type)
541 return get_fmt_desc (type)->category;
544 /* Returns the output format selected by default when TYPE is
545 used as an input format. */
547 fmt_input_to_output (enum fmt_type type)
549 switch (fmt_get_category (type))
556 case FMT_CAT_HEXADECIMAL:
564 /* Returns the SPSS format type corresponding to the given PSPP
567 fmt_to_io (enum fmt_type type)
569 return get_fmt_desc (type)->io;
572 /* Determines the PSPP format corresponding to the given SPSS
573 format type. If successful, sets *FMT_TYPE to the PSPP format
574 and returns true. On failure, return false. */
576 fmt_from_io (int io, enum fmt_type *fmt_type)
580 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
581 if (get_fmt_desc (type)->io == io)
589 /* Returns true if TYPE may be used as an input format,
592 fmt_usable_for_input (enum fmt_type type)
594 assert (is_fmt_type (type));
595 return fmt_get_category (type) != FMT_CAT_CUSTOM;
598 /* For time and date formats, returns a template used for input
601 fmt_date_template (enum fmt_type type)
622 return "dd-mmm-yyyy HH:MM";
633 /* Returns true if TYPE is a valid format type,
636 is_fmt_type (enum fmt_type type)
638 return type < FMT_NUMBER_OF_FORMATS;
641 /* Returns the minimum width of the given format TYPE,
642 for input if FOR_INPUT is true,
643 for output otherwise. */
645 min_width (enum fmt_type type, bool for_input)
647 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
650 /* Returns the maximum width of the given format TYPE,
651 which is invariant between input and output. */
653 max_width (enum fmt_type type)
655 assert (is_fmt_type (type));
673 return 2 * MAX_STRING;
680 /* Returns true if WIDTH is a valid width for the given format
682 for input if FOR_INPUT is true,
683 for output otherwise. */
685 valid_width (enum fmt_type type, int width, bool for_input)
687 return (width >= min_width (type, for_input)
688 && width <= max_width (type));
691 /* Returns the maximum number of decimal places allowed for the
692 given format TYPE with a width of WIDTH places,
693 for input if FOR_INPUT is true,
694 for output otherwise. */
696 max_decimals (enum fmt_type type, int width, bool for_input)
705 max_d = for_input ? width : width - 1;
710 max_d = for_input ? width : width - 2;
714 max_d = for_input ? width : width - 7;
732 max_d = width * 2 - 1;
741 max_d = max_digits_for_bytes (width);
794 /* Returns the maximum number of decimal digits in an unsigned
795 binary number that is BYTES bytes long. */
797 max_digits_for_bytes (int bytes)
799 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
800 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
801 return map[bytes - 1];
807 fmt_number_style_init (struct fmt_number_style *style)
809 style->neg_prefix = ss_empty ();
810 style->prefix = ss_empty ();
811 style->suffix = ss_empty ();
812 style->neg_suffix = ss_empty ();
813 style->decimal = '.';
818 /* Destroys a struct fmt_number_style. */
820 fmt_number_style_destroy (struct fmt_number_style *style)
824 ss_dealloc (&style->neg_prefix);
825 ss_dealloc (&style->prefix);
826 ss_dealloc (&style->suffix);
827 ss_dealloc (&style->neg_suffix);
831 /* Returns the number formatting style associated with the given
833 const struct fmt_number_style *
834 fmt_get_style (const struct fmt_number_style *styles, enum fmt_type type)
836 assert (is_fmt_type (type));
837 return &styles[type];
841 /* Checks that style is STYLE sane */
843 fmt_check_style (const struct fmt_number_style *style)
845 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
846 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
847 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
848 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
849 assert (style->decimal == '.' || style->decimal == ',');
850 assert (style->grouping == '.' || style->grouping == ','
851 || style->grouping == 0);
852 assert (style->grouping != style->decimal);
856 /* Returns the total width of the standard prefix and suffix for
859 fmt_affix_width (const struct fmt_number_style *style)
861 return ss_length (style->prefix) + ss_length (style->suffix);
864 /* Returns the total width of the negative prefix and suffix for
867 fmt_neg_affix_width (const struct fmt_number_style *style)
869 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
873 /* Sets the number style for TYPE to have the given standard
874 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
875 suffix, DECIMAL as the decimal point character, and GROUPING
876 as the grouping character. */
878 set_style (struct fmt_number_style *styles, enum fmt_type type,
879 const char *prefix, const char *suffix,
880 char decimal, char grouping)
882 struct fmt_number_style *style;
884 assert (is_fmt_type (type));
886 style = &styles[type] ;
888 fmt_number_style_destroy (style);
890 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
891 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
892 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
893 style->decimal = decimal;
894 style->grouping = grouping;
897 /* Sets the decimal point character to DECIMAL. */
899 fmt_set_decimal (struct fmt_number_style *styles, char decimal)
901 int grouping = decimal == '.' ? ',' : '.';
902 assert (decimal == '.' || decimal == ',');
904 set_style (styles, FMT_F, "", "", decimal, 0);
905 set_style (styles, FMT_E, "", "", decimal, 0);
906 set_style (styles, FMT_COMMA, "", "", decimal, grouping);
907 set_style (styles, FMT_DOT, "", "", grouping, decimal);
908 set_style (styles, FMT_DOLLAR, "$", "", decimal, grouping);
909 set_style (styles, FMT_PCT, "", "%", decimal, 0);
912 /* Returns the struct fmt_desc for the given format TYPE. */
913 static const struct fmt_desc *
914 get_fmt_desc (enum fmt_type type)
916 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
918 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
919 {#NAME, IMIN, OMIN, IO, CATEGORY},
920 #include "format.def"
923 assert (is_fmt_type (type));
924 return &formats[type];