1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2010, 2011, 2012 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/>. */
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/cast.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/message.h"
34 #include "libpspp/misc.h"
35 #include "libpspp/str.h"
37 #include "gl/c-strcase.h"
38 #include "gl/minmax.h"
39 #include "gl/xalloc.h"
40 #include "gl/xmemdup0.h"
43 #define _(msgid) gettext (msgid)
45 bool is_fmt_type (enum fmt_type);
47 static bool valid_width (enum fmt_type, int width, enum fmt_use);
49 static int max_digits_for_bytes (int bytes);
50 static void fmt_clamp_width (struct fmt_spec *, enum fmt_use);
51 static void fmt_clamp_decimals (struct fmt_spec *, enum fmt_use);
54 fmt_settings_init (struct fmt_settings *settings)
56 *settings = (struct fmt_settings) FMT_SETTINGS_INIT;
60 fmt_settings_uninit (struct fmt_settings *settings)
62 for (int i = 0; i < FMT_N_CCS; i++)
63 fmt_number_style_destroy (settings->ccs[i]);
67 fmt_settings_copy (const struct fmt_settings *old)
69 struct fmt_settings new = *old;
70 for (int i = 0; i < FMT_N_CCS; i++)
71 new.ccs[i] = fmt_number_style_clone (old->ccs[i]);
76 fmt_type_to_cc_index (enum fmt_type type)
80 case FMT_CCA: return 0;
81 case FMT_CCB: return 1;
82 case FMT_CCC: return 2;
83 case FMT_CCD: return 3;
84 case FMT_CCE: return 4;
85 default: NOT_REACHED ();
89 /* Returns the number formatting style associated with the given
91 const struct fmt_number_style *
92 fmt_settings_get_style (const struct fmt_settings *settings,
96 verify (FMT_COMMA < 6);
98 verify (FMT_DOLLAR < 6);
102 #define OPPOSITE(C) ((C) == ',' ? '.' : ',')
103 #define AFFIX(S) { .s = (char *) (S), .width = sizeof (S) - 1 }
104 #define NS(PREFIX, SUFFIX, DECIMAL, GROUPING, INCLUDE_LEADING_ZERO) { \
105 .neg_prefix = AFFIX ("-"), \
106 .prefix = AFFIX (PREFIX), \
107 .suffix = AFFIX (SUFFIX), \
108 .neg_suffix = AFFIX (""), \
109 .decimal = DECIMAL, \
110 .grouping = GROUPING, \
111 .include_leading_zero = INCLUDE_LEADING_ZERO \
113 #define ANS(DECIMAL, GROUPING, INCLUDE_LEADING_ZERO) { \
114 [FMT_F] = NS( "", "", DECIMAL, 0, INCLUDE_LEADING_ZERO), \
115 [FMT_E] = NS( "", "", DECIMAL, 0, INCLUDE_LEADING_ZERO), \
116 [FMT_COMMA] = NS( "", "", DECIMAL, GROUPING, INCLUDE_LEADING_ZERO), \
117 [FMT_DOT] = NS( "", "", GROUPING, DECIMAL, INCLUDE_LEADING_ZERO), \
118 [FMT_DOLLAR] = NS("$", "", DECIMAL, GROUPING, false), \
119 [FMT_PCT] = NS( "", "%", DECIMAL, 0, false), \
121 #define ANS2(DECIMAL, GROUPING) { \
122 ANS(DECIMAL, GROUPING, false), \
123 ANS(DECIMAL, GROUPING, true), \
126 /* First index: 0 for ',' decimal point, 1 for '.' decimal point.
127 Second index: 0 for no leading zero, 1 for leading zero.
130 static const struct fmt_number_style styles[2][2][6] = {
135 static const struct fmt_number_style default_style = NS ("", "", '.', 0, false);
146 int decimal_idx = settings->decimal == '.';
147 int leadzero_idx = settings->include_leading_zero;
148 return &styles[decimal_idx][leadzero_idx][type];
157 size_t idx = fmt_type_to_cc_index (type);
158 return settings->ccs[idx] ? settings->ccs[idx] : &default_style;
162 return &default_style;
169 static int epoch = 0;
173 struct tm *tm = localtime (&t);
174 epoch = (tm != NULL ? tm->tm_year + 1900 : 2000) - 69;
180 fmt_settings_get_epoch (const struct fmt_settings *settings)
182 return !settings->epoch ? default_epoch () : settings->epoch;
186 fmt_settings_set_cc (struct fmt_settings *settings, enum fmt_type type,
187 struct fmt_number_style *style)
189 size_t idx = fmt_type_to_cc_index (type);
191 fmt_number_style_destroy (settings->ccs[idx]);
192 settings->ccs[idx] = style;
196 /* Returns an input format specification with type TYPE, width W,
199 fmt_for_input (enum fmt_type type, int w, int d)
201 struct fmt_spec f = { .type = type, .w = w, .d = d };
202 assert (fmt_check_input (&f));
206 /* Returns an output format specification with type TYPE, width
207 W, and D decimals. */
209 fmt_for_output (enum fmt_type type, int w, int d)
211 struct fmt_spec f = { .type = type, .w = w, .d = d };
212 assert (fmt_check_output (&f));
216 /* Returns the output format specifier corresponding to input
217 format specifier INPUT. */
219 fmt_for_output_from_input (const struct fmt_spec *input,
220 const struct fmt_settings *settings)
222 struct fmt_spec output;
224 assert (fmt_check_input (input));
226 output.type = fmt_input_to_output (input->type);
228 if (output.w > fmt_max_output_width (output.type))
229 output.w = fmt_max_output_width (output.type);
230 else if (output.w < fmt_min_output_width (output.type))
231 output.w = fmt_min_output_width (output.type);
248 const struct fmt_number_style *style =
249 fmt_settings_get_style (settings, input->type);
251 output.w += fmt_affix_width (style);
252 if (style->grouping != 0 && input->w - input->d >= 3)
253 output.w += (input->w - input->d - 1) / 3;
265 output.d = MAX (input->d, 3);
266 output.w = MAX (input->w, output.d + 7);
270 output.w = max_digits_for_bytes (input->w / 2) + 1;
281 output.w = 2 * input->w + (input->d > 0);
286 output.w = max_digits_for_bytes (input->w) + 1;
302 output.w = input->w / 2;
322 output.w = MAX (input->w, input->d + 6);
327 output.w = MAX (input->w, input->d + 20);
334 if (output.w > fmt_max_output_width (output.type))
335 output.w = fmt_max_output_width (output.type);
337 assert (fmt_check_output (&output));
341 /* Returns the default format for the given WIDTH: F8.2 format
342 for a numeric value, A format for a string value. */
344 fmt_default_for_width (int width)
347 ? fmt_for_output (FMT_F, 8, 2)
348 : fmt_for_output (FMT_A, width, 0));
351 /* Checks whether SPEC is valid for USE and returns NULL if so. Otherwise,
352 returns a malloc()'d string that describes the error. The caller must
353 eventually free() the string. */
355 fmt_check__ (const struct fmt_spec *spec, enum fmt_use use)
357 char str[FMT_STRING_LEN_MAX + 1];
358 int min_w, max_w, max_d;
360 assert (is_fmt_type (spec->type));
361 fmt_to_string (spec, str);
363 if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
364 return xasprintf (_("Format %s may not be used for input."), str);
366 if (spec->w % fmt_step_width (spec->type))
368 assert (fmt_step_width (spec->type) == 2);
369 return (use == FMT_FOR_INPUT
370 ? xasprintf (_("Input format %s specifies width %d, "
371 "but %s requires an even width."),
372 str, spec->w, fmt_name (spec->type))
373 : xasprintf (_("Output format %s specifies width %d, "
374 "but %s requires an even width."),
375 str, spec->w, fmt_name (spec->type)));
378 min_w = fmt_min_width (spec->type, use);
379 max_w = fmt_max_width (spec->type, use);
380 if (spec->w < min_w || spec->w > max_w)
381 return (use == FMT_FOR_INPUT
382 ? xasprintf (_("Input format %s specifies width %d, but "
383 "%s requires a width between %d and %d."),
384 str, spec->w, fmt_name (spec->type), min_w, max_w)
385 : xasprintf (_("Output format %s specifies width %d, but "
386 "%s requires a width between %d and %d."),
387 str, spec->w, fmt_name (spec->type), min_w, max_w));
389 max_d = fmt_max_decimals (spec->type, spec->w, use);
390 if (!fmt_takes_decimals (spec->type) && spec->d != 0)
391 return (use == FMT_FOR_INPUT
392 ? xasprintf (ngettext (
393 "Input format %s specifies %d decimal "
394 "place, but %s does not allow any decimals.",
395 "Input format %s specifies %d decimal "
396 "places, but %s does not allow any "
399 str, spec->d, fmt_name (spec->type))
400 : xasprintf (ngettext (
401 "Output format %s specifies %d decimal "
402 "place, but %s does not allow any decimals.",
403 "Output format %s specifies %d decimal places, but "
404 "%s does not allow any decimals.",
406 str, spec->d, fmt_name (spec->type)));
407 else if (spec->d > max_d)
410 return (use == FMT_FOR_INPUT
411 ? xasprintf (ngettext (
412 "Input format %s specifies %d decimal place, "
413 "but width %d allows at most %d decimals.",
414 "Input format %s specifies %d decimal places, "
415 "but width %d allows at most %d decimals.",
417 str, spec->d, spec->w, max_d)
418 : xasprintf (ngettext (
419 "Output format %s specifies %d decimal place, "
420 "but width %d allows at most %d decimals.",
421 "Output format %s specifies %d decimal places, "
422 "but width %d allows at most %d decimals.",
424 str, spec->d, spec->w, max_d));
426 return (use == FMT_FOR_INPUT
427 ? xasprintf (ngettext (
428 "Input format %s specifies %d decimal place, "
429 "but width %d does not allow for any decimals.",
430 "Input format %s specifies %d decimal places, "
431 "but width %d does not allow for any decimals.",
433 str, spec->d, spec->w)
434 : xasprintf (ngettext (
435 "Output format %s specifies %d decimal place, "
436 "but width %d does not allow for any decimals.",
437 "Output format %s specifies %d decimal places, "
438 "but width %d does not allow for any decimals.",
440 str, spec->d, spec->w));
447 fmt_check_input__ (const struct fmt_spec *spec)
449 return fmt_check__ (spec, FMT_FOR_INPUT);
453 fmt_check_output__ (const struct fmt_spec *spec)
455 return fmt_check__ (spec, FMT_FOR_OUTPUT);
459 error_to_bool (char *error)
470 /* Returns true if SPEC is valid for USE, false otherwise. */
472 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
474 return error_to_bool (fmt_check__ (spec, use));
477 /* Returns true if SPEC is valid as an input format, otherwise false. */
479 fmt_check_input (const struct fmt_spec *spec)
481 return fmt_check (spec, FMT_FOR_INPUT);
484 /* Returnst true SPEC is valid as an output format, false otherwise. */
486 fmt_check_output (const struct fmt_spec *spec)
488 return fmt_check (spec, FMT_FOR_OUTPUT);
491 /* Checks that FORMAT is appropriate for a variable of the given VAR_TYPE and
492 returns NULL if so. Otherwise returns a malloc()'d error message that the
493 caller must eventually free(). VARNAME is optional and only used in the
496 fmt_check_type_compat__ (const struct fmt_spec *format, const char *varname,
497 enum val_type var_type)
499 assert (val_type_is_valid (var_type));
500 if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
502 char str[FMT_STRING_LEN_MAX + 1];
503 fmt_to_string (format, str);
504 if (var_type == VAL_STRING)
507 return xasprintf (_("String variable %s is not compatible with "
508 "numeric format %s."), varname, str);
510 return xasprintf (_("String variables are not compatible with "
511 "numeric format %s."), str);
516 return xasprintf (_("Numeric variable %s is not compatible with "
517 "string format %s."), varname, str);
519 return xasprintf (_("Numeric variables are not compatible with "
520 "string format %s."), str);
526 /* Returns FORMAT is appropriate for a variable of the given VAR_TYPE and
527 returns true if so, otherwise false. */
529 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
531 return error_to_bool (fmt_check_type_compat__ (format, NULL, var_type));
534 /* Checks that FORMAT is appropriate for a variable of the given WIDTH and
535 returns NULL if so. Otherwise returns a malloc()'d error message that the
536 caller must eventually free(). VARNAME is optional and only used in the
539 fmt_check_width_compat__ (const struct fmt_spec *format, const char *varname,
542 char *error = fmt_check_type_compat__ (format, varname,
543 val_type_from_width (width));
547 if (fmt_var_width (format) != width)
549 char format_str[FMT_STRING_LEN_MAX + 1];
550 fmt_to_string (format, format_str);
552 char better_str[FMT_STRING_LEN_MAX + 1];
553 if (format->type == FMT_A)
554 snprintf (better_str, sizeof better_str, "A%d", width);
556 snprintf (better_str, sizeof better_str, "AHEX%d", width * 2);
559 return xasprintf (_("String variable %s with width %d is not "
560 "compatible with format %s. "
561 "Use format %s instead."),
562 varname, width, format_str, better_str);
564 return xasprintf (_("String variable with width %d is not compatible "
565 "with format %s. Use format %s instead."),
566 width, format_str, better_str);
572 /* Checks that FORMAT is appropriate for a variable of the given WIDTH and
573 returns true if so, otherwise false. */
575 fmt_check_width_compat (const struct fmt_spec *format, int width)
577 return error_to_bool (fmt_check_width_compat__ (format, NULL, width));
580 /* Returns the width corresponding to FORMAT. The return value
581 is the width of the `union value's required by FORMAT. */
583 fmt_var_width (const struct fmt_spec *format)
585 return (format->type == FMT_AHEX ? format->w / 2
586 : format->type == FMT_A ? format->w
590 /* Converts F to its string representation (for instance, "F8.2")
591 in BUFFER. Returns BUFFER.
593 If F has decimals, they are included in the output string,
594 even if F's format type does not allow decimals, to allow
595 accurately presenting incorrect formats to the user. */
597 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
599 if (fmt_takes_decimals (f->type) || f->d > 0)
600 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
601 "%s%d.%d", fmt_name (f->type), f->w, f->d);
603 snprintf (buffer, FMT_STRING_LEN_MAX + 1,
604 "%s%d", fmt_name (f->type), f->w);
608 /* Returns true if A and B are identical formats,
611 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
613 return a->type == b->type && a->w == b->w && a->d == b->d;
616 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
617 If nothing needed to be changed the return value is false
620 fmt_resize (struct fmt_spec *fmt, int width)
622 if ((width > 0) != fmt_is_string (fmt->type))
624 /* Changed from numeric to string or vice versa. Set to
625 default format for new width. */
626 *fmt = fmt_default_for_width (width);
630 /* Changed width of string. Preserve format type, adjust
632 fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
642 /* Adjusts FMT's width and decimal places to be valid for USE. */
644 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
646 /* Clamp width to those allowed by format. */
647 fmt_clamp_width (fmt, use);
649 /* If FMT has more decimal places than allowed, attempt to increase FMT's
650 width until that number of decimal places can be achieved. */
651 if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
652 && fmt_takes_decimals (fmt->type))
654 int max_w = fmt_max_width (fmt->type, use);
655 for (; fmt->w < max_w; fmt->w++)
656 if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
660 /* Clamp decimals to those allowed by format and width. */
661 fmt_clamp_decimals (fmt, use);
664 /* Adjusts FMT's width and decimal places to be valid for an
667 fmt_fix_input (struct fmt_spec *fmt)
669 fmt_fix (fmt, FMT_FOR_INPUT);
672 /* Adjusts FMT's width and decimal places to be valid for an
675 fmt_fix_output (struct fmt_spec *fmt)
677 fmt_fix (fmt, FMT_FOR_OUTPUT);
680 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
681 reduces its decimal places as necessary (if necessary) for that width. */
683 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
686 fmt_clamp_width (fmt, use);
687 fmt_clamp_decimals (fmt, use);
690 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
691 places allowed by FMT's type) and increases its width as necessary (if
692 necessary) for that number of decimal places. */
694 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
700 /* Describes a display format. */
704 int min_input_width, min_output_width;
706 enum fmt_category category;
709 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
711 /* Returns the name of the given format TYPE. */
713 fmt_name (enum fmt_type type)
715 return get_fmt_desc (type)->name;
718 /* Tries to parse NAME as a format type.
719 If successful, stores the type in *TYPE and returns true.
720 On failure, returns false. */
722 fmt_from_name (const char *name, enum fmt_type *type)
726 for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
727 if (!c_strcasecmp (name, get_fmt_desc (i)->name))
735 /* Returns true if TYPE accepts decimal places,
738 fmt_takes_decimals (enum fmt_type type)
740 return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
743 /* Returns the minimum width of the given format TYPE for the given USE. */
745 fmt_min_width (enum fmt_type type, enum fmt_use use)
747 return (use == FMT_FOR_INPUT
748 ? fmt_min_input_width (type)
749 : fmt_min_output_width (type));
752 /* Returns the maximum width of the given format TYPE,
753 for input if FOR_INPUT is true,
754 for output otherwise. */
756 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
758 /* Maximum width is actually invariant of whether the format is
759 for input or output, so FOR_INPUT is unused. */
760 assert (is_fmt_type (type));
778 return 2 * MAX_STRING;
785 /* Returns the maximum number of decimal places allowed for the
786 given format TYPE with a width of WIDTH places, for the given USE. */
788 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
797 max_d = use == FMT_FOR_INPUT ? width : width - 1;
802 max_d = use == FMT_FOR_INPUT ? width : width - 2;
806 max_d = use == FMT_FOR_INPUT ? width : width - 7;
814 assert (use == FMT_FOR_OUTPUT);
824 max_d = width * 2 - 1;
833 max_d = max_digits_for_bytes (width);
894 /* Returns the minimum acceptable width for an input field
895 formatted with the given TYPE. */
897 fmt_min_input_width (enum fmt_type type)
899 return get_fmt_desc (type)->min_input_width;
902 /* Returns the maximum acceptable width for an input field
903 formatted with the given TYPE. */
905 fmt_max_input_width (enum fmt_type type)
907 return fmt_max_width (type, FMT_FOR_INPUT);
910 /* Returns the maximum number of decimal places allowed in an
911 input field of the given TYPE and WIDTH. */
913 fmt_max_input_decimals (enum fmt_type type, int width)
915 assert (valid_width (type, width, true));
916 return fmt_max_decimals (type, width, FMT_FOR_INPUT);
919 /* Returns the minimum acceptable width for an output field
920 formatted with the given TYPE. */
922 fmt_min_output_width (enum fmt_type type)
924 return get_fmt_desc (type)->min_output_width;
927 /* Returns the maximum acceptable width for an output field
928 formatted with the given TYPE. */
930 fmt_max_output_width (enum fmt_type type)
932 return fmt_max_width (type, FMT_FOR_OUTPUT);
935 /* Returns the maximum number of decimal places allowed in an
936 output field of the given TYPE and WIDTH. */
938 fmt_max_output_decimals (enum fmt_type type, int width)
940 assert (valid_width (type, width, false));
941 return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
944 /* Returns the width step for a field formatted with the given
945 TYPE. Field width must be a multiple of the width step. */
947 fmt_step_width (enum fmt_type type)
949 return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
953 /* Returns true if TYPE is used for string fields,
954 false if it is used for numeric fields. */
956 fmt_is_string (enum fmt_type type)
958 return fmt_get_category (type) == FMT_CAT_STRING;
961 /* Returns true if TYPE is used for numeric fields,
962 false if it is used for string fields. */
964 fmt_is_numeric (enum fmt_type type)
966 return !fmt_is_string (type);
969 /* Returns the format TYPE's category.
970 Each format type is in exactly one category,
971 and each category's value is bitwise disjoint from every other
972 category. Thus, the return value may be tested for equality
973 or compared bitwise against a mask of FMT_CAT_* values. */
975 fmt_get_category (enum fmt_type type)
977 return get_fmt_desc (type)->category;
980 /* Returns the output format selected by default when TYPE is
981 used as an input format. */
983 fmt_input_to_output (enum fmt_type type)
985 switch (fmt_get_category (type))
992 case FMT_CAT_HEXADECIMAL:
1000 /* Returns the SPSS format type corresponding to the given PSPP
1003 fmt_to_io (enum fmt_type type)
1005 return get_fmt_desc (type)->io;
1008 /* Determines the PSPP format corresponding to the given SPSS
1009 format type. If successful, sets *FMT_TYPE to the PSPP format
1010 and returns true. On failure, return false. */
1012 fmt_from_io (int io, enum fmt_type *fmt_type)
1016 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1018 *fmt_type = FMT_##NAME; \
1020 #include "format.def"
1026 /* Translate U32, which is in the form found in SAV and SPV files, into a
1027 format specification, and stores the new specification in *F.
1029 If LOOSE is false, checks that the format specification is appropriate as an
1030 output format for a variable with the given WIDTH and reports an error if
1031 not. If LOOSE is true, instead adjusts the format's width and decimals as
1032 necessary to be suitable.
1034 Return true if successful, false if there was an error.. */
1036 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
1038 uint8_t raw_type = u32 >> 16;
1039 uint8_t w = u32 >> 8;
1043 if (!fmt_from_io (raw_type, &type))
1046 *f = (struct fmt_spec) { .type = type, .w = w, .d = d };
1050 else if (!fmt_check_output (f))
1053 return fmt_check_width_compat (f, width);
1056 /* Returns true if TYPE may be used as an input format,
1059 fmt_usable_for_input (enum fmt_type type)
1061 assert (is_fmt_type (type));
1062 return fmt_get_category (type) != FMT_CAT_CUSTOM;
1065 /* For time and date formats, returns a template used for input and output in a
1066 field of the given WIDTH.
1068 WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
1069 is, whether the returned string contains "yy" or "yyyy", and whether seconds
1070 are include, that is, whether the returned string contains ":SS". A caller
1071 that doesn't care whether the returned string contains "yy" or "yyyy" or
1072 ":SS" can just specify 0 to omit them. */
1074 fmt_date_template (enum fmt_type type, int width)
1076 const char *s1, *s2;
1121 s1 = "dd-mmm-yyyy HH:MM";
1122 s2 = "dd-mmm-yyyy HH:MM:SS";
1126 s1 = "yyyy-mm-dd HH:MM";
1127 s2 = "yyyy-mm-dd HH:MM:SS";
1149 return width >= strlen (s2) ? s2 : s1;
1152 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1154 fmt_gui_name (enum fmt_type type)
1159 return _("Numeric");
1168 return _("Scientific");
1201 return fmt_name (type);
1205 /* Returns true if TYPE is a valid format type,
1208 is_fmt_type (enum fmt_type type)
1210 return type < FMT_NUMBER_OF_FORMATS;
1213 /* Returns true if WIDTH is a valid width for the given format
1214 TYPE, for the given USE. */
1216 valid_width (enum fmt_type type, int width, enum fmt_use use)
1218 return (width >= fmt_min_width (type, use)
1219 && width <= fmt_max_width (type, use));
1222 /* Returns the maximum number of decimal digits in an unsigned
1223 binary number that is BYTES bytes long. */
1225 max_digits_for_bytes (int bytes)
1227 int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1228 assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1229 return map[bytes - 1];
1232 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1234 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1239 min_w = fmt_min_width (fmt->type, use);
1240 max_w = fmt_max_width (fmt->type, use);
1243 else if (fmt->w > max_w)
1246 /* Round width to step. */
1247 step = fmt_step_width (fmt->type);
1248 fmt->w = ROUND_DOWN (fmt->w, step);
1251 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1253 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1255 int max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1260 static struct fmt_affix
1261 fmt_affix_clone (const struct fmt_affix *old)
1263 return (struct fmt_affix) {
1264 .s = xstrdup_if_nonnull (old->s),
1265 .width = old->width,
1269 /* Frees data in AFFIX. */
1271 fmt_affix_free (struct fmt_affix *affix)
1277 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1280 find_cc_separators (const char *cc_string)
1282 /* Count commas and periods. There must be exactly three of
1283 one or the other, except that an apostrophe escapes a
1284 following comma or period. */
1287 for (const char *p = cc_string; *p; p++)
1292 else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1295 return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1300 /* Extracts a token from IN into a newly allocated string AFFIXP. Tokens are
1301 delimited by GROUPING. Returns the first character following the token. */
1302 static struct fmt_affix
1303 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1305 const char *p = *sp;
1306 for (; *p && *p != grouping; p++)
1307 if (*p == '\'' && p[1] == grouping)
1310 size_t length = p - *sp;
1311 char *affix = xmemdup0 (*sp, length);
1312 size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1314 *extra_bytes += length - width;
1316 *sp = p + (*p != 0);
1318 return (struct fmt_affix) { .s = affix, .width = width };
1321 struct fmt_number_style *
1322 fmt_number_style_from_string (const char *s)
1324 char grouping = find_cc_separators (s);
1328 size_t extra_bytes = 0;
1329 struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1330 struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1331 struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1332 struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1334 struct fmt_number_style *style = xmalloc (sizeof *style);
1335 *style = (struct fmt_number_style) {
1336 .neg_prefix = neg_prefix,
1339 .neg_suffix = neg_suffix,
1340 .decimal = grouping == '.' ? ',' : '.',
1341 .grouping = grouping,
1342 .include_leading_zero = false,
1343 .extra_bytes = extra_bytes,
1349 format_cc (struct string *out, const char *in, char grouping)
1354 if (c == grouping || c == '\'')
1355 ds_put_byte (out, '\'');
1357 ds_put_byte (out, '"');
1358 ds_put_byte (out, c);
1363 fmt_number_style_to_string (const struct fmt_number_style *cc)
1365 struct string out = DS_EMPTY_INITIALIZER;
1366 format_cc (&out, cc->neg_prefix.s, cc->grouping);
1367 ds_put_byte (&out, cc->grouping);
1368 format_cc (&out, cc->prefix.s, cc->grouping);
1369 ds_put_byte (&out, cc->grouping);
1370 format_cc (&out, cc->suffix.s, cc->grouping);
1371 ds_put_byte (&out, cc->grouping);
1372 format_cc (&out, cc->neg_suffix.s, cc->grouping);
1373 return ds_steal_cstr (&out);
1376 struct fmt_number_style *
1377 fmt_number_style_clone (const struct fmt_number_style *old)
1381 struct fmt_number_style *new = xmalloc (sizeof *new);
1382 *new = (struct fmt_number_style) {
1383 .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1384 .prefix = fmt_affix_clone (&old->prefix),
1385 .suffix = fmt_affix_clone (&old->suffix),
1386 .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1387 .decimal = old->decimal,
1388 .grouping = old->grouping,
1389 .extra_bytes = old->extra_bytes,
1397 /* Destroys a struct fmt_number_style. */
1399 fmt_number_style_destroy (struct fmt_number_style *style)
1403 fmt_affix_free (&style->neg_prefix);
1404 fmt_affix_free (&style->prefix);
1405 fmt_affix_free (&style->suffix);
1406 fmt_affix_free (&style->neg_suffix);
1411 /* Returns the total width of the standard prefix and suffix for STYLE, in
1412 display columns (e.g. as returned by u8_strwidth()). */
1414 fmt_affix_width (const struct fmt_number_style *style)
1416 return style->prefix.width + style->suffix.width;
1419 /* Returns the total width of the negative prefix and suffix for STYLE, in
1420 display columns (e.g. as returned by u8_strwidth()). */
1422 fmt_neg_affix_width (const struct fmt_number_style *style)
1424 return style->neg_prefix.width + style->neg_suffix.width;
1427 /* Returns the struct fmt_desc for the given format TYPE. */
1428 static const struct fmt_desc *
1429 get_fmt_desc (enum fmt_type type)
1431 static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1433 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1434 {#NAME, IMIN, OMIN, IO, CATEGORY},
1435 #include "format.def"
1438 assert (is_fmt_type (type));
1439 return &formats[type];
1442 const struct fmt_spec F_8_0 = { .type = FMT_F, .w = 8, .d = 0 };
1443 const struct fmt_spec F_8_2 = { .type = FMT_F, .w = 8, .d = 2 };
1444 const struct fmt_spec F_4_3 = { .type = FMT_F, .w = 4, .d = 3 };
1445 const struct fmt_spec F_5_1 = { .type = FMT_F, .w = 5, .d = 1 };