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