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