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