format: Introduce a new type "enum fmt_use".
[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, enum fmt_use);
50
51 static int max_digits_for_bytes (int bytes);
52 static void fmt_clamp_width (struct fmt_spec *, enum fmt_use);
53 static void fmt_clamp_decimals (struct fmt_spec *, enum fmt_use);
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 for USE and returns nonzero if so.
317    Otherwise, emits an error message and returns zero. */
318 bool
319 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
320 {
321   const char *io_fmt;
322   char str[FMT_STRING_LEN_MAX + 1];
323   int min_w, max_w, max_d;
324
325   assert (is_fmt_type (spec->type));
326   fmt_to_string (spec, str);
327
328   io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
329   if (use == FMT_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, use);
344   max_w = fmt_max_width (spec->type, use);
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, use);
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, FMT_FOR_INPUT);
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, FMT_FOR_OUTPUT);
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 USE.  */
500 void
501 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
502 {
503   /* Clamp width to those allowed by format. */
504   fmt_clamp_width (fmt, use);
505
506   /* If FMT has more decimal places than allowed, attempt to increase FMT's
507      width until that number of decimal places can be achieved. */
508   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
509       && fmt_takes_decimals (fmt->type))
510     {
511       int max_w = fmt_max_width (fmt->type, use);
512       for (; fmt->w < max_w; fmt->w++)
513         if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
514           break;
515     }
516
517   /* Clamp decimals to those allowed by format and width. */
518   fmt_clamp_decimals (fmt, use);
519 }
520
521 /* Adjusts FMT's width and decimal places to be valid for an
522    input format.  */
523 void
524 fmt_fix_input (struct fmt_spec *fmt)
525 {
526   fmt_fix (fmt, FMT_FOR_INPUT);
527 }
528
529 /* Adjusts FMT's width and decimal places to be valid for an
530    output format.  */
531 void
532 fmt_fix_output (struct fmt_spec *fmt)
533 {
534   fmt_fix (fmt, FMT_FOR_OUTPUT);
535 }
536
537 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
538    reduces its decimal places as necessary (if necessary) for that width.  */
539 void
540 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
541 {
542   fmt->w = width;
543   fmt_clamp_width (fmt, use);
544   fmt_clamp_decimals (fmt, use);
545 }
546
547 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
548    places allowed by FMT's type) and increases its width as necessary (if
549    necessary) for that number of decimal places.  */
550 void
551 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
552 {
553   fmt->d = decimals;
554   fmt_fix (fmt, use);
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 for the given USE. */
601 int
602 fmt_min_width (enum fmt_type type, enum fmt_use use)
603 {
604   return (use == FMT_FOR_INPUT
605           ? fmt_min_input_width (type)
606           : 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, enum fmt_use use 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, for the given USE. */
644 int
645 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
646 {
647   int max_d;
648
649   switch (type)
650     {
651     case FMT_F:
652     case FMT_COMMA:
653     case FMT_DOT:
654       max_d = use == FMT_FOR_INPUT ? width : width - 1;
655       break;
656
657     case FMT_DOLLAR:
658     case FMT_PCT:
659       max_d = use == FMT_FOR_INPUT ? width : width - 2;
660       break;
661
662     case FMT_E:
663       max_d = use == FMT_FOR_INPUT ? width : width - 7;
664       break;
665
666     case FMT_CCA:
667     case FMT_CCB:
668     case FMT_CCC:
669     case FMT_CCD:
670     case FMT_CCE:
671       assert (use == FMT_FOR_OUTPUT);
672       max_d = width - 1;
673       break;
674
675     case FMT_N:
676     case FMT_Z:
677       max_d = width;
678       break;
679
680     case FMT_P:
681       max_d = width * 2 - 1;
682       break;
683
684     case FMT_PK:
685       max_d = width * 2;
686       break;
687
688     case FMT_IB:
689     case FMT_PIB:
690       max_d = max_digits_for_bytes (width);
691       break;
692
693     case FMT_PIBHEX:
694       max_d = 0;
695       break;
696
697     case FMT_RB:
698     case FMT_RBHEX:
699       max_d = 16;
700       break;
701
702     case FMT_DATE:
703     case FMT_ADATE:
704     case FMT_EDATE:
705     case FMT_JDATE:
706     case FMT_SDATE:
707     case FMT_QYR:
708     case FMT_MOYR:
709     case FMT_WKYR:
710       max_d = 0;
711       break;
712
713     case FMT_DATETIME:
714       max_d = width - 21;
715       break;
716
717     case FMT_TIME:
718       max_d = width - 9;
719       break;
720
721     case FMT_DTIME:
722       max_d = width - 12;
723       break;
724
725     case FMT_WKDAY:
726     case FMT_MONTH:
727     case FMT_A:
728     case FMT_AHEX:
729       max_d = 0;
730       break;
731
732     default:
733       NOT_REACHED ();
734     }
735
736   if (max_d < 0)
737     max_d = 0;
738   else if (max_d > 16)
739     max_d = 16;
740   return max_d;
741 }
742
743 /* Returns the minimum acceptable width for an input field
744    formatted with the given TYPE. */
745 int
746 fmt_min_input_width (enum fmt_type type)
747 {
748   return get_fmt_desc (type)->min_input_width;
749 }
750
751 /* Returns the maximum acceptable width for an input field
752    formatted with the given TYPE. */
753 int
754 fmt_max_input_width (enum fmt_type type)
755 {
756   return fmt_max_width (type, FMT_FOR_INPUT);
757 }
758
759 /* Returns the maximum number of decimal places allowed in an
760    input field of the given TYPE and WIDTH. */
761 int
762 fmt_max_input_decimals (enum fmt_type type, int width)
763 {
764   assert (valid_width (type, width, true));
765   return fmt_max_decimals (type, width, FMT_FOR_INPUT);
766 }
767
768 /* Returns the minimum acceptable width for an output field
769    formatted with the given TYPE. */
770 int
771 fmt_min_output_width (enum fmt_type type)
772 {
773   return get_fmt_desc (type)->min_output_width;
774 }
775
776 /* Returns the maximum acceptable width for an output field
777    formatted with the given TYPE. */
778 int
779 fmt_max_output_width (enum fmt_type type)
780 {
781   return fmt_max_width (type, FMT_FOR_OUTPUT);
782 }
783
784 /* Returns the maximum number of decimal places allowed in an
785    output field of the given TYPE and WIDTH. */
786 int
787 fmt_max_output_decimals (enum fmt_type type, int width)
788 {
789   assert (valid_width (type, width, false));
790   return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
791 }
792
793 /* Returns the width step for a field formatted with the given
794    TYPE.  Field width must be a multiple of the width step. */
795 int
796 fmt_step_width (enum fmt_type type)
797 {
798   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
799           ? 2 : 1);
800 }
801
802 /* Returns true if TYPE is used for string fields,
803    false if it is used for numeric fields. */
804 bool
805 fmt_is_string (enum fmt_type type)
806 {
807   return fmt_get_category (type) == FMT_CAT_STRING;
808 }
809
810 /* Returns true if TYPE is used for numeric fields,
811    false if it is used for string fields. */
812 bool
813 fmt_is_numeric (enum fmt_type type)
814 {
815   return !fmt_is_string (type);
816 }
817
818 /* Returns the format TYPE's category.
819    Each format type is in exactly one category,
820    and each category's value is bitwise disjoint from every other
821    category.  Thus, the return value may be tested for equality
822    or compared bitwise against a mask of FMT_CAT_* values. */
823 enum fmt_category
824 fmt_get_category (enum fmt_type type)
825 {
826   return get_fmt_desc (type)->category;
827 }
828
829 /* Returns the output format selected by default when TYPE is
830    used as an input format. */
831 enum fmt_type
832 fmt_input_to_output (enum fmt_type type)
833 {
834   switch (fmt_get_category (type))
835     {
836     case FMT_CAT_STRING:
837       return FMT_A;
838
839     case FMT_CAT_LEGACY:
840     case FMT_CAT_BINARY:
841     case FMT_CAT_HEXADECIMAL:
842       return FMT_F;
843
844     default:
845       return type;
846     }
847 }
848
849 /* Returns the SPSS format type corresponding to the given PSPP
850    format type. */
851 int
852 fmt_to_io (enum fmt_type type)
853 {
854   return get_fmt_desc (type)->io;
855 }
856
857 /* Determines the PSPP format corresponding to the given SPSS
858    format type.  If successful, sets *FMT_TYPE to the PSPP format
859    and returns true.  On failure, return false. */
860 bool
861 fmt_from_io (int io, enum fmt_type *fmt_type)
862 {
863   switch (io)
864     {
865 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY)     \
866     case IO:                                          \
867       *fmt_type = FMT_##NAME;                           \
868       return true;
869 #include "format.def"
870     default:
871       return false;
872     }
873 }
874
875 /* Returns true if TYPE may be used as an input format,
876    false otherwise. */
877 bool
878 fmt_usable_for_input (enum fmt_type type)
879 {
880   assert (is_fmt_type (type));
881   return fmt_get_category (type) != FMT_CAT_CUSTOM;
882 }
883
884 /* For time and date formats, returns a template used for input
885    and output. */
886 const char *
887 fmt_date_template (enum fmt_type type)
888 {
889   switch (type)
890     {
891     case FMT_DATE:
892       return "dd-mmm-yy";
893     case FMT_ADATE:
894       return "mm/dd/yy";
895     case FMT_EDATE:
896       return "dd.mm.yy";
897     case FMT_JDATE:
898       return "yyddd";
899     case FMT_SDATE:
900       return "yy/mm/dd";
901     case FMT_QYR:
902       return "q Q yy";
903     case FMT_MOYR:
904       return "mmmXyy";
905     case FMT_WKYR:
906       return "ww WK yy";
907     case FMT_DATETIME:
908       return "dd-mmm-yyyy HH:MM";
909     case FMT_TIME:
910       return "H:MM";
911     case FMT_DTIME:
912       return "D HH:MM";
913     default:
914       NOT_REACHED ();
915     }
916 }
917
918 /* Returns a string representing the format TYPE for use in a GUI dialog. */
919 const char *
920 fmt_gui_name (enum fmt_type type)
921 {
922   switch (type)
923     {
924     case FMT_F:
925       return _("Numeric");
926
927     case FMT_COMMA:
928       return _("Comma");
929
930     case FMT_DOT:
931       return _("Dot");
932
933     case FMT_E:
934       return _("Scientific");
935
936     case FMT_DATE:
937     case FMT_EDATE:
938     case FMT_SDATE:
939     case FMT_ADATE:
940     case FMT_JDATE:
941     case FMT_QYR:
942     case FMT_MOYR:
943     case FMT_WKYR:
944     case FMT_DATETIME:
945     case FMT_TIME:
946     case FMT_DTIME:
947     case FMT_WKDAY:
948     case FMT_MONTH:
949       return _("Date");
950
951     case FMT_DOLLAR:
952       return _("Dollar");
953
954     case FMT_CCA:
955     case FMT_CCB:
956     case FMT_CCC:
957     case FMT_CCD:
958     case FMT_CCE:
959       return _("Custom");
960
961     case FMT_A:
962       return _("String");
963
964     default:
965       return fmt_name (type);
966     }
967 }
968 \f
969 /* Returns true if TYPE is a valid format type,
970    false otherwise. */
971 bool
972 is_fmt_type (enum fmt_type type)
973 {
974   return type < FMT_NUMBER_OF_FORMATS;
975 }
976
977 /* Returns true if WIDTH is a valid width for the given format
978    TYPE, for the given USE. */
979 static bool
980 valid_width (enum fmt_type type, int width, enum fmt_use use)
981 {
982   return (width >= fmt_min_width (type, use)
983           && width <= fmt_max_width (type, use));
984 }
985
986 /* Returns the maximum number of decimal digits in an unsigned
987    binary number that is BYTES bytes long. */
988 static int
989 max_digits_for_bytes (int bytes)
990 {
991   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
992   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
993   return map[bytes - 1];
994 }
995
996 /* Clamp FMT's width to the range and values allowed by FMT's type. */
997 static void
998 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
999 {
1000   unsigned int step;
1001   int min_w, max_w;
1002
1003   min_w = fmt_min_width (fmt->type, use);
1004   max_w = fmt_max_width (fmt->type, use);
1005   if (fmt->w < min_w)
1006     fmt->w = min_w;
1007   else if (fmt->w > max_w)
1008     fmt->w = max_w;
1009
1010   /* Round width to step. */
1011   step = fmt_step_width (fmt->type);
1012   fmt->w = ROUND_DOWN (fmt->w, step);
1013 }
1014
1015 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1016 static void
1017 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1018 {
1019   int max_d;
1020
1021   max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1022   if (fmt->d < 0)
1023     fmt->d = 0;
1024   else if (fmt->d > max_d)
1025     fmt->d = max_d;
1026 }
1027 \f
1028 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
1029 static void
1030 fmt_affix_set (struct fmt_affix *affix, const char *s)
1031 {
1032   affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1033   affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1034 }
1035
1036 /* Frees data in AFFIX. */
1037 static void
1038 fmt_affix_free (struct fmt_affix *affix)
1039 {
1040   if (affix->s[0])
1041     free (affix->s);
1042 }
1043
1044 static void
1045 fmt_number_style_init (struct fmt_number_style *style)
1046 {
1047   fmt_affix_set (&style->neg_prefix, "");
1048   fmt_affix_set (&style->prefix, "");
1049   fmt_affix_set (&style->suffix, "");
1050   fmt_affix_set (&style->neg_suffix, "");
1051   style->decimal = '.';
1052   style->grouping = 0;
1053 }
1054
1055 static void
1056 fmt_number_style_clone (struct fmt_number_style *new,
1057                         const struct fmt_number_style *old)
1058 {
1059   fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1060   fmt_affix_set (&new->prefix, old->prefix.s);
1061   fmt_affix_set (&new->suffix, old->suffix.s);
1062   fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1063   new->decimal = old->decimal;
1064   new->grouping = old->grouping;
1065   new->extra_bytes = old->extra_bytes;
1066 }
1067
1068 /* Destroys a struct fmt_number_style. */
1069 static void
1070 fmt_number_style_destroy (struct fmt_number_style *style)
1071 {
1072   if (style != NULL)
1073     {
1074       fmt_affix_free (&style->neg_prefix);
1075       fmt_affix_free (&style->prefix);
1076       fmt_affix_free (&style->suffix);
1077       fmt_affix_free (&style->neg_suffix);
1078     }
1079 }
1080
1081 /* Returns the total width of the standard prefix and suffix for STYLE, in
1082    display columns (e.g. as returned by u8_strwidth()). */
1083 int
1084 fmt_affix_width (const struct fmt_number_style *style)
1085 {
1086   return style->prefix.width + style->suffix.width;
1087 }
1088
1089 /* Returns the total width of the negative prefix and suffix for STYLE, in
1090    display columns (e.g. as returned by u8_strwidth()). */
1091 int
1092 fmt_neg_affix_width (const struct fmt_number_style *style)
1093 {
1094   return style->neg_prefix.width + style->neg_suffix.width;
1095 }
1096
1097 /* Returns the struct fmt_desc for the given format TYPE. */
1098 static const struct fmt_desc *
1099 get_fmt_desc (enum fmt_type type)
1100 {
1101   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1102     {
1103 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1104         {#NAME, IMIN, OMIN, IO, CATEGORY},
1105 #include "format.def"
1106     };
1107
1108   assert (is_fmt_type (type));
1109   return &formats[type];
1110 }
1111
1112 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};