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