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