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