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