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