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/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 /* Deinitialize the format module. */
69 /* Returns an input format specification with type TYPE, width W,
72 fmt_for_input (enum fmt_type type, int w, int d)
78 assert (fmt_check_input (&f));
82 /* Returns an output format specification with type TYPE, width
85 fmt_for_output (enum fmt_type type, int w, int d)
91 assert (fmt_check_output (&f));
95 /* Returns the output format specifier corresponding to input
96 format specifier INPUT. */
98 fmt_for_output_from_input (const struct fmt_spec *input)
100 struct fmt_spec output;
102 assert (fmt_check_input (input));
104 output.type = fmt_input_to_output (input->type);
106 if (output.w > fmt_max_output_width (output.type))
107 output.w = fmt_max_output_width (output.type);
108 else if (output.w < fmt_min_output_width (output.type))
109 output.w = fmt_min_output_width (output.type);
126 const struct fmt_number_style *style = fmt_get_style (input->type);
127 output.w += fmt_affix_width (style);
128 if (style->grouping != 0 && input->w - input->d >= 3)
129 output.w += (input->w - input->d - 1) / 3;
141 output.d = MAX (input->d, 3);
142 output.w = MAX (input->w, output.d + 7);
146 output.w = max_digits_for_bytes (input->w / 2) + 1;
157 output.w = 2 * input->w + (input->d > 0);
162 output.w = max_digits_for_bytes (input->w) + 1;
178 output.w = input->w / 2;
200 if (output.w > fmt_max_output_width (output.type))
201 output.w = fmt_max_output_width (output.type);
203 assert (fmt_check_output (&output));
207 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
208 or an output format (otherwise) and returns nonzero if so.
209 Otherwise, emits an error message and returns zero. */
211 fmt_check (const struct fmt_spec *spec, bool for_input)
213 const char *io_fmt = for_input ? _("Input format") : _("Output format");
214 char str[FMT_STRING_LEN_MAX + 1];
215 int min_w, max_w, max_d;
217 assert (is_fmt_type (spec->type));
218 fmt_to_string (spec, str);
220 if (for_input && !fmt_usable_for_input (spec->type))
222 msg (SE, _("Format %s may not be used for input."), str);
226 if (spec->w % fmt_step_width (spec->type))
228 assert (fmt_step_width (spec->type) == 2);
229 msg (SE, _("%s specifies width %d, but %s requires an even width."),
230 str, spec->w, fmt_name (spec->type));
234 min_w = min_width (spec->type, for_input);
235 max_w = max_width (spec->type);
236 if (spec->w < min_w || spec->w > max_w)
238 msg (SE, _("%s %s specifies width %d, but "
239 "%s requires a width between %d and %d."),
240 io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
244 max_d = max_decimals (spec->type, spec->w, for_input);
245 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
247 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
248 "%s does not allow any decimals.",
249 "%s %s specifies %d decimal places, but "
250 "%s does not allow any decimals.",
252 io_fmt, str, spec->d, fmt_name (spec->type));
255 else if (spec->d > max_d)
258 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
259 "the given width allows at most %d decimals.",
260 "%s %s specifies %d decimal places, but "
261 "the given width allows at most %d decimals.",
263 io_fmt, str, spec->d, max_d);
265 msg (SE, ngettext ("%s %s specifies %d decimal place, but "
266 "the given width does not allow for any decimals.",
267 "%s %s specifies %d decimal places, but "
268 "the given width does not allow for any decimals.",
270 io_fmt, str, spec->d);
277 /* Checks whether SPEC is valid as an input format and returns
278 nonzero if so. Otherwise, emits an error message and returns
281 fmt_check_input (const struct fmt_spec *spec)
283 return fmt_check (spec, true);
286 /* Checks whether SPEC is valid as an output format and returns
287 true if so. Otherwise, emits an error message and returns false. */
289 fmt_check_output (const struct fmt_spec *spec)
291 return fmt_check (spec, false);
294 /* Checks that FORMAT is appropriate for a variable of the given
295 TYPE and returns true if so. Otherwise returns false and
296 emits an error message. */
298 fmt_check_type_compat (const struct fmt_spec *format, int var_type)
300 assert (var_type == NUMERIC || var_type == ALPHA);
301 if ((var_type == ALPHA) != (fmt_is_string (format->type) != 0))
303 char str[FMT_STRING_LEN_MAX + 1];
304 msg (SE, _("%s variables are not compatible with %s format %s."),
305 var_type == ALPHA ? _("String") : _("Numeric"),
306 var_type == ALPHA ? _("numeric") : _("string"),
307 fmt_to_string (format, str));
313 /* Checks that FORMAT is appropriate for a variable of the given
314 WIDTH and returns true if so. Otherwise returns false and
315 emits an error message. */
317 fmt_check_width_compat (const struct fmt_spec *format, int width)
319 if (!fmt_check_type_compat (format, width != 0 ? ALPHA : NUMERIC))
321 if (fmt_var_width (format) != width)
323 char str[FMT_STRING_LEN_MAX + 1];
324 msg (SE, _("String variable with width %d is not compatible with "
326 width, fmt_to_string (format, str));
332 /* Returns the width corresponding to the format specifier. The
333 return value is the value of the `width' member of a `struct
334 variable' for such an input format. */
336 fmt_var_width (const struct fmt_spec *spec)
338 return (spec->type == FMT_AHEX ? spec->w / 2
339 : spec->type == FMT_A ? spec->w
343 /* Converts F to its string representation (for instance, "F8.2")
344 in BUFFER. Returns BUFFER.
346 If F has decimals, they are included in the output string,
347 even if F's format type does not allow decimals, to allow
348 accurately presenting incorrect formats to the user. */
350 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
352 if (fmt_takes_decimals (f->type) || f->d > 0)
353 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
354 "%s%d.%d", fmt_name (f->type), f->w, f->d);
356 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
357 "%s%d", fmt_name (f->type), f->w);
361 /* Describes a display format. */
365 int min_input_width, min_output_width;
367 enum fmt_category category;
370 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
372 /* Returns the name of the given format TYPE. */
374 fmt_name (enum fmt_type type)
376 return get_fmt_desc (type)->name;
379 /* Tries to parse NAME as a format type.
380 If successful, stores the type in *TYPE and returns true.
381 On failure, returns false. */
383 fmt_from_name (const char *name, enum fmt_type *type)
387 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
388 if (!strcasecmp (name, get_fmt_desc (i)->name))
396 /* Returns true if TYPE accepts decimal places,
399 fmt_takes_decimals (enum fmt_type type)
401 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
404 /* Returns the minimum acceptable width for an input field
405 formatted with the given TYPE. */
407 fmt_min_input_width (enum fmt_type type)
409 return get_fmt_desc (type)->min_input_width;
412 /* Returns the maximum acceptable width for an input field
413 formatted with the given TYPE. */
415 fmt_max_input_width (enum fmt_type type)
417 return max_width (type);
420 /* Returns the maximum number of decimal places allowed in an
421 input field of the given TYPE and WIDTH. */
423 fmt_max_input_decimals (enum fmt_type type, int width)
425 assert (valid_width (type, width, true));
426 return max_decimals (type, width, true);
429 /* Returns the minimum acceptable width for an output field
430 formatted with the given TYPE. */
432 fmt_min_output_width (enum fmt_type type)
434 return get_fmt_desc (type)->min_output_width;
437 /* Returns the maximum acceptable width for an output field
438 formatted with the given TYPE. */
440 fmt_max_output_width (enum fmt_type type)
442 return max_width (type);
445 /* Returns the maximum number of decimal places allowed in an
446 output field of the given TYPE and WIDTH. */
448 fmt_max_output_decimals (enum fmt_type type, int width)
450 assert (valid_width (type, width, false));
451 return max_decimals (type, width, false);
454 /* Returns the width step for a field formatted with the given
455 TYPE. Field width must be a multiple of the width step. */
457 fmt_step_width (enum fmt_type type)
459 return fmt_get_category (type) & FMT_CAT_HEXADECIMAL ? 2 : 1;
462 /* Returns true if TYPE is used for string fields,
463 false if it is used for numeric fields. */
465 fmt_is_string (enum fmt_type type)
467 return fmt_get_category (type) & FMT_CAT_STRING;
470 /* Returns true if TYPE is used for numeric fields,
471 false if it is used for string fields. */
473 fmt_is_numeric (enum fmt_type type)
475 return !fmt_is_string (type);
478 /* Returns the format TYPE's category.
479 Each format type is in exactly one category,
480 and each category's value is bitwise disjoint from every other
481 category. Thus, the return value may be tested for equality
482 or compared bitwise against a mask of FMT_CAT_* values. */
484 fmt_get_category (enum fmt_type type)
486 return get_fmt_desc (type)->category;
489 /* Returns the output format selected by default when TYPE is
490 used as an input format. */
492 fmt_input_to_output (enum fmt_type type)
494 enum fmt_category category = fmt_get_category (type);
495 return (category & FMT_CAT_STRING ? FMT_A
496 : category & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL) ? FMT_F
500 /* Returns the SPSS format type corresponding to the given PSPP
503 fmt_to_io (enum fmt_type type)
505 return get_fmt_desc (type)->io;
508 /* Determines the PSPP format corresponding to the given SPSS
509 format type. If successful, sets *FMT_TYPE to the PSPP format
510 and returns true. On failure, return false. */
512 fmt_from_io (int io, enum fmt_type *fmt_type)
516 for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
517 if (get_fmt_desc (type)->io == io)
525 /* Returns true if TYPE may be used as an input format,
528 fmt_usable_for_input (enum fmt_type type)
530 assert (is_fmt_type (type));
531 return fmt_get_category (type) != FMT_CAT_CUSTOM;
534 /* For time and date formats, returns a template used for input
537 fmt_date_template (enum fmt_type type)
558 return "dd-mmm-yyyy HH:MM";
568 /* Returns true if TYPE is a valid format type,
571 is_fmt_type (enum fmt_type type)
573 return type < FMT_NUMBER_OF_FORMATS;
576 /* Returns the minimum width of the given format TYPE,
577 for input if FOR_INPUT is true,
578 for output otherwise. */
580 min_width (enum fmt_type type, bool for_input)
582 return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
585 /* Returns the maximum width of the given format TYPE,
586 which is invariant between input and output. */
588 max_width (enum fmt_type type)
590 assert (is_fmt_type (type));
608 return 2 * MAX_STRING;
615 /* Returns true if WIDTH is a valid width for the given format
617 for input if FOR_INPUT is true,
618 for output otherwise. */
620 valid_width (enum fmt_type type, int width, bool for_input)
622 return (width >= min_width (type, for_input)
623 && width <= max_width (type));
626 /* Returns the maximum number of decimal places allowed for the
627 given format TYPE with a width of WIDTH places,
628 for input if FOR_INPUT is true,
629 for output otherwise. */
631 max_decimals (enum fmt_type type, int width, bool for_input)
640 max_d = for_input ? width : width - 1;
645 max_d = for_input ? width : width - 2;
649 max_d = for_input ? width : width - 7;
667 max_d = width * 2 - 1;
676 max_d = max_digits_for_bytes (width);
729 /* Returns the maximum number of decimal digits in an unsigned
730 binary number that is BYTES bytes long. */
732 max_digits_for_bytes (int bytes)
734 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
735 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
736 return map[bytes - 1];
739 static struct fmt_number_style *styles[FMT_NUMBER_OF_FORMATS];
741 /* Creates and returns a new struct fmt_number_style,
742 initializing all affixes to empty strings. */
743 struct fmt_number_style *
744 fmt_number_style_create (void)
746 struct fmt_number_style *style = xmalloc (sizeof *style);
747 style->neg_prefix = ss_empty ();
748 style->prefix = ss_empty ();
749 style->suffix = ss_empty ();
750 style->neg_suffix = ss_empty ();
751 style->decimal = '.';
756 /* Destroys a struct fmt_number_style. */
758 fmt_number_style_destroy (struct fmt_number_style *style)
762 ss_dealloc (&style->neg_prefix);
763 ss_dealloc (&style->prefix);
764 ss_dealloc (&style->suffix);
765 ss_dealloc (&style->neg_suffix);
770 /* Returns the number formatting style associated with the given
772 const struct fmt_number_style *
773 fmt_get_style (enum fmt_type type)
775 assert (is_fmt_type (type));
776 assert (styles[type] != NULL);
780 /* Sets STYLE as the number formatting style associated with the
781 given format TYPE, transferring ownership of STYLE. */
783 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
785 assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
786 assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
787 assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
788 assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
789 assert (style->decimal == '.' || style->decimal == ',');
790 assert (style->grouping != style->decimal
791 && (style->grouping == '.' || style->grouping == ','
792 || style->grouping == 0));
794 assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
795 assert (styles[type] != NULL);
797 fmt_number_style_destroy (styles[type]);
798 styles[type] = style;
801 /* Returns the total width of the standard prefix and suffix for
804 fmt_affix_width (const struct fmt_number_style *style)
806 return ss_length (style->prefix) + ss_length (style->suffix);
809 /* Returns the total width of the negative prefix and suffix for
812 fmt_neg_affix_width (const struct fmt_number_style *style)
814 return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
817 /* Returns the decimal point character for the given format
820 fmt_decimal_char (enum fmt_type type)
822 return fmt_get_style (type)->decimal;
825 /* Returns the grouping character for the given format TYPE, or 0
826 if the format type does not group digits. */
828 fmt_grouping_char (enum fmt_type type)
830 return fmt_get_style (type)->grouping;
833 /* Sets the number style for TYPE to have the given standard
834 PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
835 suffix, DECIMAL as the decimal point character, and GROUPING
836 as the grouping character. */
838 set_style (enum fmt_type type,
839 const char *prefix, const char *suffix,
840 char decimal, char grouping)
842 struct fmt_number_style *style;
844 assert (is_fmt_type (type));
846 fmt_number_style_destroy (styles[type]);
848 style = styles[type] = fmt_number_style_create ();
849 ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
850 ss_alloc_substring (&style->prefix, ss_cstr (prefix));
851 ss_alloc_substring (&style->suffix, ss_cstr (suffix));
852 style->decimal = decimal;
853 style->grouping = grouping;
856 /* Sets the number style for TYPE as with set_style, but only if
857 TYPE has not already been initialized. */
859 init_style (enum fmt_type type,
860 const char *prefix, const char *suffix,
861 char decimal, char grouping)
863 assert (is_fmt_type (type));
864 if (styles[type] == NULL)
865 set_style (type, prefix, suffix, decimal, grouping);
868 /* Sets the decimal point character to DECIMAL. */
870 fmt_set_decimal (char decimal)
872 int grouping = decimal == '.' ? ',' : '.';
873 assert (decimal == '.' || decimal == ',');
875 set_style (FMT_F, "", "", decimal, 0);
876 set_style (FMT_E, "", "", decimal, 0);
877 set_style (FMT_COMMA, "", "", decimal, grouping);
878 set_style (FMT_DOT, "", "", grouping, decimal);
879 set_style (FMT_DOLLAR, "$", "", decimal, grouping);
880 set_style (FMT_PCT, "", "%", decimal, 0);
882 init_style (FMT_CCA, "", "", decimal, grouping);
883 init_style (FMT_CCB, "", "", decimal, grouping);
884 init_style (FMT_CCC, "", "", decimal, grouping);
885 init_style (FMT_CCD, "", "", decimal, grouping);
886 init_style (FMT_CCE, "", "", decimal, grouping);
889 /* Returns true if M is a valid variable measurement level,
892 measure_is_valid (enum measure m)
894 return m > 0 && m < n_MEASURES;
897 /* Returns true if A is a valid alignment,
900 alignment_is_valid (enum alignment a)
905 /* Returns the struct fmt_desc for the given format TYPE. */
906 static const struct fmt_desc *
907 get_fmt_desc (enum fmt_type type)
909 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
911 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
912 {#NAME, IMIN, OMIN, IO, CATEGORY},
913 #include "format.def"
916 assert (is_fmt_type (type));
917 return &formats[type];