3c407f4a85f3c0bb842e41a3cca37098b1a5850a
[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   /* If FMT has more decimal places than allowed, attempt to increase FMT's
520      width until that number of decimal places can be achieved. */
521   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input)
522       && fmt_takes_decimals (fmt->type))
523     {
524       int max_w = fmt_max_width (fmt->type, for_input);
525       for (; fmt->w < max_w; fmt->w++)
526         if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, for_input))
527           break;
528     }
529
530   /* Clamp decimals to those allowed by format and width. */
531   max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
532   if (fmt->d < 0)
533     fmt->d = 0;
534   else if (fmt->d > max_d)
535     fmt->d = max_d;
536 }
537
538 /* Adjusts FMT's width and decimal places to be valid for an
539    input format.  */
540 void
541 fmt_fix_input (struct fmt_spec *fmt)
542 {
543   fmt_fix (fmt, true);
544 }
545
546 /* Adjusts FMT's width and decimal places to be valid for an
547    output format.  */
548 void
549 fmt_fix_output (struct fmt_spec *fmt)
550 {
551   fmt_fix (fmt, false);
552 }
553 \f
554 /* Describes a display format. */
555 struct fmt_desc
556   {
557     char name[9];
558     int min_input_width, min_output_width;
559     int io;
560     enum fmt_category category;
561   };
562
563 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
564
565 /* Returns the name of the given format TYPE. */
566 const char *
567 fmt_name (enum fmt_type type)
568 {
569   return get_fmt_desc (type)->name;
570 }
571
572 /* Tries to parse NAME as a format type.
573    If successful, stores the type in *TYPE and returns true.
574    On failure, returns false. */
575 bool
576 fmt_from_name (const char *name, enum fmt_type *type)
577 {
578   int i;
579
580   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
581     if (!strcasecmp (name, get_fmt_desc (i)->name))
582       {
583         *type = i;
584         return true;
585       }
586   return false;
587 }
588
589 /* Returns true if TYPE accepts decimal places,
590    false otherwise. */
591 bool
592 fmt_takes_decimals (enum fmt_type type)
593 {
594   return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
595 }
596
597 /* Returns the minimum width of the given format TYPE,
598    for input if FOR_INPUT is true,
599    for output otherwise. */
600 int
601 fmt_min_width (enum fmt_type type, bool for_input)
602 {
603   return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
604 }
605
606 /* Returns the maximum width of the given format TYPE,
607    for input if FOR_INPUT is true,
608    for output otherwise. */
609 int
610 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
611 {
612   /* Maximum width is actually invariant of whether the format is
613      for input or output, so FOR_INPUT is unused. */
614   assert (is_fmt_type (type));
615   switch (type)
616     {
617     case FMT_P:
618     case FMT_PK:
619     case FMT_PIBHEX:
620     case FMT_RBHEX:
621       return 16;
622
623     case FMT_IB:
624     case FMT_PIB:
625     case FMT_RB:
626       return 8;
627
628     case FMT_A:
629       return MAX_STRING;
630
631     case FMT_AHEX:
632       return 2 * MAX_STRING;
633
634     default:
635       return 40;
636     }
637 }
638
639 /* Returns the maximum number of decimal places allowed for the
640    given format TYPE with a width of WIDTH places,
641    for input if FOR_INPUT is true,
642    for output otherwise. */
643 int
644 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
645 {
646   int max_d;
647
648   switch (type)
649     {
650     case FMT_F:
651     case FMT_COMMA:
652     case FMT_DOT:
653       max_d = for_input ? width : width - 1;
654       break;
655
656     case FMT_DOLLAR:
657     case FMT_PCT:
658       max_d = for_input ? width : width - 2;
659       break;
660
661     case FMT_E:
662       max_d = for_input ? width : width - 7;
663       break;
664
665     case FMT_CCA:
666     case FMT_CCB:
667     case FMT_CCC:
668     case FMT_CCD:
669     case FMT_CCE:
670       assert (!for_input);
671       max_d = width - 1;
672       break;
673
674     case FMT_N:
675     case FMT_Z:
676       max_d = width;
677       break;
678
679     case FMT_P:
680       max_d = width * 2 - 1;
681       break;
682
683     case FMT_PK:
684       max_d = width * 2;
685       break;
686
687     case FMT_IB:
688     case FMT_PIB:
689       max_d = max_digits_for_bytes (width);
690       break;
691
692     case FMT_PIBHEX:
693       max_d = 0;
694       break;
695
696     case FMT_RB:
697     case FMT_RBHEX:
698       max_d = 16;
699       break;
700
701     case FMT_DATE:
702     case FMT_ADATE:
703     case FMT_EDATE:
704     case FMT_JDATE:
705     case FMT_SDATE:
706     case FMT_QYR:
707     case FMT_MOYR:
708     case FMT_WKYR:
709       max_d = 0;
710       break;
711
712     case FMT_DATETIME:
713       max_d = width - 21;
714       break;
715
716     case FMT_TIME:
717       max_d = width - 9;
718       break;
719
720     case FMT_DTIME:
721       max_d = width - 12;
722       break;
723
724     case FMT_WKDAY:
725     case FMT_MONTH:
726     case FMT_A:
727     case FMT_AHEX:
728       max_d = 0;
729       break;
730
731     default:
732       NOT_REACHED ();
733     }
734
735   if (max_d < 0)
736     max_d = 0;
737   else if (max_d > 16)
738     max_d = 16;
739   return max_d;
740 }
741
742 /* Returns the minimum acceptable width for an input field
743    formatted with the given TYPE. */
744 int
745 fmt_min_input_width (enum fmt_type type)
746 {
747   return get_fmt_desc (type)->min_input_width;
748 }
749
750 /* Returns the maximum acceptable width for an input field
751    formatted with the given TYPE. */
752 int
753 fmt_max_input_width (enum fmt_type type)
754 {
755   return fmt_max_width (type, true);
756 }
757
758 /* Returns the maximum number of decimal places allowed in an
759    input field of the given TYPE and WIDTH. */
760 int
761 fmt_max_input_decimals (enum fmt_type type, int width)
762 {
763   assert (valid_width (type, width, true));
764   return fmt_max_decimals (type, width, true);
765 }
766
767 /* Returns the minimum acceptable width for an output field
768    formatted with the given TYPE. */
769 int
770 fmt_min_output_width (enum fmt_type type)
771 {
772   return get_fmt_desc (type)->min_output_width;
773 }
774
775 /* Returns the maximum acceptable width for an output field
776    formatted with the given TYPE. */
777 int
778 fmt_max_output_width (enum fmt_type type)
779 {
780   return fmt_max_width (type, false);
781 }
782
783 /* Returns the maximum number of decimal places allowed in an
784    output field of the given TYPE and WIDTH. */
785 int
786 fmt_max_output_decimals (enum fmt_type type, int width)
787 {
788   assert (valid_width (type, width, false));
789   return fmt_max_decimals (type, width, false);
790 }
791
792 /* Returns the width step for a field formatted with the given
793    TYPE.  Field width must be a multiple of the width step. */
794 int
795 fmt_step_width (enum fmt_type type)
796 {
797   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
798           ? 2 : 1);
799 }
800
801 /* Returns true if TYPE is used for string fields,
802    false if it is used for numeric fields. */
803 bool
804 fmt_is_string (enum fmt_type type)
805 {
806   return fmt_get_category (type) == FMT_CAT_STRING;
807 }
808
809 /* Returns true if TYPE is used for numeric fields,
810    false if it is used for string fields. */
811 bool
812 fmt_is_numeric (enum fmt_type type)
813 {
814   return !fmt_is_string (type);
815 }
816
817 /* Returns the format TYPE's category.
818    Each format type is in exactly one category,
819    and each category's value is bitwise disjoint from every other
820    category.  Thus, the return value may be tested for equality
821    or compared bitwise against a mask of FMT_CAT_* values. */
822 enum fmt_category
823 fmt_get_category (enum fmt_type type)
824 {
825   return get_fmt_desc (type)->category;
826 }
827
828 /* Returns the output format selected by default when TYPE is
829    used as an input format. */
830 enum fmt_type
831 fmt_input_to_output (enum fmt_type type)
832 {
833   switch (fmt_get_category (type))
834     {
835     case FMT_CAT_STRING:
836       return FMT_A;
837
838     case FMT_CAT_LEGACY:
839     case FMT_CAT_BINARY:
840     case FMT_CAT_HEXADECIMAL:
841       return FMT_F;
842
843     default:
844       return type;
845     }
846 }
847
848 /* Returns the SPSS format type corresponding to the given PSPP
849    format type. */
850 int
851 fmt_to_io (enum fmt_type type)
852 {
853   return get_fmt_desc (type)->io;
854 }
855
856 /* Determines the PSPP format corresponding to the given SPSS
857    format type.  If successful, sets *FMT_TYPE to the PSPP format
858    and returns true.  On failure, return false. */
859 bool
860 fmt_from_io (int io, enum fmt_type *fmt_type)
861 {
862   switch (io)
863     {
864 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY)     \
865     case IO:                                          \
866       *fmt_type = FMT_##NAME;                           \
867       return true;
868 #include "format.def"
869     default:
870       return false;
871     }
872 }
873
874 /* Returns true if TYPE may be used as an input format,
875    false otherwise. */
876 bool
877 fmt_usable_for_input (enum fmt_type type)
878 {
879   assert (is_fmt_type (type));
880   return fmt_get_category (type) != FMT_CAT_CUSTOM;
881 }
882
883 /* For time and date formats, returns a template used for input
884    and output. */
885 const char *
886 fmt_date_template (enum fmt_type type)
887 {
888   switch (type)
889     {
890     case FMT_DATE:
891       return "dd-mmm-yy";
892     case FMT_ADATE:
893       return "mm/dd/yy";
894     case FMT_EDATE:
895       return "dd.mm.yy";
896     case FMT_JDATE:
897       return "yyddd";
898     case FMT_SDATE:
899       return "yy/mm/dd";
900     case FMT_QYR:
901       return "q Q yy";
902     case FMT_MOYR:
903       return "mmmXyy";
904     case FMT_WKYR:
905       return "ww WK yy";
906     case FMT_DATETIME:
907       return "dd-mmm-yyyy HH:MM";
908     case FMT_TIME:
909       return "H:MM";
910     case FMT_DTIME:
911       return "D HH:MM";
912     default:
913       NOT_REACHED ();
914     }
915 }
916
917 /* Returns a string representing the format TYPE for use in a GUI dialog. */
918 const char *
919 fmt_gui_name (enum fmt_type type)
920 {
921   switch (type)
922     {
923     case FMT_F:
924       return _("Numeric");
925
926     case FMT_COMMA:
927       return _("Comma");
928
929     case FMT_DOT:
930       return _("Dot");
931
932     case FMT_E:
933       return _("Scientific");
934
935     case FMT_DATE:
936     case FMT_EDATE:
937     case FMT_SDATE:
938     case FMT_ADATE:
939     case FMT_JDATE:
940     case FMT_QYR:
941     case FMT_MOYR:
942     case FMT_WKYR:
943     case FMT_DATETIME:
944     case FMT_TIME:
945     case FMT_DTIME:
946     case FMT_WKDAY:
947     case FMT_MONTH:
948       return _("Date");
949
950     case FMT_DOLLAR:
951       return _("Dollar");
952
953     case FMT_CCA:
954     case FMT_CCB:
955     case FMT_CCC:
956     case FMT_CCD:
957     case FMT_CCE:
958       return _("Custom");
959
960     case FMT_A:
961       return _("String");
962
963     default:
964       return fmt_name (type);
965     }
966 }
967 \f
968 /* Returns true if TYPE is a valid format type,
969    false otherwise. */
970 bool
971 is_fmt_type (enum fmt_type type)
972 {
973   return type < FMT_NUMBER_OF_FORMATS;
974 }
975
976 /* Returns true if WIDTH is a valid width for the given format
977    TYPE,
978    for input if FOR_INPUT is true,
979    for output otherwise. */
980 static bool
981 valid_width (enum fmt_type type, int width, bool for_input)
982 {
983   return (width >= fmt_min_width (type, for_input)
984           && width <= fmt_max_width (type, for_input));
985 }
986
987 /* Returns the maximum number of decimal digits in an unsigned
988    binary number that is BYTES bytes long. */
989 static int
990 max_digits_for_bytes (int bytes)
991 {
992   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
993   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
994   return map[bytes - 1];
995 }
996 \f
997 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
998 static void
999 fmt_affix_set (struct fmt_affix *affix, const char *s)
1000 {
1001   affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1002   affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1003 }
1004
1005 /* Frees data in AFFIX. */
1006 static void
1007 fmt_affix_free (struct fmt_affix *affix)
1008 {
1009   if (affix->s[0])
1010     free (affix->s);
1011 }
1012
1013 static void
1014 fmt_number_style_init (struct fmt_number_style *style)
1015 {
1016   fmt_affix_set (&style->neg_prefix, "");
1017   fmt_affix_set (&style->prefix, "");
1018   fmt_affix_set (&style->suffix, "");
1019   fmt_affix_set (&style->neg_suffix, "");
1020   style->decimal = '.';
1021   style->grouping = 0;
1022 }
1023
1024 static void
1025 fmt_number_style_clone (struct fmt_number_style *new,
1026                         const struct fmt_number_style *old)
1027 {
1028   fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1029   fmt_affix_set (&new->prefix, old->prefix.s);
1030   fmt_affix_set (&new->suffix, old->suffix.s);
1031   fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1032   new->decimal = old->decimal;
1033   new->grouping = old->grouping;
1034   new->extra_bytes = old->extra_bytes;
1035 }
1036
1037 /* Destroys a struct fmt_number_style. */
1038 static void
1039 fmt_number_style_destroy (struct fmt_number_style *style)
1040 {
1041   if (style != NULL)
1042     {
1043       fmt_affix_free (&style->neg_prefix);
1044       fmt_affix_free (&style->prefix);
1045       fmt_affix_free (&style->suffix);
1046       fmt_affix_free (&style->neg_suffix);
1047     }
1048 }
1049
1050 /* Returns the total width of the standard prefix and suffix for STYLE, in
1051    display columns (e.g. as returned by u8_strwidth()). */
1052 int
1053 fmt_affix_width (const struct fmt_number_style *style)
1054 {
1055   return style->prefix.width + style->suffix.width;
1056 }
1057
1058 /* Returns the total width of the negative prefix and suffix for STYLE, in
1059    display columns (e.g. as returned by u8_strwidth()). */
1060 int
1061 fmt_neg_affix_width (const struct fmt_number_style *style)
1062 {
1063   return style->neg_prefix.width + style->neg_suffix.width;
1064 }
1065
1066 /* Returns the struct fmt_desc for the given format TYPE. */
1067 static const struct fmt_desc *
1068 get_fmt_desc (enum fmt_type type)
1069 {
1070   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1071     {
1072 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1073         {#NAME, IMIN, OMIN, IO, CATEGORY},
1074 #include "format.def"
1075     };
1076
1077   assert (is_fmt_type (type));
1078   return &formats[type];
1079 }
1080
1081 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};