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