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