expressions: Major work to improve error messages.
[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 NULL if so.  Otherwise,
338    returns a malloc()'d string that describes the error.  The caller must
339    eventually free() the string. */
340 char *
341 fmt_check__ (const struct fmt_spec *spec, enum fmt_use use)
342 {
343   char str[FMT_STRING_LEN_MAX + 1];
344   int min_w, max_w, max_d;
345
346   assert (is_fmt_type (spec->type));
347   fmt_to_string (spec, str);
348
349   if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
350     return xasprintf (_("Format %s may not be used for input."), str);
351
352   if (spec->w % fmt_step_width (spec->type))
353     {
354       assert (fmt_step_width (spec->type) == 2);
355       return (use == FMT_FOR_INPUT
356               ? xasprintf (_("Input format %s specifies width %d, "
357                              "but %s requires an even width."),
358                            str, spec->w, fmt_name (spec->type))
359               : xasprintf (_("Output format %s specifies width %d, "
360                              "but %s requires an even width."),
361                            str, spec->w, fmt_name (spec->type)));
362     }
363
364   min_w = fmt_min_width (spec->type, use);
365   max_w = fmt_max_width (spec->type, use);
366   if (spec->w < min_w || spec->w > max_w)
367     return (use == FMT_FOR_INPUT
368             ? xasprintf (_("Input format %s specifies width %d, but "
369                            "%s requires a width between %d and %d."),
370                          str, spec->w, fmt_name (spec->type), min_w, max_w)
371             : xasprintf (_("Output format %s specifies width %d, but "
372                            "%s requires a width between %d and %d."),
373                          str, spec->w, fmt_name (spec->type), min_w, max_w));
374
375   max_d = fmt_max_decimals (spec->type, spec->w, use);
376   if (!fmt_takes_decimals (spec->type) && spec->d != 0)
377     return (use == FMT_FOR_INPUT
378             ? xasprintf (ngettext (
379                            "Input format %s specifies %d decimal "
380                            "place, but %s does not allow any decimals.",
381                            "Input format %s specifies %d decimal "
382                            "places, but %s does not allow any "
383                            "decimals.",
384                            spec->d),
385                          str, spec->d, fmt_name (spec->type))
386             : xasprintf (ngettext (
387                            "Output format %s specifies %d decimal "
388                            "place, but %s does not allow any decimals.",
389                            "Output 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 if (spec->d > max_d)
394     {
395       if (max_d > 0)
396         return (use == FMT_FOR_INPUT
397                 ? xasprintf (ngettext (
398                                "Input format %s specifies %d decimal place, "
399                                "but the given width allows at most "
400                                "%d decimals.",
401                                "Input format %s specifies %d decimal places, "
402                                "but the given width allows at most "
403                                "%d decimals.",
404                                spec->d),
405                              str, spec->d, max_d)
406                 : xasprintf (ngettext (
407                                "Output format %s specifies %d decimal place, "
408                                "but the given width allows at most "
409                                "%d decimals.",
410                                "Output 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         return (use == FMT_FOR_INPUT
417                 ? xasprintf (ngettext (
418                                "Input format %s specifies %d decimal place, "
419                                "but the given width does not allow "
420                                "for any decimals.",
421                                "Input format %s specifies %d decimal places, "
422                                "but the given width does not allow "
423                                "for any decimals.",
424                                spec->d),
425                              str, spec->d)
426                 : xasprintf (ngettext (
427                                "Output format %s specifies %d decimal place, "
428                                "but the given width does not allow "
429                                "for any decimals.",
430                                "Output format %s specifies %d decimal places, "
431                                "but the given width does not allow "
432                                "for any decimals.",
433                                spec->d),
434                              str, spec->d));
435     }
436
437   return NULL;
438 }
439
440 static bool
441 fmt_emit_and_free_error (char *error)
442 {
443   if (error)
444     {
445       msg (SE, "%s", error);
446       free (error);
447       return false;
448     }
449   else
450     return true;
451 }
452
453 /* Checks whether SPEC is valid for USE and returns nonzero if so.  Otherwise,
454    emits an error message for the current source location and returns zero. */
455 bool
456 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
457 {
458   return fmt_emit_and_free_error (fmt_check__ (spec, use));
459 }
460
461 /* Checks whether SPEC is valid as an input format and returns
462    nonzero if so.  Otherwise, emits an error message and returns
463    zero. */
464 bool
465 fmt_check_input (const struct fmt_spec *spec)
466 {
467   return fmt_check (spec, FMT_FOR_INPUT);
468 }
469
470 /* Checks whether SPEC is valid as an output format and returns
471    true if so.  Otherwise, emits an error message and returns false. */
472 bool
473 fmt_check_output (const struct fmt_spec *spec)
474 {
475   return fmt_check (spec, FMT_FOR_OUTPUT);
476 }
477
478 /* Checks that FORMAT is appropriate for a variable of the given VAR_TYPE and
479    returns NULL if so.  Otherwise returns a malloc()'d error message that the
480    calelr must eventually free(). */
481 char *
482 fmt_check_type_compat__ (const struct fmt_spec *format, enum val_type var_type)
483 {
484   assert (val_type_is_valid (var_type));
485   if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
486     {
487       char str[FMT_STRING_LEN_MAX + 1];
488       return xasprintf (_("%s variables are not compatible with %s format %s."),
489                         var_type == VAL_STRING ? _("String") : _("Numeric"),
490                         var_type == VAL_STRING ? _("numeric") : _("string"),
491                         fmt_to_string (format, str));
492     }
493   return NULL;
494 }
495
496 /* Checks that FORMAT is appropriate for a variable of the given
497    VAR_TYPE and returns true if so.  Otherwise returns false and
498    emits an error message. */
499 bool
500 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
501 {
502   return fmt_emit_and_free_error (fmt_check_type_compat__ (format, var_type));
503 }
504
505 /* Checks that FORMAT is appropriate for a variable of the given WIDTH and
506    returns NULL if so.  Otherwise returns a malloc()'d error message that the
507    calelr must eventually free(). */
508 char *
509 fmt_check_width_compat__ (const struct fmt_spec *format, int width)
510 {
511   char *error = fmt_check_type_compat__ (format, val_type_from_width (width));
512   if (error)
513     return error;
514
515   if (fmt_var_width (format) != width)
516     {
517       char str[FMT_STRING_LEN_MAX + 1];
518       return xasprintf (_("String variable with width %d is not compatible "
519                           "with format %s."),
520                         width, fmt_to_string (format, str));
521     }
522
523   return NULL;
524 }
525
526 /* Checks that FORMAT is appropriate for a variable of the given
527    WIDTH and returns true if so.  Otherwise returns false and
528    emits an error message. */
529 bool
530 fmt_check_width_compat (const struct fmt_spec *format, int width)
531 {
532   return fmt_emit_and_free_error (fmt_check_width_compat__ (format, width));
533 }
534
535 /* Returns the width corresponding to FORMAT.  The return value
536    is the width of the `union value's required by FORMAT. */
537 int
538 fmt_var_width (const struct fmt_spec *format)
539 {
540   return (format->type == FMT_AHEX ? format->w / 2
541           : format->type == FMT_A ? format->w
542           : 0);
543 }
544
545 /* Converts F to its string representation (for instance, "F8.2")
546    in BUFFER.  Returns BUFFER.
547
548    If F has decimals, they are included in the output string,
549    even if F's format type does not allow decimals, to allow
550    accurately presenting incorrect formats to the user. */
551 char *
552 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
553 {
554   if (fmt_takes_decimals (f->type) || f->d > 0)
555     snprintf (buffer, FMT_STRING_LEN_MAX + 1,
556               "%s%d.%d", fmt_name (f->type), f->w, f->d);
557   else
558     snprintf (buffer, FMT_STRING_LEN_MAX + 1,
559               "%s%d", fmt_name (f->type), f->w);
560   return buffer;
561 }
562
563 /* Returns true if A and B are identical formats,
564    false otherwise. */
565 bool
566 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
567 {
568   return a->type == b->type && a->w == b->w && a->d == b->d;
569 }
570
571 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
572    If nothing needed to be changed the return value is false
573  */
574 bool
575 fmt_resize (struct fmt_spec *fmt, int width)
576 {
577   if ((width > 0) != fmt_is_string (fmt->type))
578     {
579       /* Changed from numeric to string or vice versa.  Set to
580          default format for new width. */
581       *fmt = fmt_default_for_width (width);
582     }
583   else if (width > 0)
584     {
585       /* Changed width of string.  Preserve format type, adjust
586          width. */
587       fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
588     }
589   else
590     {
591       /* Still numeric. */
592       return false;
593     }
594   return true;
595 }
596
597 /* Adjusts FMT's width and decimal places to be valid for USE.  */
598 void
599 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
600 {
601   /* Clamp width to those allowed by format. */
602   fmt_clamp_width (fmt, use);
603
604   /* If FMT has more decimal places than allowed, attempt to increase FMT's
605      width until that number of decimal places can be achieved. */
606   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
607       && fmt_takes_decimals (fmt->type))
608     {
609       int max_w = fmt_max_width (fmt->type, use);
610       for (; fmt->w < max_w; fmt->w++)
611         if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
612           break;
613     }
614
615   /* Clamp decimals to those allowed by format and width. */
616   fmt_clamp_decimals (fmt, use);
617 }
618
619 /* Adjusts FMT's width and decimal places to be valid for an
620    input format.  */
621 void
622 fmt_fix_input (struct fmt_spec *fmt)
623 {
624   fmt_fix (fmt, FMT_FOR_INPUT);
625 }
626
627 /* Adjusts FMT's width and decimal places to be valid for an
628    output format.  */
629 void
630 fmt_fix_output (struct fmt_spec *fmt)
631 {
632   fmt_fix (fmt, FMT_FOR_OUTPUT);
633 }
634
635 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
636    reduces its decimal places as necessary (if necessary) for that width.  */
637 void
638 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
639 {
640   fmt->w = width;
641   fmt_clamp_width (fmt, use);
642   fmt_clamp_decimals (fmt, use);
643 }
644
645 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
646    places allowed by FMT's type) and increases its width as necessary (if
647    necessary) for that number of decimal places.  */
648 void
649 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
650 {
651   fmt->d = decimals;
652   fmt_fix (fmt, use);
653 }
654 \f
655 /* Describes a display format. */
656 struct fmt_desc
657   {
658     char name[9];
659     int min_input_width, min_output_width;
660     int io;
661     enum fmt_category category;
662   };
663
664 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
665
666 /* Returns the name of the given format TYPE. */
667 const char *
668 fmt_name (enum fmt_type type)
669 {
670   return get_fmt_desc (type)->name;
671 }
672
673 /* Tries to parse NAME as a format type.
674    If successful, stores the type in *TYPE and returns true.
675    On failure, returns false. */
676 bool
677 fmt_from_name (const char *name, enum fmt_type *type)
678 {
679   int i;
680
681   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
682     if (!c_strcasecmp (name, get_fmt_desc (i)->name))
683       {
684         *type = i;
685         return true;
686       }
687   return false;
688 }
689
690 /* Returns true if TYPE accepts decimal places,
691    false otherwise. */
692 bool
693 fmt_takes_decimals (enum fmt_type type)
694 {
695   return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
696 }
697
698 /* Returns the minimum width of the given format TYPE for the given USE. */
699 int
700 fmt_min_width (enum fmt_type type, enum fmt_use use)
701 {
702   return (use == FMT_FOR_INPUT
703           ? fmt_min_input_width (type)
704           : fmt_min_output_width (type));
705 }
706
707 /* Returns the maximum width of the given format TYPE,
708    for input if FOR_INPUT is true,
709    for output otherwise. */
710 int
711 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
712 {
713   /* Maximum width is actually invariant of whether the format is
714      for input or output, so FOR_INPUT is unused. */
715   assert (is_fmt_type (type));
716   switch (type)
717     {
718     case FMT_P:
719     case FMT_PK:
720     case FMT_PIBHEX:
721     case FMT_RBHEX:
722       return 16;
723
724     case FMT_IB:
725     case FMT_PIB:
726     case FMT_RB:
727       return 8;
728
729     case FMT_A:
730       return MAX_STRING;
731
732     case FMT_AHEX:
733       return 2 * MAX_STRING;
734
735     default:
736       return 40;
737     }
738 }
739
740 /* Returns the maximum number of decimal places allowed for the
741    given format TYPE with a width of WIDTH places, for the given USE. */
742 int
743 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
744 {
745   int max_d;
746
747   switch (type)
748     {
749     case FMT_F:
750     case FMT_COMMA:
751     case FMT_DOT:
752       max_d = use == FMT_FOR_INPUT ? width : width - 1;
753       break;
754
755     case FMT_DOLLAR:
756     case FMT_PCT:
757       max_d = use == FMT_FOR_INPUT ? width : width - 2;
758       break;
759
760     case FMT_E:
761       max_d = use == FMT_FOR_INPUT ? width : width - 7;
762       break;
763
764     case FMT_CCA:
765     case FMT_CCB:
766     case FMT_CCC:
767     case FMT_CCD:
768     case FMT_CCE:
769       assert (use == FMT_FOR_OUTPUT);
770       max_d = width - 1;
771       break;
772
773     case FMT_N:
774     case FMT_Z:
775       max_d = width;
776       break;
777
778     case FMT_P:
779       max_d = width * 2 - 1;
780       break;
781
782     case FMT_PK:
783       max_d = width * 2;
784       break;
785
786     case FMT_IB:
787     case FMT_PIB:
788       max_d = max_digits_for_bytes (width);
789       break;
790
791     case FMT_PIBHEX:
792       max_d = 0;
793       break;
794
795     case FMT_RB:
796     case FMT_RBHEX:
797       max_d = 16;
798       break;
799
800     case FMT_DATE:
801     case FMT_ADATE:
802     case FMT_EDATE:
803     case FMT_JDATE:
804     case FMT_SDATE:
805     case FMT_QYR:
806     case FMT_MOYR:
807     case FMT_WKYR:
808       max_d = 0;
809       break;
810
811     case FMT_DATETIME:
812       max_d = width - 21;
813       break;
814
815     case FMT_YMDHMS:
816       max_d = width - 20;
817       break;
818
819     case FMT_MTIME:
820       max_d = width - 6;
821       break;
822
823     case FMT_TIME:
824       max_d = width - 9;
825       break;
826
827     case FMT_DTIME:
828       max_d = width - 12;
829       break;
830
831     case FMT_WKDAY:
832     case FMT_MONTH:
833     case FMT_A:
834     case FMT_AHEX:
835       max_d = 0;
836       break;
837
838     default:
839       NOT_REACHED ();
840     }
841
842   if (max_d < 0)
843     max_d = 0;
844   else if (max_d > 16)
845     max_d = 16;
846   return max_d;
847 }
848
849 /* Returns the minimum acceptable width for an input field
850    formatted with the given TYPE. */
851 int
852 fmt_min_input_width (enum fmt_type type)
853 {
854   return get_fmt_desc (type)->min_input_width;
855 }
856
857 /* Returns the maximum acceptable width for an input field
858    formatted with the given TYPE. */
859 int
860 fmt_max_input_width (enum fmt_type type)
861 {
862   return fmt_max_width (type, FMT_FOR_INPUT);
863 }
864
865 /* Returns the maximum number of decimal places allowed in an
866    input field of the given TYPE and WIDTH. */
867 int
868 fmt_max_input_decimals (enum fmt_type type, int width)
869 {
870   assert (valid_width (type, width, true));
871   return fmt_max_decimals (type, width, FMT_FOR_INPUT);
872 }
873
874 /* Returns the minimum acceptable width for an output field
875    formatted with the given TYPE. */
876 int
877 fmt_min_output_width (enum fmt_type type)
878 {
879   return get_fmt_desc (type)->min_output_width;
880 }
881
882 /* Returns the maximum acceptable width for an output field
883    formatted with the given TYPE. */
884 int
885 fmt_max_output_width (enum fmt_type type)
886 {
887   return fmt_max_width (type, FMT_FOR_OUTPUT);
888 }
889
890 /* Returns the maximum number of decimal places allowed in an
891    output field of the given TYPE and WIDTH. */
892 int
893 fmt_max_output_decimals (enum fmt_type type, int width)
894 {
895   assert (valid_width (type, width, false));
896   return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
897 }
898
899 /* Returns the width step for a field formatted with the given
900    TYPE.  Field width must be a multiple of the width step. */
901 int
902 fmt_step_width (enum fmt_type type)
903 {
904   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
905           ? 2 : 1);
906 }
907
908 /* Returns true if TYPE is used for string fields,
909    false if it is used for numeric fields. */
910 bool
911 fmt_is_string (enum fmt_type type)
912 {
913   return fmt_get_category (type) == FMT_CAT_STRING;
914 }
915
916 /* Returns true if TYPE is used for numeric fields,
917    false if it is used for string fields. */
918 bool
919 fmt_is_numeric (enum fmt_type type)
920 {
921   return !fmt_is_string (type);
922 }
923
924 /* Returns the format TYPE's category.
925    Each format type is in exactly one category,
926    and each category's value is bitwise disjoint from every other
927    category.  Thus, the return value may be tested for equality
928    or compared bitwise against a mask of FMT_CAT_* values. */
929 enum fmt_category
930 fmt_get_category (enum fmt_type type)
931 {
932   return get_fmt_desc (type)->category;
933 }
934
935 /* Returns the output format selected by default when TYPE is
936    used as an input format. */
937 enum fmt_type
938 fmt_input_to_output (enum fmt_type type)
939 {
940   switch (fmt_get_category (type))
941     {
942     case FMT_CAT_STRING:
943       return FMT_A;
944
945     case FMT_CAT_LEGACY:
946     case FMT_CAT_BINARY:
947     case FMT_CAT_HEXADECIMAL:
948       return FMT_F;
949
950     default:
951       return type;
952     }
953 }
954
955 /* Returns the SPSS format type corresponding to the given PSPP
956    format type. */
957 int
958 fmt_to_io (enum fmt_type type)
959 {
960   return get_fmt_desc (type)->io;
961 }
962
963 /* Determines the PSPP format corresponding to the given SPSS
964    format type.  If successful, sets *FMT_TYPE to the PSPP format
965    and returns true.  On failure, return false. */
966 bool
967 fmt_from_io (int io, enum fmt_type *fmt_type)
968 {
969   switch (io)
970     {
971 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY)     \
972     case IO:                                          \
973       *fmt_type = FMT_##NAME;                           \
974       return true;
975 #include "format.def"
976     default:
977       return false;
978     }
979 }
980
981 /* Translate U32, which is in the form found in SAV and SPV files, into a
982    format specification, and stores the new specification in *F.
983
984    If LOOSE is false, checks that the format specification is appropriate as an
985    output format for a variable with the given WIDTH and reports an error if
986    not.  If LOOSE is true, instead adjusts the format's width and decimals as
987    necessary to be suitable.
988
989    Return true if successful, false if there was an error.. */
990 bool
991 fmt_from_u32 (uint32_t u32, int width, bool loose, struct fmt_spec *f)
992 {
993   uint8_t raw_type = u32 >> 16;
994   uint8_t w = u32 >> 8;
995   uint8_t d = u32;
996
997   msg_disable ();
998   f->w = w;
999   f->d = d;
1000   bool ok = fmt_from_io (raw_type, &f->type);
1001   if (ok)
1002     {
1003       if (loose)
1004         fmt_fix_output (f);
1005       else
1006         ok = fmt_check_output (f);
1007     }
1008   if (ok)
1009     ok = fmt_check_width_compat (f, width);
1010   msg_enable ();
1011
1012   return ok;
1013 }
1014
1015 /* Returns true if TYPE may be used as an input format,
1016    false otherwise. */
1017 bool
1018 fmt_usable_for_input (enum fmt_type type)
1019 {
1020   assert (is_fmt_type (type));
1021   return fmt_get_category (type) != FMT_CAT_CUSTOM;
1022 }
1023
1024 /* For time and date formats, returns a template used for input and output in a
1025    field of the given WIDTH.
1026
1027    WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
1028    is, whether the returned string contains "yy" or "yyyy", and whether seconds
1029    are include, that is, whether the returned string contains ":SS".  A caller
1030    that doesn't care whether the returned string contains "yy" or "yyyy" or
1031    ":SS" can just specify 0 to omit them. */
1032 const char *
1033 fmt_date_template (enum fmt_type type, int width)
1034 {
1035   const char *s1, *s2;
1036
1037   switch (type)
1038     {
1039     case FMT_DATE:
1040       s1 = "dd-mmm-yy";
1041       s2 = "dd-mmm-yyyy";
1042       break;
1043
1044     case FMT_ADATE:
1045       s1 = "mm/dd/yy";
1046       s2 = "mm/dd/yyyy";
1047       break;
1048
1049     case FMT_EDATE:
1050       s1 = "dd.mm.yy";
1051       s2 = "dd.mm.yyyy";
1052       break;
1053
1054     case FMT_JDATE:
1055       s1 = "yyddd";
1056       s2 = "yyyyddd";
1057       break;
1058
1059     case FMT_SDATE:
1060       s1 = "yy/mm/dd";
1061       s2 = "yyyy/mm/dd";
1062       break;
1063
1064     case FMT_QYR:
1065       s1 = "q Q yy";
1066       s2 = "q Q yyyy";
1067       break;
1068
1069     case FMT_MOYR:
1070       s1 = "mmm yy";
1071       s2 = "mmm yyyy";
1072       break;
1073
1074     case FMT_WKYR:
1075       s1 = "ww WK yy";
1076       s2 = "ww WK yyyy";
1077       break;
1078
1079     case FMT_DATETIME:
1080       s1 = "dd-mmm-yyyy HH:MM";
1081       s2 = "dd-mmm-yyyy HH:MM:SS";
1082       break;
1083
1084     case FMT_YMDHMS:
1085       s1 = "yyyy-mm-dd HH:MM";
1086       s2 = "yyyy-mm-dd HH:MM:SS";
1087       break;
1088
1089     case FMT_MTIME:
1090       s1 = "MM";
1091       s2 = "MM:SS";
1092       break;
1093
1094     case FMT_TIME:
1095       s1 = "HH:MM";
1096       s2 = "HH:MM:SS";
1097       break;
1098
1099     case FMT_DTIME:
1100       s1 = "D HH:MM";
1101       s2 = "D HH:MM:SS";
1102       break;
1103
1104     default:
1105       NOT_REACHED ();
1106     }
1107
1108   return width >= strlen (s2) ? s2 : s1;
1109 }
1110
1111 /* Returns a string representing the format TYPE for use in a GUI dialog. */
1112 const char *
1113 fmt_gui_name (enum fmt_type type)
1114 {
1115   switch (type)
1116     {
1117     case FMT_F:
1118       return _("Numeric");
1119
1120     case FMT_COMMA:
1121       return _("Comma");
1122
1123     case FMT_DOT:
1124       return _("Dot");
1125
1126     case FMT_E:
1127       return _("Scientific");
1128
1129     case FMT_DATE:
1130     case FMT_EDATE:
1131     case FMT_SDATE:
1132     case FMT_ADATE:
1133     case FMT_JDATE:
1134     case FMT_QYR:
1135     case FMT_MOYR:
1136     case FMT_WKYR:
1137     case FMT_DATETIME:
1138     case FMT_YMDHMS:
1139     case FMT_MTIME:
1140     case FMT_TIME:
1141     case FMT_DTIME:
1142     case FMT_WKDAY:
1143     case FMT_MONTH:
1144       return _("Date");
1145
1146     case FMT_DOLLAR:
1147       return _("Dollar");
1148
1149     case FMT_CCA:
1150     case FMT_CCB:
1151     case FMT_CCC:
1152     case FMT_CCD:
1153     case FMT_CCE:
1154       return _("Custom");
1155
1156     case FMT_A:
1157       return _("String");
1158
1159     default:
1160       return fmt_name (type);
1161     }
1162 }
1163 \f
1164 /* Returns true if TYPE is a valid format type,
1165    false otherwise. */
1166 bool
1167 is_fmt_type (enum fmt_type type)
1168 {
1169   return type < FMT_NUMBER_OF_FORMATS;
1170 }
1171
1172 /* Returns true if WIDTH is a valid width for the given format
1173    TYPE, for the given USE. */
1174 static bool
1175 valid_width (enum fmt_type type, int width, enum fmt_use use)
1176 {
1177   return (width >= fmt_min_width (type, use)
1178           && width <= fmt_max_width (type, use));
1179 }
1180
1181 /* Returns the maximum number of decimal digits in an unsigned
1182    binary number that is BYTES bytes long. */
1183 static int
1184 max_digits_for_bytes (int bytes)
1185 {
1186   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1187   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1188   return map[bytes - 1];
1189 }
1190
1191 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1192 static void
1193 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1194 {
1195   unsigned int step;
1196   int min_w, max_w;
1197
1198   min_w = fmt_min_width (fmt->type, use);
1199   max_w = fmt_max_width (fmt->type, use);
1200   if (fmt->w < min_w)
1201     fmt->w = min_w;
1202   else if (fmt->w > max_w)
1203     fmt->w = max_w;
1204
1205   /* Round width to step. */
1206   step = fmt_step_width (fmt->type);
1207   fmt->w = ROUND_DOWN (fmt->w, step);
1208 }
1209
1210 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1211 static void
1212 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1213 {
1214   int max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1215   if (fmt->d > max_d)
1216     fmt->d = max_d;
1217 }
1218 \f
1219 static struct fmt_affix
1220 fmt_affix_clone (const struct fmt_affix *old)
1221 {
1222   return (struct fmt_affix) {
1223     .s = xstrdup_if_nonnull (old->s),
1224     .width = old->width,
1225   };
1226 }
1227
1228 /* Frees data in AFFIX. */
1229 static void
1230 fmt_affix_free (struct fmt_affix *affix)
1231 {
1232   if (affix->s)
1233     free (affix->s);
1234 }
1235
1236 /* Find and returns the grouping character in CC_STRING (either '.' or ',') or
1237    0 on error. */
1238 static int
1239 find_cc_separators (const char *cc_string)
1240 {
1241   /* Count commas and periods.  There must be exactly three of
1242      one or the other, except that an apostrophe escapes a
1243      following comma or period. */
1244   int n_commas = 0;
1245   int n_dots = 0;
1246   for (const char *p = cc_string; *p; p++)
1247     if (*p == ',')
1248       n_commas++;
1249     else if (*p == '.')
1250       n_dots++;
1251     else if (*p == '\'' && (p[1] == '.' || p[1] == ',' || p[1] == '\''))
1252       p++;
1253
1254   return (n_commas == 3 ? (n_dots != 3 ? ',' : 0)
1255           : n_dots == 3 ? '.'
1256           : 0);
1257 }
1258
1259 /* Extracts a token from IN into a newly allocated string AFFIXP.  Tokens are
1260    delimited by GROUPING.  Returns the first character following the token. */
1261 static struct fmt_affix
1262 extract_cc_token (const char **sp, int grouping, size_t *extra_bytes)
1263 {
1264   const char *p = *sp;
1265   for (; *p && *p != grouping; p++)
1266     if (*p == '\'' && p[1] == grouping)
1267       p++;
1268
1269   size_t length = p - *sp;
1270   char *affix = xmemdup0 (*sp, length);
1271   size_t width = u8_strwidth (CHAR_CAST (const uint8_t *, affix), "UTF-8");
1272   if (length > width)
1273     *extra_bytes += length - width;
1274
1275   *sp = p + (*p != 0);
1276
1277   return (struct fmt_affix) { .s = affix, .width = width };
1278 }
1279
1280 struct fmt_number_style *
1281 fmt_number_style_from_string (const char *s)
1282 {
1283   char grouping = find_cc_separators (s);
1284   if (!grouping)
1285     return NULL;
1286
1287   size_t extra_bytes = 0;
1288   struct fmt_affix neg_prefix = extract_cc_token (&s, grouping, &extra_bytes);
1289   struct fmt_affix prefix = extract_cc_token (&s, grouping, &extra_bytes);
1290   struct fmt_affix suffix = extract_cc_token (&s, grouping, &extra_bytes);
1291   struct fmt_affix neg_suffix = extract_cc_token (&s, grouping, &extra_bytes);
1292
1293   struct fmt_number_style *style = xmalloc (sizeof *style);
1294   *style = (struct fmt_number_style) {
1295     .neg_prefix = neg_prefix,
1296     .prefix = prefix,
1297     .suffix = suffix,
1298     .neg_suffix = neg_suffix,
1299     .decimal = grouping == '.' ? ',' : '.',
1300     .grouping = grouping,
1301     .extra_bytes = extra_bytes,
1302   };
1303   return style;
1304 }
1305
1306 static void
1307 format_cc (struct string *out, const char *in, char grouping)
1308 {
1309   while (*in != '\0')
1310     {
1311       char c = *in++;
1312       if (c == grouping || c == '\'')
1313         ds_put_byte (out, '\'');
1314       else if (c == '"')
1315         ds_put_byte (out, '"');
1316       ds_put_byte (out, c);
1317     }
1318 }
1319
1320 char *
1321 fmt_number_style_to_string (const struct fmt_number_style *cc)
1322 {
1323   struct string out = DS_EMPTY_INITIALIZER;
1324   format_cc (&out, cc->neg_prefix.s, cc->grouping);
1325   ds_put_byte (&out, cc->grouping);
1326   format_cc (&out, cc->prefix.s, cc->grouping);
1327   ds_put_byte (&out, cc->grouping);
1328   format_cc (&out, cc->suffix.s, cc->grouping);
1329   ds_put_byte (&out, cc->grouping);
1330   format_cc (&out, cc->neg_suffix.s, cc->grouping);
1331   return ds_steal_cstr (&out);
1332 }
1333
1334 struct fmt_number_style *
1335 fmt_number_style_clone (const struct fmt_number_style *old)
1336 {
1337   if (old)
1338     {
1339       struct fmt_number_style *new = xmalloc (sizeof *new);
1340       *new = (struct fmt_number_style) {
1341         .neg_prefix = fmt_affix_clone (&old->neg_prefix),
1342         .prefix = fmt_affix_clone (&old->prefix),
1343         .suffix = fmt_affix_clone (&old->suffix),
1344         .neg_suffix = fmt_affix_clone (&old->neg_suffix),
1345         .decimal = old->decimal,
1346         .grouping = old->grouping,
1347         .extra_bytes = old->extra_bytes,
1348       };
1349       return new;
1350     }
1351   else
1352     return NULL;
1353 }
1354
1355 /* Destroys a struct fmt_number_style. */
1356 void
1357 fmt_number_style_destroy (struct fmt_number_style *style)
1358 {
1359   if (style != NULL)
1360     {
1361       fmt_affix_free (&style->neg_prefix);
1362       fmt_affix_free (&style->prefix);
1363       fmt_affix_free (&style->suffix);
1364       fmt_affix_free (&style->neg_suffix);
1365       free (style);
1366     }
1367 }
1368
1369 /* Returns the total width of the standard prefix and suffix for STYLE, in
1370    display columns (e.g. as returned by u8_strwidth()). */
1371 int
1372 fmt_affix_width (const struct fmt_number_style *style)
1373 {
1374   return style->prefix.width + style->suffix.width;
1375 }
1376
1377 /* Returns the total width of the negative prefix and suffix for STYLE, in
1378    display columns (e.g. as returned by u8_strwidth()). */
1379 int
1380 fmt_neg_affix_width (const struct fmt_number_style *style)
1381 {
1382   return style->neg_prefix.width + style->neg_suffix.width;
1383 }
1384
1385 /* Returns the struct fmt_desc for the given format TYPE. */
1386 static const struct fmt_desc *
1387 get_fmt_desc (enum fmt_type type)
1388 {
1389   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1390     {
1391 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1392         {#NAME, IMIN, OMIN, IO, CATEGORY},
1393 #include "format.def"
1394     };
1395
1396   assert (is_fmt_type (type));
1397   return &formats[type];
1398 }
1399
1400 const struct fmt_spec F_8_0 = { .type = FMT_F, .w = 8, .d = 0 };
1401 const struct fmt_spec F_8_2 = { .type = FMT_F, .w = 8, .d = 2 };
1402 const struct fmt_spec F_4_3 = { .type = FMT_F, .w = 4, .d = 3 };
1403 const struct fmt_spec F_5_1 = { .type = FMT_F, .w = 5, .d = 1 };