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