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