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