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