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