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