aa8723482840dfb05a53057987a5d564d896c59d
[pspp] / 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
412 /* Adjusts FMT's width and decimal places to be valid for an
413    input format (if FOR_INPUT) or an output format (if
414    !FOR_INPUT).  */
415 void
416 fmt_fix (struct fmt_spec *fmt, bool for_input)
417 {
418   int min_w, max_w;
419   int max_d;
420
421   /* Clamp width to those allowed by format. */
422   min_w = fmt_min_width (fmt->type, for_input);
423   max_w = fmt_max_width (fmt->type, for_input);
424   if (fmt->w < min_w)
425     fmt->w = min_w;
426   else if (fmt->w > max_w)
427     fmt->w = max_w;
428
429   /* First, if FMT has more decimal places than allowed, attempt
430      to increase FMT's width until that number of decimal places
431      can be achieved. */
432   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, for_input))
433     {
434       int w;
435       for (w = fmt->w; w <= max_w; w++)
436         if (fmt_max_decimals (fmt->type, w, for_input) >= fmt->d)
437           {
438             fmt->w = w;
439             break;
440           }
441     }
442
443   /* Clamp decimals to those allowed by format and width. */
444   max_d = fmt_max_decimals (fmt->type, fmt->w, for_input);
445   if (fmt->d < 0)
446     fmt->d = 0;
447   else if (fmt->d > max_d)
448     fmt->d = max_d;
449 }
450
451 /* Adjusts FMT's width and decimal places to be valid for an
452    input format.  */
453 void
454 fmt_fix_input (struct fmt_spec *fmt)
455 {
456   fmt_fix (fmt, true);
457 }
458
459 /* Adjusts FMT's width and decimal places to be valid for an
460    output format.  */
461 void
462 fmt_fix_output (struct fmt_spec *fmt)
463 {
464   fmt_fix (fmt, false);
465 }
466 \f
467 /* Describes a display format. */
468 struct fmt_desc
469   {
470     char name[9];
471     int min_input_width, min_output_width;
472     int io;
473     enum fmt_category category;
474   };
475
476 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
477
478 /* Returns the name of the given format TYPE. */
479 const char *
480 fmt_name (enum fmt_type type)
481 {
482   return get_fmt_desc (type)->name;
483 }
484
485 /* Tries to parse NAME as a format type.
486    If successful, stores the type in *TYPE and returns true.
487    On failure, returns false. */
488 bool
489 fmt_from_name (const char *name, enum fmt_type *type)
490 {
491   int i;
492
493   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
494     if (!strcasecmp (name, get_fmt_desc (i)->name))
495       {
496         *type = i;
497         return true;
498       }
499   return false;
500 }
501
502 /* Returns true if TYPE accepts decimal places,
503    false otherwise. */
504 bool
505 fmt_takes_decimals (enum fmt_type type)
506 {
507   return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
508 }
509
510 /* Returns the minimum width of the given format TYPE,
511    for input if FOR_INPUT is true,
512    for output otherwise. */
513 int
514 fmt_min_width (enum fmt_type type, bool for_input)
515 {
516   return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
517 }
518
519 /* Returns the maximum width of the given format TYPE,
520    for input if FOR_INPUT is true,
521    for output otherwise. */
522 int
523 fmt_max_width (enum fmt_type type, bool for_input UNUSED)
524 {
525   /* Maximum width is actually invariant of whether the format is
526      for input or output, so FOR_INPUT is unused. */
527   assert (is_fmt_type (type));
528   switch (type)
529     {
530     case FMT_P:
531     case FMT_PK:
532     case FMT_PIBHEX:
533     case FMT_RBHEX:
534       return 16;
535
536     case FMT_IB:
537     case FMT_PIB:
538     case FMT_RB:
539       return 8;
540
541     case FMT_A:
542       return MAX_STRING;
543
544     case FMT_AHEX:
545       return 2 * MAX_STRING;
546
547     default:
548       return 40;
549     }
550 }
551
552 /* Returns the maximum number of decimal places allowed for the
553    given format TYPE with a width of WIDTH places,
554    for input if FOR_INPUT is true,
555    for output otherwise. */
556 int
557 fmt_max_decimals (enum fmt_type type, int width, bool for_input)
558 {
559   int max_d;
560
561   switch (type)
562     {
563     case FMT_F:
564     case FMT_COMMA:
565     case FMT_DOT:
566       max_d = for_input ? width : width - 1;
567       break;
568
569     case FMT_DOLLAR:
570     case FMT_PCT:
571       max_d = for_input ? width : width - 2;
572       break;
573
574     case FMT_E:
575       max_d = for_input ? width : width - 7;
576       break;
577
578     case FMT_CCA:
579     case FMT_CCB:
580     case FMT_CCC:
581     case FMT_CCD:
582     case FMT_CCE:
583       assert (!for_input);
584       max_d = width - 1;
585       break;
586
587     case FMT_N:
588     case FMT_Z:
589       max_d = width;
590       break;
591
592     case FMT_P:
593       max_d = width * 2 - 1;
594       break;
595
596     case FMT_PK:
597       max_d = width * 2;
598       break;
599
600     case FMT_IB:
601     case FMT_PIB:
602       max_d = max_digits_for_bytes (width);
603       break;
604
605     case FMT_PIBHEX:
606       max_d = 0;
607       break;
608
609     case FMT_RB:
610     case FMT_RBHEX:
611       max_d = 16;
612       break;
613
614     case FMT_DATE:
615     case FMT_ADATE:
616     case FMT_EDATE:
617     case FMT_JDATE:
618     case FMT_SDATE:
619     case FMT_QYR:
620     case FMT_MOYR:
621     case FMT_WKYR:
622       max_d = 0;
623       break;
624
625     case FMT_DATETIME:
626       max_d = width - 21;
627       break;
628
629     case FMT_TIME:
630       max_d = width - 9;
631       break;
632
633     case FMT_DTIME:
634       max_d = width - 12;
635       break;
636
637     case FMT_WKDAY:
638     case FMT_MONTH:
639     case FMT_A:
640     case FMT_AHEX:
641       max_d = 0;
642       break;
643
644     default:
645       NOT_REACHED ();
646     }
647
648   if (max_d < 0)
649     max_d = 0;
650   else if (max_d > 16)
651     max_d = 16;
652   return max_d;
653 }
654
655 /* Returns the minimum acceptable width for an input field
656    formatted with the given TYPE. */
657 int
658 fmt_min_input_width (enum fmt_type type)
659 {
660   return get_fmt_desc (type)->min_input_width;
661 }
662
663 /* Returns the maximum acceptable width for an input field
664    formatted with the given TYPE. */
665 int
666 fmt_max_input_width (enum fmt_type type)
667 {
668   return fmt_max_width (type, true);
669 }
670
671 /* Returns the maximum number of decimal places allowed in an
672    input field of the given TYPE and WIDTH. */
673 int
674 fmt_max_input_decimals (enum fmt_type type, int width)
675 {
676   assert (valid_width (type, width, true));
677   return fmt_max_decimals (type, width, true);
678 }
679
680 /* Returns the minimum acceptable width for an output field
681    formatted with the given TYPE. */
682 int
683 fmt_min_output_width (enum fmt_type type)
684 {
685   return get_fmt_desc (type)->min_output_width;
686 }
687
688 /* Returns the maximum acceptable width for an output field
689    formatted with the given TYPE. */
690 int
691 fmt_max_output_width (enum fmt_type type)
692 {
693   return fmt_max_width (type, false);
694 }
695
696 /* Returns the maximum number of decimal places allowed in an
697    output field of the given TYPE and WIDTH. */
698 int
699 fmt_max_output_decimals (enum fmt_type type, int width)
700 {
701   assert (valid_width (type, width, false));
702   return fmt_max_decimals (type, width, false);
703 }
704
705 /* Returns the width step for a field formatted with the given
706    TYPE.  Field width must be a multiple of the width step. */
707 int
708 fmt_step_width (enum fmt_type type)
709 {
710   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
711           ? 2 : 1);
712 }
713
714 /* Returns true if TYPE is used for string fields,
715    false if it is used for numeric fields. */
716 bool
717 fmt_is_string (enum fmt_type type)
718 {
719   return fmt_get_category (type) == FMT_CAT_STRING;
720 }
721
722 /* Returns true if TYPE is used for numeric fields,
723    false if it is used for string fields. */
724 bool
725 fmt_is_numeric (enum fmt_type type)
726 {
727   return !fmt_is_string (type);
728 }
729
730 /* Returns the format TYPE's category.
731    Each format type is in exactly one category,
732    and each category's value is bitwise disjoint from every other
733    category.  Thus, the return value may be tested for equality
734    or compared bitwise against a mask of FMT_CAT_* values. */
735 enum fmt_category
736 fmt_get_category (enum fmt_type type)
737 {
738   return get_fmt_desc (type)->category;
739 }
740
741 /* Returns the output format selected by default when TYPE is
742    used as an input format. */
743 enum fmt_type
744 fmt_input_to_output (enum fmt_type type)
745 {
746   switch (fmt_get_category (type))
747     {
748     case FMT_CAT_STRING:
749       return FMT_A;
750
751     case FMT_CAT_LEGACY:
752     case FMT_CAT_BINARY:
753     case FMT_CAT_HEXADECIMAL:
754       return FMT_F;
755
756     default:
757       return type;
758     }
759 }
760
761 /* Returns the SPSS format type corresponding to the given PSPP
762    format type. */
763 int
764 fmt_to_io (enum fmt_type type)
765 {
766   return get_fmt_desc (type)->io;
767 }
768
769 /* Determines the PSPP format corresponding to the given SPSS
770    format type.  If successful, sets *FMT_TYPE to the PSPP format
771    and returns true.  On failure, return false. */
772 bool
773 fmt_from_io (int io, enum fmt_type *fmt_type)
774 {
775   enum fmt_type type;
776
777   for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
778     if (get_fmt_desc (type)->io == io)
779       {
780         *fmt_type = type;
781         return true;
782       }
783   return false;
784 }
785
786 /* Returns true if TYPE may be used as an input format,
787    false otherwise. */
788 bool
789 fmt_usable_for_input (enum fmt_type type)
790 {
791   assert (is_fmt_type (type));
792   return fmt_get_category (type) != FMT_CAT_CUSTOM;
793 }
794
795 /* For time and date formats, returns a template used for input
796    and output. */
797 const char *
798 fmt_date_template (enum fmt_type type)
799 {
800   switch (type)
801     {
802     case FMT_DATE:
803       return "dd-mmm-yy";
804     case FMT_ADATE:
805       return "mm/dd/yy";
806     case FMT_EDATE:
807       return "dd.mm.yy";
808     case FMT_JDATE:
809       return "yyddd";
810     case FMT_SDATE:
811       return "yy/mm/dd";
812     case FMT_QYR:
813       return "q Q yy";
814     case FMT_MOYR:
815       return "mmmXyy";
816     case FMT_WKYR:
817       return "ww WK yy";
818     case FMT_DATETIME:
819       return "dd-mmm-yyyy HH:MM";
820     case FMT_TIME:
821       return "H:MM";
822     case FMT_DTIME:
823       return "D HH:MM";
824     default:
825       NOT_REACHED ();
826     }
827 }
828
829 \f
830 /* Returns true if TYPE is a valid format type,
831    false otherwise. */
832 bool
833 is_fmt_type (enum fmt_type type)
834 {
835   return type < FMT_NUMBER_OF_FORMATS;
836 }
837
838 /* Returns true if WIDTH is a valid width for the given format
839    TYPE,
840    for input if FOR_INPUT is true,
841    for output otherwise. */
842 static bool
843 valid_width (enum fmt_type type, int width, bool for_input)
844 {
845   return (width >= fmt_min_width (type, for_input)
846           && width <= fmt_max_width (type, for_input));
847 }
848
849 /* Returns the maximum number of decimal digits in an unsigned
850    binary number that is BYTES bytes long. */
851 static int
852 max_digits_for_bytes (int bytes)
853 {
854   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
855   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
856   return map[bytes - 1];
857 }
858 \f
859
860
861 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 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};