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