fef7c1c3e4d6aa0d7210118584fb38d183a86439
[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   assert (fmt->type == FMT_DOLLAR);
601
602   ds_put_char (&s, '$');
603   for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
604     {
605       ds_put_char (&s, '#');
606       if (--c % 4 == 0 && c > 0) 
607         {
608           ds_put_char (&s, fmt_grouping_char (fmt->type));
609           --c;
610         }
611     }
612   if (fmt->d > 0) 
613     {
614       ds_put_char (&s, fmt_decimal_char (fmt->type));
615       ds_put_char_multiple (&s, '#', fmt->d);
616     }
617
618   return ds_cstr (&s);
619 }
620 \f
621 /* Returns true if TYPE is a valid format type,
622    false otherwise. */
623 static bool
624 is_fmt_type (enum fmt_type type)
625 {
626   return type < FMT_NUMBER_OF_FORMATS;
627 }
628
629 /* Returns the minimum width of the given format TYPE,
630    for input if FOR_INPUT is true,
631    for output otherwise. */
632 static int
633 min_width (enum fmt_type type, bool for_input)
634 {
635   return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
636 }
637
638 /* Returns the maximum width of the given format TYPE,
639    which is invariant between input and output. */
640 static int
641 max_width (enum fmt_type type)
642 {
643   assert (is_fmt_type (type));
644   switch (type)
645     {
646     case FMT_P:
647     case FMT_PK:
648     case FMT_PIBHEX:
649     case FMT_RBHEX:
650       return 16;
651
652     case FMT_IB:
653     case FMT_PIB:
654     case FMT_RB:
655       return 8;
656
657     case FMT_A:
658       return MAX_STRING;
659
660     case FMT_AHEX:
661       return 2 * MAX_STRING;
662
663     default:
664       return 40;
665     }
666 }
667
668 /* Returns true if WIDTH is a valid width for the given format
669    TYPE,
670    for input if FOR_INPUT is true,
671    for output otherwise. */
672 static bool
673 valid_width (enum fmt_type type, int width, bool for_input)
674 {
675   return (width >= min_width (type, for_input)
676           && width <= max_width (type));
677 }
678
679 /* Returns the maximum number of decimal places allowed for the
680    given format TYPE with a width of WIDTH places,
681    for input if FOR_INPUT is true,
682    for output otherwise. */
683 static int
684 max_decimals (enum fmt_type type, int width, bool for_input)
685 {
686   int max_d;
687
688   switch (type)
689     {
690     case FMT_F:
691     case FMT_COMMA:
692     case FMT_DOT:
693       max_d = for_input ? width : width - 1;
694       break;
695
696     case FMT_DOLLAR:
697     case FMT_PCT:
698       max_d = for_input ? width : width - 2;
699       break;
700
701     case FMT_E:
702       max_d = for_input ? width : width - 7;
703       break;
704
705     case FMT_CCA:
706     case FMT_CCB:
707     case FMT_CCC:
708     case FMT_CCD:
709     case FMT_CCE:
710       assert (!for_input);
711       max_d = width - 1;
712       break;
713
714     case FMT_N:
715     case FMT_Z:
716       max_d = width;
717       break;
718
719     case FMT_P:
720       max_d = width * 2 - 1;
721       break;
722
723     case FMT_PK:
724       max_d = width * 2;
725       break;
726
727     case FMT_IB:
728     case FMT_PIB:
729       max_d = max_digits_for_bytes (width);
730       break;
731
732     case FMT_PIBHEX:
733       max_d = 0;
734       break;
735
736     case FMT_RB:
737     case FMT_RBHEX:
738       max_d = 16;
739       break;
740
741     case FMT_DATE:
742     case FMT_ADATE:
743     case FMT_EDATE:
744     case FMT_JDATE:
745     case FMT_SDATE:
746     case FMT_QYR:
747     case FMT_MOYR:
748     case FMT_WKYR:
749       max_d = 0;
750       break;
751
752     case FMT_DATETIME:
753       max_d = width - 21;
754       break;
755
756     case FMT_TIME:
757       max_d = width - 9;
758       break;
759
760     case FMT_DTIME:
761       max_d = width - 12;
762       break;
763
764     case FMT_WKDAY:
765     case FMT_MONTH:
766     case FMT_A:
767     case FMT_AHEX:
768       max_d = 0;
769       break;
770
771     default:
772       NOT_REACHED ();
773     }
774
775   if (max_d < 0)
776     max_d = 0;
777   else if (max_d > 16)
778     max_d = 16;
779   return max_d;
780 }
781
782 /* Returns the maximum number of decimal digits in an unsigned
783    binary number that is BYTES bytes long. */
784 static int
785 max_digits_for_bytes (int bytes)
786 {
787   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
788   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
789   return map[bytes - 1];
790 }
791 \f
792
793 /* Creates and returns a new struct fmt_number_style,
794    initializing all affixes to empty strings. */
795 struct fmt_number_style *
796 fmt_number_style_create (void)
797 {
798   struct fmt_number_style *style = xmalloc (sizeof *style);
799   style->neg_prefix = ss_empty ();
800   style->prefix = ss_empty ();
801   style->suffix = ss_empty ();
802   style->neg_suffix = ss_empty ();
803   style->decimal = '.';
804   style->grouping = 0;
805   return style;
806 }
807
808 /* Destroys a struct fmt_number_style. */
809 void
810 fmt_number_style_destroy (struct fmt_number_style *style)
811 {
812   if (style != NULL)
813     {
814       ss_dealloc (&style->neg_prefix);
815       ss_dealloc (&style->prefix);
816       ss_dealloc (&style->suffix);
817       ss_dealloc (&style->neg_suffix);
818       free (style);
819     }
820 }
821
822 /* Returns the number formatting style associated with the given
823    format TYPE. */
824 const struct fmt_number_style *
825 fmt_get_style (enum fmt_type type)
826 {
827   assert (is_fmt_type (type));
828   assert (styles[type] != NULL);
829   return styles[type];
830 }
831
832 /* Sets STYLE as the number formatting style associated with the
833    given format TYPE, transferring ownership of STYLE.  */
834 void
835 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
836 {
837   assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
838   assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
839   assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
840   assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
841   assert (style->decimal == '.' || style->decimal == ',');
842   assert (style->grouping != style->decimal
843           && (style->grouping == '.' || style->grouping == ','
844               || style->grouping == 0));
845
846   assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
847   assert (styles[type] != NULL);
848
849   fmt_number_style_destroy (styles[type]);
850   styles[type] = style;
851 }
852
853 /* Returns the total width of the standard prefix and suffix for
854    STYLE. */
855 int
856 fmt_affix_width (const struct fmt_number_style *style)
857 {
858   return ss_length (style->prefix) + ss_length (style->suffix);
859 }
860
861 /* Returns the total width of the negative prefix and suffix for
862    STYLE. */
863 int
864 fmt_neg_affix_width (const struct fmt_number_style *style)
865 {
866   return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
867 }
868
869 /* Returns the decimal point character for the given format
870    TYPE. */
871 int
872 fmt_decimal_char (enum fmt_type type)
873 {
874   return fmt_get_style (type)->decimal;
875 }
876
877 /* Returns the grouping character for the given format TYPE, or 0
878    if the format type does not group digits. */
879 int
880 fmt_grouping_char (enum fmt_type type)
881 {
882   return fmt_get_style (type)->grouping;
883 }
884
885 /* Sets the number style for TYPE to have the given standard
886    PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
887    suffix, DECIMAL as the decimal point character, and GROUPING
888    as the grouping character. */
889 static void
890 set_style (enum fmt_type type,
891            const char *prefix, const char *suffix,
892            char decimal, char grouping)
893 {
894   struct fmt_number_style *style;
895
896   assert (is_fmt_type (type));
897
898   fmt_number_style_destroy (styles[type]);
899
900   style = styles[type] = fmt_number_style_create ();
901   ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
902   ss_alloc_substring (&style->prefix, ss_cstr (prefix));
903   ss_alloc_substring (&style->suffix, ss_cstr (suffix));
904   style->decimal = decimal;
905   style->grouping = grouping;
906 }
907
908 /* Sets the number style for TYPE as with set_style, but only if
909    TYPE has not already been initialized. */
910 static void
911 init_style (enum fmt_type type,
912             const char *prefix, const char *suffix,
913             char decimal, char grouping)
914 {
915   assert (is_fmt_type (type));
916   if (styles[type] == NULL)
917     set_style (type, prefix, suffix, decimal, grouping);
918 }
919
920 /* Sets the decimal point character to DECIMAL. */
921 void
922 fmt_set_decimal (char decimal)
923 {
924   int grouping = decimal == '.' ? ',' : '.';
925   assert (decimal == '.' || decimal == ',');
926
927   set_style (FMT_F, "", "", decimal, 0);
928   set_style (FMT_E, "", "", decimal, 0);
929   set_style (FMT_COMMA, "", "", decimal, grouping);
930   set_style (FMT_DOT, "", "", grouping, decimal);
931   set_style (FMT_DOLLAR, "$", "", decimal, grouping);
932   set_style (FMT_PCT, "", "%", decimal, 0);
933
934   init_style (FMT_CCA, "", "", decimal, grouping);
935   init_style (FMT_CCB, "", "", decimal, grouping);
936   init_style (FMT_CCC, "", "", decimal, grouping);
937   init_style (FMT_CCD, "", "", decimal, grouping);
938   init_style (FMT_CCE, "", "", decimal, grouping);
939 }
940 \f
941 /* Returns the struct fmt_desc for the given format TYPE. */
942 static const struct fmt_desc *
943 get_fmt_desc (enum fmt_type type)
944 {
945   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
946     {
947 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
948         {#NAME, IMIN, OMIN, IO, CATEGORY},
949 #include "format.def"
950     };
951
952   assert (is_fmt_type (type));
953   return &formats[type];
954 }