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