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