format: New functions fmt_change_width(), fmt_change_decimals().
[pspp] / src / data / format.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2010, 2011 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "format.h"
20
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <uniwidth.h>
24
25 #include "data/identifier.h"
26 #include "data/settings.h"
27 #include "data/value.h"
28 #include "data/variable.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/cast.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/message.h"
33 #include "libpspp/misc.h"
34 #include "libpspp/str.h"
35
36 #include "gl/minmax.h"
37 #include "gl/xalloc.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41
42 struct fmt_settings
43   {
44     struct fmt_number_style styles[FMT_NUMBER_OF_FORMATS];
45   };
46
47 bool is_fmt_type (enum fmt_type);
48
49 static bool valid_width (enum fmt_type, int width, bool for_input);
50
51 static int max_digits_for_bytes (int bytes);
52 static void fmt_clamp_width (struct fmt_spec *, bool for_input);
53 static void fmt_clamp_decimals (struct fmt_spec *, bool for_input);
54
55 static void fmt_affix_set (struct fmt_affix *, const char *);
56 static void fmt_affix_free (struct fmt_affix *);
57
58 static void fmt_number_style_init (struct fmt_number_style *);
59 static void fmt_number_style_clone (struct fmt_number_style *,
60                                     const struct fmt_number_style *);
61 static void fmt_number_style_destroy (struct fmt_number_style *);
62
63 /* Creates and returns a new struct fmt_settings with default format styles. */
64 struct fmt_settings *
65 fmt_settings_create (void)
66 {
67   struct fmt_settings *settings;
68   int t;
69
70   settings = xzalloc (sizeof *settings);
71   for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
72     fmt_number_style_init (&settings->styles[t]);
73   fmt_settings_set_decimal (settings, '.');
74
75   return settings;
76 }
77
78 /* Destroys SETTINGS. */
79 void
80 fmt_settings_destroy (struct fmt_settings *settings)
81 {
82   if (settings != NULL)
83     {
84       int t;
85
86       for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
87         fmt_number_style_destroy (&settings->styles[t]);
88
89       free (settings->styles);
90     }
91 }
92
93 /* Returns a copy of SETTINGS. */
94 struct fmt_settings *
95 fmt_settings_clone (const struct fmt_settings *old)
96 {
97   struct fmt_settings *new;
98   int t;
99
100   new = xmalloc (sizeof *new);
101   for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
102     fmt_number_style_clone (&new->styles[t], &old->styles[t]);
103
104   return new;
105 }
106
107 /* Returns the number formatting style associated with the given
108    format TYPE. */
109 const struct fmt_number_style *
110 fmt_settings_get_style (const struct fmt_settings *settings,
111                         enum fmt_type type)
112 {
113   assert (is_fmt_type (type));
114   return &settings->styles[type];
115 }
116
117 /* Sets the number style for TYPE to have the given DECIMAL and GROUPING
118    characters, negative prefix NEG_PREFIX, prefix PREFIX, suffix SUFFIX, and
119    negative suffix NEG_SUFFIX.  All of the strings are UTF-8 encoded. */
120 void
121 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
122                         char decimal, char grouping,
123                         const char *neg_prefix, const char *prefix,
124                         const char *suffix, const char *neg_suffix)
125 {
126   struct fmt_number_style *style = &settings->styles[type];
127   int total_bytes, total_width;
128
129   assert (grouping == '.' || grouping == ',' || grouping == 0);
130   assert (decimal == '.' || decimal == ',');
131   assert (decimal != grouping);
132
133   fmt_number_style_destroy (style);
134
135   fmt_affix_set (&style->neg_prefix, neg_prefix);
136   fmt_affix_set (&style->prefix, prefix);
137   fmt_affix_set (&style->suffix, suffix);
138   fmt_affix_set (&style->neg_suffix, neg_suffix);
139   style->decimal = decimal;
140   style->grouping = grouping;
141
142   total_bytes = (strlen (neg_prefix) + strlen (prefix)
143                  + strlen (suffix) + strlen (neg_suffix));
144   total_width = (style->neg_prefix.width + style->prefix.width
145                  + style->suffix.width + style->neg_suffix.width);
146   style->extra_bytes = MAX (0, total_bytes - total_width);
147 }
148
149 /* Sets the decimal point character for the settings in S to DECIMAL.
150
151    This has no effect on custom currency formats. */
152 void
153 fmt_settings_set_decimal (struct fmt_settings *s, char decimal)
154 {
155   int grouping = decimal == '.' ? ',' : '.';
156   assert (decimal == '.' || decimal == ',');
157
158   fmt_settings_set_style (s, FMT_F,      decimal,        0, "-",  "",  "", "");
159   fmt_settings_set_style (s, FMT_E,      decimal,        0, "-",  "",  "", "");
160   fmt_settings_set_style (s, FMT_COMMA,  decimal, grouping, "-",  "",  "", "");
161   fmt_settings_set_style (s, FMT_DOT,   grouping,  decimal, "-",  "",  "", "");
162   fmt_settings_set_style (s, FMT_DOLLAR, decimal, grouping, "-", "$",  "", "");
163   fmt_settings_set_style (s, FMT_PCT,    decimal,        0, "-",  "", "%", "");
164 }
165
166 /* Returns an input format specification with type TYPE, width W,
167    and D decimals. */
168 struct fmt_spec
169 fmt_for_input (enum fmt_type type, int w, int d)
170 {
171   struct fmt_spec f;
172   f.type = type;
173   f.w = w;
174   f.d = d;
175   assert (fmt_check_input (&f));
176   return f;
177 }
178
179 /* Returns an output format specification with type TYPE, width
180    W, and D decimals. */
181 struct fmt_spec
182 fmt_for_output (enum fmt_type type, int w, int d)
183 {
184   struct fmt_spec f;
185   f.type = type;
186   f.w = w;
187   f.d = d;
188   assert (fmt_check_output (&f));
189   return f;
190 }
191
192 /* Returns the output format specifier corresponding to input
193    format specifier INPUT. */
194 struct fmt_spec
195 fmt_for_output_from_input (const struct fmt_spec *input)
196 {
197   struct fmt_spec output;
198
199   assert (fmt_check_input (input));
200
201   output.type = fmt_input_to_output (input->type);
202   output.w = input->w;
203   if (output.w > fmt_max_output_width (output.type))
204     output.w = fmt_max_output_width (output.type);
205   else if (output.w < fmt_min_output_width (output.type))
206     output.w = fmt_min_output_width (output.type);
207   output.d = input->d;
208
209   switch (input->type)
210     {
211     case FMT_Z:
212       output.w++;
213       if (output.d > 0)
214         output.w++;
215       break;
216
217     case FMT_F:
218     case FMT_COMMA:
219     case FMT_DOT:
220     case FMT_DOLLAR:
221     case FMT_PCT:
222       {
223         const struct fmt_number_style *style =
224           settings_get_style (input->type);
225
226         output.w += fmt_affix_width (style);
227         if (style->grouping != 0 && input->w - input->d >= 3)
228           output.w += (input->w - input->d - 1) / 3;
229         if (output.d > 0)
230           output.w++;
231       }
232       break;
233
234     case FMT_N:
235       if (output.d > 0)
236         output.w++;
237       break;
238
239     case FMT_E:
240       output.d = MAX (input->d, 3);
241       output.w = MAX (input->w, output.d + 7);
242       break;
243
244     case FMT_PIBHEX:
245       output.w = max_digits_for_bytes (input->w / 2) + 1;
246       break;
247
248     case FMT_RB:
249     case FMT_RBHEX:
250       output.w = 8;
251       output.d = 2;
252       break;
253
254     case FMT_P:
255     case FMT_PK:
256       output.w = 2 * input->w + (input->d > 0);
257       break;
258
259     case FMT_IB:
260     case FMT_PIB:
261       output.w = max_digits_for_bytes (input->w) + 1;
262       if (output.d > 0)
263         output.w++;
264       break;
265
266     case FMT_CCA:
267     case FMT_CCB:
268     case FMT_CCC:
269     case FMT_CCD:
270     case FMT_CCE:
271       NOT_REACHED ();
272
273     case FMT_A:
274       break;
275
276     case FMT_AHEX:
277       output.w = input->w / 2;
278       break;
279
280     case FMT_DATE:
281     case FMT_EDATE:
282     case FMT_SDATE:
283     case FMT_ADATE:
284     case FMT_JDATE:
285     case FMT_QYR:
286     case FMT_MOYR:
287     case FMT_WKYR:
288     case FMT_TIME:
289     case FMT_DTIME:
290     case FMT_DATETIME:
291     case FMT_WKDAY:
292     case FMT_MONTH:
293       break;
294
295     default:
296       NOT_REACHED ();
297     }
298
299   if (output.w > fmt_max_output_width (output.type))
300     output.w = fmt_max_output_width (output.type);
301
302   assert (fmt_check_output (&output));
303   return output;
304 }
305
306 /* Returns the default format for the given WIDTH: F8.2 format
307    for a numeric value, A format for a string value. */
308 struct fmt_spec
309 fmt_default_for_width (int width)
310 {
311   return (width == 0
312           ? fmt_for_output (FMT_F, 8, 2)
313           : fmt_for_output (FMT_A, width, 0));
314 }
315
316 /* Checks whether SPEC is valid as an input format (if FOR_INPUT)
317    or an output format (otherwise) and returns nonzero if so.
318    Otherwise, emits an error message and returns zero. */
319 bool
320 fmt_check (const struct fmt_spec *spec, bool for_input)
321 {
322   const char *io_fmt = for_input ? _("Input format") : _("Output format");
323   char str[FMT_STRING_LEN_MAX + 1];
324   int min_w, max_w, max_d;
325
326   assert (is_fmt_type (spec->type));
327   fmt_to_string (spec, str);
328
329   if (for_input && !fmt_usable_for_input (spec->type))
330     {
331       msg (SE, _("Format %s may not be used for input."), str);
332       return false;
333     }
334
335   if (spec->w % fmt_step_width (spec->type))
336     {
337       assert (fmt_step_width (spec->type) == 2);
338       msg (SE, _("%s specifies width %d, but %s requires an even width."),
339            str, spec->w, fmt_name (spec->type));
340       return false;
341     }
342
343   min_w = fmt_min_width (spec->type, for_input);
344   max_w = fmt_max_width (spec->type, for_input);
345   if (spec->w < min_w || spec->w > max_w)
346     {
347       msg (SE, _("%s %s specifies width %d, but "
348                  "%s requires a width between %d and %d."),
349            io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
350       return false;
351     }
352
353   max_d = fmt_max_decimals (spec->type, spec->w, for_input);
354   if (!fmt_takes_decimals (spec->type) && spec->d != 0)
355     {
356       msg (SE, ngettext ("%s %s specifies %d decimal place, but "
357                          "%s does not allow any decimals.",
358                          "%s %s specifies %d decimal places, but "
359                          "%s does not allow any decimals.",
360                          spec->d),
361            io_fmt, str, spec->d, fmt_name (spec->type));
362       return false;
363     }
364   else if (spec->d > max_d)
365     {
366       if (max_d > 0)
367         msg (SE, ngettext ("%s %s specifies %d decimal place, but "
368                            "the given width allows at most %d decimals.",
369                            "%s %s specifies %d decimal places, but "
370                            "the given width allows at most %d decimals.",
371                            spec->d),
372              io_fmt, str, spec->d, max_d);
373       else
374         msg (SE, ngettext ("%s %s specifies %d decimal place, but "
375                            "the given width does not allow for any decimals.",
376                            "%s %s specifies %d decimal places, but "
377                            "the given width does not allow for any decimals.",
378                            spec->d),
379              io_fmt, str, spec->d);
380       return false;
381     }
382
383   return true;
384 }
385
386 /* Checks whether SPEC is valid as an input format and returns
387    nonzero if so.  Otherwise, emits an error message and returns
388    zero. */
389 bool
390 fmt_check_input (const struct fmt_spec *spec)
391 {
392   return fmt_check (spec, true);
393 }
394
395 /* Checks whether SPEC is valid as an output format and returns
396    true if so.  Otherwise, emits an error message and returns false. */
397 bool
398 fmt_check_output (const struct fmt_spec *spec)
399 {
400   return fmt_check (spec, false);
401 }
402
403 /* Checks that FORMAT is appropriate for a variable of the given
404    VAR_TYPE and returns true if so.  Otherwise returns false and
405    emits an error message. */
406 bool
407 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
408 {
409   assert (val_type_is_valid (var_type));
410   if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
411     {
412       char str[FMT_STRING_LEN_MAX + 1];
413       msg (SE, _("%s variables are not compatible with %s format %s."),
414            var_type == VAL_STRING ? _("String") : _("Numeric"),
415            var_type == VAL_STRING ? _("numeric") : _("string"),
416            fmt_to_string (format, str));
417       return false;
418     }
419   return true;
420 }
421
422 /* Checks that FORMAT is appropriate for a variable of the given
423    WIDTH and returns true if so.  Otherwise returns false and
424    emits an error message. */
425 bool
426 fmt_check_width_compat (const struct fmt_spec *format, int width)
427 {
428   if (!fmt_check_type_compat (format, val_type_from_width (width)))
429     return false;
430   if (fmt_var_width (format) != width)
431     {
432       char str[FMT_STRING_LEN_MAX + 1];
433       msg (SE, _("String variable with width %d is not compatible with "
434                  "format %s."),
435            width, fmt_to_string (format, str));
436       return false;
437     }
438   return true;
439 }
440
441 /* Returns the width corresponding to FORMAT.  The return value
442    is the width of the `union value's required by FORMAT. */
443 int
444 fmt_var_width (const struct fmt_spec *format)
445 {
446   return (format->type == FMT_AHEX ? format->w / 2
447           : format->type == FMT_A ? format->w
448           : 0);
449 }
450
451 /* Converts F to its string representation (for instance, "F8.2")
452    in BUFFER.  Returns BUFFER.
453
454    If F has decimals, they are included in the output string,
455    even if F's format type does not allow decimals, to allow
456    accurately presenting incorrect formats to the user. */
457 char *
458 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
459 {
460   if (fmt_takes_decimals (f->type) || f->d > 0)
461     snprintf (buffer, FMT_STRING_LEN_MAX + 1,
462               "%s%d.%d", fmt_name (f->type), f->w, f->d);
463   else
464     snprintf (buffer, FMT_STRING_LEN_MAX + 1,
465               "%s%d", fmt_name (f->type), f->w);
466   return buffer;
467 }
468
469 /* Returns true if A and B are identical formats,
470    false otherwise. */
471 bool
472 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
473 {
474   return a->type == b->type && a->w == b->w && a->d == b->d;
475 }
476
477 /* Adjusts FMT to be valid for a value of the given WIDTH. */
478 void
479 fmt_resize (struct fmt_spec *fmt, int width)
480 {
481   if ((width > 0) != fmt_is_string (fmt->type))
482     {
483       /* Changed from numeric to string or vice versa.  Set to
484          default format for new width. */
485       *fmt = fmt_default_for_width (width);
486     }
487   else if (width > 0)
488     {
489       /* Changed width of string.  Preserve format type, adjust
490          width. */
491       fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
492     }
493   else
494     {
495       /* Still numeric. */
496     }
497 }
498
499 /* Adjusts FMT's width and decimal places to be valid for an
500    input format (if FOR_INPUT) or an output format (if
501    !FOR_INPUT).  */
502 void
503 fmt_fix (struct fmt_spec *fmt, bool for_input)
504 {
505   /* Clamp width to those allowed by format. */
506   fmt_clamp_width (fmt, for_input);
507
508   /* If FMT has more decimal places than allowed, attempt to increase FMT's
509      width until that number of decimal places can be achieved. */
510   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input)
511       && fmt_takes_decimals (fmt->type))
512     {
513       int max_w = fmt_max_width (fmt->type, for_input);
514       for (; fmt->w < max_w; fmt->w++)
515         if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, for_input))
516           break;
517     }
518
519   /* Clamp decimals to those allowed by format and width. */
520   fmt_clamp_decimals (fmt, for_input);
521 }
522
523 /* Adjusts FMT's width and decimal places to be valid for an
524    input format.  */
525 void
526 fmt_fix_input (struct fmt_spec *fmt)
527 {
528   fmt_fix (fmt, true);
529 }
530
531 /* Adjusts FMT's width and decimal places to be valid for an
532    output format.  */
533 void
534 fmt_fix_output (struct fmt_spec *fmt)
535 {
536   fmt_fix (fmt, false);
537 }
538
539 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
540    reduces its decimal places as necessary (if necessary) for that width.  */
541 void
542 fmt_change_width (struct fmt_spec *fmt, int width, bool for_input)
543 {
544   fmt->w = width;
545   fmt_clamp_width (fmt, for_input);
546   fmt_clamp_decimals (fmt, for_input);
547 }
548
549 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
550    places allowed by FMT's type) and increases its width as necessary (if
551    necessary) for that number of decimal places.  */
552 void
553 fmt_change_decimals (struct fmt_spec *fmt, int decimals, bool for_input)
554 {
555   fmt->d = decimals;
556   fmt_fix (fmt, for_input);
557 }
558 \f
559 /* Describes a display format. */
560 struct fmt_desc
561   {
562     char name[9];
563     int min_input_width, min_output_width;
564     int io;
565     enum fmt_category category;
566   };
567
568 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
569
570 /* Returns the name of the given format TYPE. */
571 const char *
572 fmt_name (enum fmt_type type)
573 {
574   return get_fmt_desc (type)->name;
575 }
576
577 /* Tries to parse NAME as a format type.
578    If successful, stores the type in *TYPE and returns true.
579    On failure, returns false. */
580 bool
581 fmt_from_name (const char *name, enum fmt_type *type)
582 {
583   int i;
584
585   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
586     if (!strcasecmp (name, get_fmt_desc (i)->name))
587       {
588         *type = i;
589         return true;
590       }
591   return false;
592 }
593
594 /* Returns true if TYPE accepts decimal places,
595    false otherwise. */
596 bool
597 fmt_takes_decimals (enum fmt_type type)
598 {
599   return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
600 }
601
602 /* Returns the minimum width of the given format TYPE,
603    for input if FOR_INPUT is true,
604    for output otherwise. */
605 int
606 fmt_min_width (enum fmt_type type, bool for_input)
607 {
608   return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
609 }
610
611 /* Returns the maximum width of the given format TYPE,
612    for input if FOR_INPUT is true,
613    for output otherwise. */
614 int
615 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
616 {
617   /* Maximum width is actually invariant of whether the format is
618      for input or output, so FOR_INPUT is unused. */
619   assert (is_fmt_type (type));
620   switch (type)
621     {
622     case FMT_P:
623     case FMT_PK:
624     case FMT_PIBHEX:
625     case FMT_RBHEX:
626       return 16;
627
628     case FMT_IB:
629     case FMT_PIB:
630     case FMT_RB:
631       return 8;
632
633     case FMT_A:
634       return MAX_STRING;
635
636     case FMT_AHEX:
637       return 2 * MAX_STRING;
638
639     default:
640       return 40;
641     }
642 }
643
644 /* Returns the maximum number of decimal places allowed for the
645    given format TYPE with a width of WIDTH places,
646    for input if FOR_INPUT is true,
647    for output otherwise. */
648 int
649 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
650 {
651   int max_d;
652
653   switch (type)
654     {
655     case FMT_F:
656     case FMT_COMMA:
657     case FMT_DOT:
658       max_d = for_input ? width : width - 1;
659       break;
660
661     case FMT_DOLLAR:
662     case FMT_PCT:
663       max_d = for_input ? width : width - 2;
664       break;
665
666     case FMT_E:
667       max_d = for_input ? width : width - 7;
668       break;
669
670     case FMT_CCA:
671     case FMT_CCB:
672     case FMT_CCC:
673     case FMT_CCD:
674     case FMT_CCE:
675       assert (!for_input);
676       max_d = width - 1;
677       break;
678
679     case FMT_N:
680     case FMT_Z:
681       max_d = width;
682       break;
683
684     case FMT_P:
685       max_d = width * 2 - 1;
686       break;
687
688     case FMT_PK:
689       max_d = width * 2;
690       break;
691
692     case FMT_IB:
693     case FMT_PIB:
694       max_d = max_digits_for_bytes (width);
695       break;
696
697     case FMT_PIBHEX:
698       max_d = 0;
699       break;
700
701     case FMT_RB:
702     case FMT_RBHEX:
703       max_d = 16;
704       break;
705
706     case FMT_DATE:
707     case FMT_ADATE:
708     case FMT_EDATE:
709     case FMT_JDATE:
710     case FMT_SDATE:
711     case FMT_QYR:
712     case FMT_MOYR:
713     case FMT_WKYR:
714       max_d = 0;
715       break;
716
717     case FMT_DATETIME:
718       max_d = width - 21;
719       break;
720
721     case FMT_TIME:
722       max_d = width - 9;
723       break;
724
725     case FMT_DTIME:
726       max_d = width - 12;
727       break;
728
729     case FMT_WKDAY:
730     case FMT_MONTH:
731     case FMT_A:
732     case FMT_AHEX:
733       max_d = 0;
734       break;
735
736     default:
737       NOT_REACHED ();
738     }
739
740   if (max_d < 0)
741     max_d = 0;
742   else if (max_d > 16)
743     max_d = 16;
744   return max_d;
745 }
746
747 /* Returns the minimum acceptable width for an input field
748    formatted with the given TYPE. */
749 int
750 fmt_min_input_width (enum fmt_type type)
751 {
752   return get_fmt_desc (type)->min_input_width;
753 }
754
755 /* Returns the maximum acceptable width for an input field
756    formatted with the given TYPE. */
757 int
758 fmt_max_input_width (enum fmt_type type)
759 {
760   return fmt_max_width (type, true);
761 }
762
763 /* Returns the maximum number of decimal places allowed in an
764    input field of the given TYPE and WIDTH. */
765 int
766 fmt_max_input_decimals (enum fmt_type type, int width)
767 {
768   assert (valid_width (type, width, true));
769   return fmt_max_decimals (type, width, true);
770 }
771
772 /* Returns the minimum acceptable width for an output field
773    formatted with the given TYPE. */
774 int
775 fmt_min_output_width (enum fmt_type type)
776 {
777   return get_fmt_desc (type)->min_output_width;
778 }
779
780 /* Returns the maximum acceptable width for an output field
781    formatted with the given TYPE. */
782 int
783 fmt_max_output_width (enum fmt_type type)
784 {
785   return fmt_max_width (type, false);
786 }
787
788 /* Returns the maximum number of decimal places allowed in an
789    output field of the given TYPE and WIDTH. */
790 int
791 fmt_max_output_decimals (enum fmt_type type, int width)
792 {
793   assert (valid_width (type, width, false));
794   return fmt_max_decimals (type, width, false);
795 }
796
797 /* Returns the width step for a field formatted with the given
798    TYPE.  Field width must be a multiple of the width step. */
799 int
800 fmt_step_width (enum fmt_type type)
801 {
802   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
803           ? 2 : 1);
804 }
805
806 /* Returns true if TYPE is used for string fields,
807    false if it is used for numeric fields. */
808 bool
809 fmt_is_string (enum fmt_type type)
810 {
811   return fmt_get_category (type) == FMT_CAT_STRING;
812 }
813
814 /* Returns true if TYPE is used for numeric fields,
815    false if it is used for string fields. */
816 bool
817 fmt_is_numeric (enum fmt_type type)
818 {
819   return !fmt_is_string (type);
820 }
821
822 /* Returns the format TYPE's category.
823    Each format type is in exactly one category,
824    and each category's value is bitwise disjoint from every other
825    category.  Thus, the return value may be tested for equality
826    or compared bitwise against a mask of FMT_CAT_* values. */
827 enum fmt_category
828 fmt_get_category (enum fmt_type type)
829 {
830   return get_fmt_desc (type)->category;
831 }
832
833 /* Returns the output format selected by default when TYPE is
834    used as an input format. */
835 enum fmt_type
836 fmt_input_to_output (enum fmt_type type)
837 {
838   switch (fmt_get_category (type))
839     {
840     case FMT_CAT_STRING:
841       return FMT_A;
842
843     case FMT_CAT_LEGACY:
844     case FMT_CAT_BINARY:
845     case FMT_CAT_HEXADECIMAL:
846       return FMT_F;
847
848     default:
849       return type;
850     }
851 }
852
853 /* Returns the SPSS format type corresponding to the given PSPP
854    format type. */
855 int
856 fmt_to_io (enum fmt_type type)
857 {
858   return get_fmt_desc (type)->io;
859 }
860
861 /* Determines the PSPP format corresponding to the given SPSS
862    format type.  If successful, sets *FMT_TYPE to the PSPP format
863    and returns true.  On failure, return false. */
864 bool
865 fmt_from_io (int io, enum fmt_type *fmt_type)
866 {
867   switch (io)
868     {
869 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY)     \
870     case IO:                                          \
871       *fmt_type = FMT_##NAME;                           \
872       return true;
873 #include "format.def"
874     default:
875       return false;
876     }
877 }
878
879 /* Returns true if TYPE may be used as an input format,
880    false otherwise. */
881 bool
882 fmt_usable_for_input (enum fmt_type type)
883 {
884   assert (is_fmt_type (type));
885   return fmt_get_category (type) != FMT_CAT_CUSTOM;
886 }
887
888 /* For time and date formats, returns a template used for input
889    and output. */
890 const char *
891 fmt_date_template (enum fmt_type type)
892 {
893   switch (type)
894     {
895     case FMT_DATE:
896       return "dd-mmm-yy";
897     case FMT_ADATE:
898       return "mm/dd/yy";
899     case FMT_EDATE:
900       return "dd.mm.yy";
901     case FMT_JDATE:
902       return "yyddd";
903     case FMT_SDATE:
904       return "yy/mm/dd";
905     case FMT_QYR:
906       return "q Q yy";
907     case FMT_MOYR:
908       return "mmmXyy";
909     case FMT_WKYR:
910       return "ww WK yy";
911     case FMT_DATETIME:
912       return "dd-mmm-yyyy HH:MM";
913     case FMT_TIME:
914       return "H:MM";
915     case FMT_DTIME:
916       return "D HH:MM";
917     default:
918       NOT_REACHED ();
919     }
920 }
921
922 /* Returns a string representing the format TYPE for use in a GUI dialog. */
923 const char *
924 fmt_gui_name (enum fmt_type type)
925 {
926   switch (type)
927     {
928     case FMT_F:
929       return _("Numeric");
930
931     case FMT_COMMA:
932       return _("Comma");
933
934     case FMT_DOT:
935       return _("Dot");
936
937     case FMT_E:
938       return _("Scientific");
939
940     case FMT_DATE:
941     case FMT_EDATE:
942     case FMT_SDATE:
943     case FMT_ADATE:
944     case FMT_JDATE:
945     case FMT_QYR:
946     case FMT_MOYR:
947     case FMT_WKYR:
948     case FMT_DATETIME:
949     case FMT_TIME:
950     case FMT_DTIME:
951     case FMT_WKDAY:
952     case FMT_MONTH:
953       return _("Date");
954
955     case FMT_DOLLAR:
956       return _("Dollar");
957
958     case FMT_CCA:
959     case FMT_CCB:
960     case FMT_CCC:
961     case FMT_CCD:
962     case FMT_CCE:
963       return _("Custom");
964
965     case FMT_A:
966       return _("String");
967
968     default:
969       return fmt_name (type);
970     }
971 }
972 \f
973 /* Returns true if TYPE is a valid format type,
974    false otherwise. */
975 bool
976 is_fmt_type (enum fmt_type type)
977 {
978   return type < FMT_NUMBER_OF_FORMATS;
979 }
980
981 /* Returns true if WIDTH is a valid width for the given format
982    TYPE,
983    for input if FOR_INPUT is true,
984    for output otherwise. */
985 static bool
986 valid_width (enum fmt_type type, int width, bool for_input)
987 {
988   return (width >= fmt_min_width (type, for_input)
989           && width <= fmt_max_width (type, for_input));
990 }
991
992 /* Returns the maximum number of decimal digits in an unsigned
993    binary number that is BYTES bytes long. */
994 static int
995 max_digits_for_bytes (int bytes)
996 {
997   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
998   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
999   return map[bytes - 1];
1000 }
1001
1002 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1003 static void
1004 fmt_clamp_width (struct fmt_spec *fmt, bool for_input)
1005 {
1006   unsigned int step;
1007   int min_w, max_w;
1008
1009   min_w = fmt_min_width (fmt->type, for_input);
1010   max_w = fmt_max_width (fmt->type, for_input);
1011   if (fmt->w < min_w)
1012     fmt->w = min_w;
1013   else if (fmt->w > max_w)
1014     fmt->w = max_w;
1015
1016   /* Round width to step. */
1017   step = fmt_step_width (fmt->type);
1018   fmt->w = ROUND_DOWN (fmt->w, step);
1019 }
1020
1021 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1022 static void
1023 fmt_clamp_decimals (struct fmt_spec *fmt, bool for_input)
1024 {
1025   int max_d;
1026
1027   max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
1028   if (fmt->d < 0)
1029     fmt->d = 0;
1030   else if (fmt->d > max_d)
1031     fmt->d = max_d;
1032 }
1033 \f
1034 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
1035 static void
1036 fmt_affix_set (struct fmt_affix *affix, const char *s)
1037 {
1038   affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1039   affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1040 }
1041
1042 /* Frees data in AFFIX. */
1043 static void
1044 fmt_affix_free (struct fmt_affix *affix)
1045 {
1046   if (affix->s[0])
1047     free (affix->s);
1048 }
1049
1050 static void
1051 fmt_number_style_init (struct fmt_number_style *style)
1052 {
1053   fmt_affix_set (&style->neg_prefix, "");
1054   fmt_affix_set (&style->prefix, "");
1055   fmt_affix_set (&style->suffix, "");
1056   fmt_affix_set (&style->neg_suffix, "");
1057   style->decimal = '.';
1058   style->grouping = 0;
1059 }
1060
1061 static void
1062 fmt_number_style_clone (struct fmt_number_style *new,
1063                         const struct fmt_number_style *old)
1064 {
1065   fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1066   fmt_affix_set (&new->prefix, old->prefix.s);
1067   fmt_affix_set (&new->suffix, old->suffix.s);
1068   fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1069   new->decimal = old->decimal;
1070   new->grouping = old->grouping;
1071   new->extra_bytes = old->extra_bytes;
1072 }
1073
1074 /* Destroys a struct fmt_number_style. */
1075 static void
1076 fmt_number_style_destroy (struct fmt_number_style *style)
1077 {
1078   if (style != NULL)
1079     {
1080       fmt_affix_free (&style->neg_prefix);
1081       fmt_affix_free (&style->prefix);
1082       fmt_affix_free (&style->suffix);
1083       fmt_affix_free (&style->neg_suffix);
1084     }
1085 }
1086
1087 /* Returns the total width of the standard prefix and suffix for STYLE, in
1088    display columns (e.g. as returned by u8_strwidth()). */
1089 int
1090 fmt_affix_width (const struct fmt_number_style *style)
1091 {
1092   return style->prefix.width + style->suffix.width;
1093 }
1094
1095 /* Returns the total width of the negative prefix and suffix for STYLE, in
1096    display columns (e.g. as returned by u8_strwidth()). */
1097 int
1098 fmt_neg_affix_width (const struct fmt_number_style *style)
1099 {
1100   return style->neg_prefix.width + style->neg_suffix.width;
1101 }
1102
1103 /* Returns the struct fmt_desc for the given format TYPE. */
1104 static const struct fmt_desc *
1105 get_fmt_desc (enum fmt_type type)
1106 {
1107   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1108     {
1109 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1110         {#NAME, IMIN, OMIN, IO, CATEGORY},
1111 #include "format.def"
1112     };
1113
1114   assert (is_fmt_type (type));
1115   return &formats[type];
1116 }
1117
1118 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};