Added result_class parameter to tab_double and updated all callers. Removed tab_fixed
[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 if necessary.
479    If nothing needed to be changed the return value is false
480  */
481 bool
482 fmt_resize (struct fmt_spec *fmt, int width)
483 {
484   if ((width > 0) != fmt_is_string (fmt->type))
485     {
486       /* Changed from numeric to string or vice versa.  Set to
487          default format for new width. */
488       *fmt = fmt_default_for_width (width);
489     }
490   else if (width > 0)
491     {
492       /* Changed width of string.  Preserve format type, adjust
493          width. */
494       fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
495     }
496   else
497     {
498       /* Still numeric. */
499       return false;
500     }
501   return true;
502 }
503
504 /* Adjusts FMT's width and decimal places to be valid for USE.  */
505 void
506 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
507 {
508   /* Clamp width to those allowed by format. */
509   fmt_clamp_width (fmt, use);
510
511   /* If FMT has more decimal places than allowed, attempt to increase FMT's
512      width until that number of decimal places can be achieved. */
513   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
514       && fmt_takes_decimals (fmt->type))
515     {
516       int max_w = fmt_max_width (fmt->type, use);
517       for (; fmt->w < max_w; fmt->w++)
518         if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
519           break;
520     }
521
522   /* Clamp decimals to those allowed by format and width. */
523   fmt_clamp_decimals (fmt, use);
524 }
525
526 /* Adjusts FMT's width and decimal places to be valid for an
527    input format.  */
528 void
529 fmt_fix_input (struct fmt_spec *fmt)
530 {
531   fmt_fix (fmt, FMT_FOR_INPUT);
532 }
533
534 /* Adjusts FMT's width and decimal places to be valid for an
535    output format.  */
536 void
537 fmt_fix_output (struct fmt_spec *fmt)
538 {
539   fmt_fix (fmt, FMT_FOR_OUTPUT);
540 }
541
542 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
543    reduces its decimal places as necessary (if necessary) for that width.  */
544 void
545 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
546 {
547   fmt->w = width;
548   fmt_clamp_width (fmt, use);
549   fmt_clamp_decimals (fmt, use);
550 }
551
552 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
553    places allowed by FMT's type) and increases its width as necessary (if
554    necessary) for that number of decimal places.  */
555 void
556 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
557 {
558   fmt->d = decimals;
559   fmt_fix (fmt, use);
560 }
561 \f
562 /* Describes a display format. */
563 struct fmt_desc
564   {
565     char name[9];
566     int min_input_width, min_output_width;
567     int io;
568     enum fmt_category category;
569   };
570
571 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
572
573 /* Returns the name of the given format TYPE. */
574 const char *
575 fmt_name (enum fmt_type type)
576 {
577   return get_fmt_desc (type)->name;
578 }
579
580 /* Tries to parse NAME as a format type.
581    If successful, stores the type in *TYPE and returns true.
582    On failure, returns false. */
583 bool
584 fmt_from_name (const char *name, enum fmt_type *type)
585 {
586   int i;
587
588   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
589     if (!c_strcasecmp (name, get_fmt_desc (i)->name))
590       {
591         *type = i;
592         return true;
593       }
594   return false;
595 }
596
597 /* Returns true if TYPE accepts decimal places,
598    false otherwise. */
599 bool
600 fmt_takes_decimals (enum fmt_type type)
601 {
602   return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
603 }
604
605 /* Returns the minimum width of the given format TYPE for the given USE. */
606 int
607 fmt_min_width (enum fmt_type type, enum fmt_use use)
608 {
609   return (use == FMT_FOR_INPUT
610           ? fmt_min_input_width (type)
611           : fmt_min_output_width (type));
612 }
613
614 /* Returns the maximum width of the given format TYPE,
615    for input if FOR_INPUT is true,
616    for output otherwise. */
617 int
618 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
619 {
620   /* Maximum width is actually invariant of whether the format is
621      for input or output, so FOR_INPUT is unused. */
622   assert (is_fmt_type (type));
623   switch (type)
624     {
625     case FMT_P:
626     case FMT_PK:
627     case FMT_PIBHEX:
628     case FMT_RBHEX:
629       return 16;
630
631     case FMT_IB:
632     case FMT_PIB:
633     case FMT_RB:
634       return 8;
635
636     case FMT_A:
637       return MAX_STRING;
638
639     case FMT_AHEX:
640       return 2 * MAX_STRING;
641
642     default:
643       return 40;
644     }
645 }
646
647 /* Returns the maximum number of decimal places allowed for the
648    given format TYPE with a width of WIDTH places, for the given USE. */
649 int
650 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
651 {
652   int max_d;
653
654   switch (type)
655     {
656     case FMT_F:
657     case FMT_COMMA:
658     case FMT_DOT:
659       max_d = use == FMT_FOR_INPUT ? width : width - 1;
660       break;
661
662     case FMT_DOLLAR:
663     case FMT_PCT:
664       max_d = use == FMT_FOR_INPUT ? width : width - 2;
665       break;
666
667     case FMT_E:
668       max_d = use == FMT_FOR_INPUT ? width : width - 7;
669       break;
670
671     case FMT_CCA:
672     case FMT_CCB:
673     case FMT_CCC:
674     case FMT_CCD:
675     case FMT_CCE:
676       assert (use == FMT_FOR_OUTPUT);
677       max_d = width - 1;
678       break;
679
680     case FMT_N:
681     case FMT_Z:
682       max_d = width;
683       break;
684
685     case FMT_P:
686       max_d = width * 2 - 1;
687       break;
688
689     case FMT_PK:
690       max_d = width * 2;
691       break;
692
693     case FMT_IB:
694     case FMT_PIB:
695       max_d = max_digits_for_bytes (width);
696       break;
697
698     case FMT_PIBHEX:
699       max_d = 0;
700       break;
701
702     case FMT_RB:
703     case FMT_RBHEX:
704       max_d = 16;
705       break;
706
707     case FMT_DATE:
708     case FMT_ADATE:
709     case FMT_EDATE:
710     case FMT_JDATE:
711     case FMT_SDATE:
712     case FMT_QYR:
713     case FMT_MOYR:
714     case FMT_WKYR:
715       max_d = 0;
716       break;
717
718     case FMT_DATETIME:
719       max_d = width - 21;
720       break;
721
722     case FMT_TIME:
723       max_d = width - 9;
724       break;
725
726     case FMT_DTIME:
727       max_d = width - 12;
728       break;
729
730     case FMT_WKDAY:
731     case FMT_MONTH:
732     case FMT_A:
733     case FMT_AHEX:
734       max_d = 0;
735       break;
736
737     default:
738       NOT_REACHED ();
739     }
740
741   if (max_d < 0)
742     max_d = 0;
743   else if (max_d > 16)
744     max_d = 16;
745   return max_d;
746 }
747
748 /* Returns the minimum acceptable width for an input field
749    formatted with the given TYPE. */
750 int
751 fmt_min_input_width (enum fmt_type type)
752 {
753   return get_fmt_desc (type)->min_input_width;
754 }
755
756 /* Returns the maximum acceptable width for an input field
757    formatted with the given TYPE. */
758 int
759 fmt_max_input_width (enum fmt_type type)
760 {
761   return fmt_max_width (type, FMT_FOR_INPUT);
762 }
763
764 /* Returns the maximum number of decimal places allowed in an
765    input field of the given TYPE and WIDTH. */
766 int
767 fmt_max_input_decimals (enum fmt_type type, int width)
768 {
769   assert (valid_width (type, width, true));
770   return fmt_max_decimals (type, width, FMT_FOR_INPUT);
771 }
772
773 /* Returns the minimum acceptable width for an output field
774    formatted with the given TYPE. */
775 int
776 fmt_min_output_width (enum fmt_type type)
777 {
778   return get_fmt_desc (type)->min_output_width;
779 }
780
781 /* Returns the maximum acceptable width for an output field
782    formatted with the given TYPE. */
783 int
784 fmt_max_output_width (enum fmt_type type)
785 {
786   return fmt_max_width (type, FMT_FOR_OUTPUT);
787 }
788
789 /* Returns the maximum number of decimal places allowed in an
790    output field of the given TYPE and WIDTH. */
791 int
792 fmt_max_output_decimals (enum fmt_type type, int width)
793 {
794   assert (valid_width (type, width, false));
795   return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
796 }
797
798 /* Returns the width step for a field formatted with the given
799    TYPE.  Field width must be a multiple of the width step. */
800 int
801 fmt_step_width (enum fmt_type type)
802 {
803   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
804           ? 2 : 1);
805 }
806
807 /* Returns true if TYPE is used for string fields,
808    false if it is used for numeric fields. */
809 bool
810 fmt_is_string (enum fmt_type type)
811 {
812   return fmt_get_category (type) == FMT_CAT_STRING;
813 }
814
815 /* Returns true if TYPE is used for numeric fields,
816    false if it is used for string fields. */
817 bool
818 fmt_is_numeric (enum fmt_type type)
819 {
820   return !fmt_is_string (type);
821 }
822
823 /* Returns the format TYPE's category.
824    Each format type is in exactly one category,
825    and each category's value is bitwise disjoint from every other
826    category.  Thus, the return value may be tested for equality
827    or compared bitwise against a mask of FMT_CAT_* values. */
828 enum fmt_category
829 fmt_get_category (enum fmt_type type)
830 {
831   return get_fmt_desc (type)->category;
832 }
833
834 /* Returns the output format selected by default when TYPE is
835    used as an input format. */
836 enum fmt_type
837 fmt_input_to_output (enum fmt_type type)
838 {
839   switch (fmt_get_category (type))
840     {
841     case FMT_CAT_STRING:
842       return FMT_A;
843
844     case FMT_CAT_LEGACY:
845     case FMT_CAT_BINARY:
846     case FMT_CAT_HEXADECIMAL:
847       return FMT_F;
848
849     default:
850       return type;
851     }
852 }
853
854 /* Returns the SPSS format type corresponding to the given PSPP
855    format type. */
856 int
857 fmt_to_io (enum fmt_type type)
858 {
859   return get_fmt_desc (type)->io;
860 }
861
862 /* Determines the PSPP format corresponding to the given SPSS
863    format type.  If successful, sets *FMT_TYPE to the PSPP format
864    and returns true.  On failure, return false. */
865 bool
866 fmt_from_io (int io, enum fmt_type *fmt_type)
867 {
868   switch (io)
869     {
870 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY)     \
871     case IO:                                          \
872       *fmt_type = FMT_##NAME;                           \
873       return true;
874 #include "format.def"
875     default:
876       return false;
877     }
878 }
879
880 /* Returns true if TYPE may be used as an input format,
881    false otherwise. */
882 bool
883 fmt_usable_for_input (enum fmt_type type)
884 {
885   assert (is_fmt_type (type));
886   return fmt_get_category (type) != FMT_CAT_CUSTOM;
887 }
888
889 /* For time and date formats, returns a template used for input and output in a
890    field of the given WIDTH.
891
892    WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
893    is, whether the returned string contains "yy" or "yyyy", and whether seconds
894    are include, that is, whether the returned string contains ":SS".  A caller
895    that doesn't care whether the returned string contains "yy" or "yyyy" or
896    ":SS" can just specify 0 to omit them. */
897 const char *
898 fmt_date_template (enum fmt_type type, int width)
899 {
900   const char *s1, *s2;
901
902   switch (type)
903     {
904     case FMT_DATE:
905       s1 = "dd-mmm-yy";
906       s2 = "dd-mmm-yyyy";
907       break;
908
909     case FMT_ADATE:
910       s1 = "mm/dd/yy";
911       s2 = "mm/dd/yyyy";
912       break;
913
914     case FMT_EDATE:
915       s1 = "dd.mm.yy";
916       s2 = "dd.mm.yyyy";
917       break;
918
919     case FMT_JDATE:
920       s1 = "yyddd";
921       s2 = "yyyyddd";
922       break;
923
924     case FMT_SDATE:
925       s1 = "yy/mm/dd";
926       s2 = "yyyy/mm/dd";
927       break;
928
929     case FMT_QYR:
930       s1 = "q Q yy";
931       s2 = "q Q yyyy";
932       break;
933
934     case FMT_MOYR:
935       s1 = "mmm yy";
936       s2 = "mmm yyyy";
937       break;
938
939     case FMT_WKYR:
940       s1 = "ww WK yy";
941       s2 = "ww WK yyyy";
942       break;
943
944     case FMT_DATETIME:
945       s1 = "dd-mmm-yyyy HH:MM";
946       s2 = "dd-mmm-yyyy HH:MM:SS";
947       break;
948
949     case FMT_TIME:
950       s1 = "H:MM";
951       s2 = "H:MM:SS";
952       break;
953
954     case FMT_DTIME:
955       s1 = "D HH:MM";
956       s2 = "D HH:MM:SS";
957       break;
958
959     default:
960       NOT_REACHED ();
961     }
962
963   return width >= strlen (s2) ? s2 : s1;
964 }
965
966 /* Returns a string representing the format TYPE for use in a GUI dialog. */
967 const char *
968 fmt_gui_name (enum fmt_type type)
969 {
970   switch (type)
971     {
972     case FMT_F:
973       return _("Numeric");
974
975     case FMT_COMMA:
976       return _("Comma");
977
978     case FMT_DOT:
979       return _("Dot");
980
981     case FMT_E:
982       return _("Scientific");
983
984     case FMT_DATE:
985     case FMT_EDATE:
986     case FMT_SDATE:
987     case FMT_ADATE:
988     case FMT_JDATE:
989     case FMT_QYR:
990     case FMT_MOYR:
991     case FMT_WKYR:
992     case FMT_DATETIME:
993     case FMT_TIME:
994     case FMT_DTIME:
995     case FMT_WKDAY:
996     case FMT_MONTH:
997       return _("Date");
998
999     case FMT_DOLLAR:
1000       return _("Dollar");
1001
1002     case FMT_CCA:
1003     case FMT_CCB:
1004     case FMT_CCC:
1005     case FMT_CCD:
1006     case FMT_CCE:
1007       return _("Custom");
1008
1009     case FMT_A:
1010       return _("String");
1011
1012     default:
1013       return fmt_name (type);
1014     }
1015 }
1016 \f
1017 /* Returns true if TYPE is a valid format type,
1018    false otherwise. */
1019 bool
1020 is_fmt_type (enum fmt_type type)
1021 {
1022   return type < FMT_NUMBER_OF_FORMATS;
1023 }
1024
1025 /* Returns true if WIDTH is a valid width for the given format
1026    TYPE, for the given USE. */
1027 static bool
1028 valid_width (enum fmt_type type, int width, enum fmt_use use)
1029 {
1030   return (width >= fmt_min_width (type, use)
1031           && width <= fmt_max_width (type, use));
1032 }
1033
1034 /* Returns the maximum number of decimal digits in an unsigned
1035    binary number that is BYTES bytes long. */
1036 static int
1037 max_digits_for_bytes (int bytes)
1038 {
1039   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1040   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1041   return map[bytes - 1];
1042 }
1043
1044 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1045 static void
1046 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1047 {
1048   unsigned int step;
1049   int min_w, max_w;
1050
1051   min_w = fmt_min_width (fmt->type, use);
1052   max_w = fmt_max_width (fmt->type, use);
1053   if (fmt->w < min_w)
1054     fmt->w = min_w;
1055   else if (fmt->w > max_w)
1056     fmt->w = max_w;
1057
1058   /* Round width to step. */
1059   step = fmt_step_width (fmt->type);
1060   fmt->w = ROUND_DOWN (fmt->w, step);
1061 }
1062
1063 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1064 static void
1065 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1066 {
1067   int max_d;
1068
1069   max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1070   if (fmt->d < 0)
1071     fmt->d = 0;
1072   else if (fmt->d > max_d)
1073     fmt->d = max_d;
1074 }
1075 \f
1076 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
1077 static void
1078 fmt_affix_set (struct fmt_affix *affix, const char *s)
1079 {
1080   affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1081   affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1082 }
1083
1084 /* Frees data in AFFIX. */
1085 static void
1086 fmt_affix_free (struct fmt_affix *affix)
1087 {
1088   if (affix->s[0])
1089     free (affix->s);
1090 }
1091
1092 static void
1093 fmt_number_style_init (struct fmt_number_style *style)
1094 {
1095   fmt_affix_set (&style->neg_prefix, "");
1096   fmt_affix_set (&style->prefix, "");
1097   fmt_affix_set (&style->suffix, "");
1098   fmt_affix_set (&style->neg_suffix, "");
1099   style->decimal = '.';
1100   style->grouping = 0;
1101 }
1102
1103 static void
1104 fmt_number_style_clone (struct fmt_number_style *new,
1105                         const struct fmt_number_style *old)
1106 {
1107   fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1108   fmt_affix_set (&new->prefix, old->prefix.s);
1109   fmt_affix_set (&new->suffix, old->suffix.s);
1110   fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1111   new->decimal = old->decimal;
1112   new->grouping = old->grouping;
1113   new->extra_bytes = old->extra_bytes;
1114 }
1115
1116 /* Destroys a struct fmt_number_style. */
1117 static void
1118 fmt_number_style_destroy (struct fmt_number_style *style)
1119 {
1120   if (style != NULL)
1121     {
1122       fmt_affix_free (&style->neg_prefix);
1123       fmt_affix_free (&style->prefix);
1124       fmt_affix_free (&style->suffix);
1125       fmt_affix_free (&style->neg_suffix);
1126     }
1127 }
1128
1129 /* Returns the total width of the standard prefix and suffix for STYLE, in
1130    display columns (e.g. as returned by u8_strwidth()). */
1131 int
1132 fmt_affix_width (const struct fmt_number_style *style)
1133 {
1134   return style->prefix.width + style->suffix.width;
1135 }
1136
1137 /* Returns the total width of the negative prefix and suffix for STYLE, in
1138    display columns (e.g. as returned by u8_strwidth()). */
1139 int
1140 fmt_neg_affix_width (const struct fmt_number_style *style)
1141 {
1142   return style->neg_prefix.width + style->neg_suffix.width;
1143 }
1144
1145 /* Returns the struct fmt_desc for the given format TYPE. */
1146 static const struct fmt_desc *
1147 get_fmt_desc (enum fmt_type type)
1148 {
1149   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1150     {
1151 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1152         {#NAME, IMIN, OMIN, IO, CATEGORY},
1153 #include "format.def"
1154     };
1155
1156   assert (is_fmt_type (type));
1157   return &formats[type];
1158 }
1159
1160 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};
1161 const struct fmt_spec F_8_2 = {FMT_F, 8, 2};
1162 const struct fmt_spec F_4_3 = {FMT_F, 4, 3};
1163 const struct fmt_spec F_5_1 = {FMT_F, 5, 1};