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