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