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