Rewrite and improve formatted output routines.
[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   switch (fmt_get_category (type)) 
495     {
496     case FMT_CAT_STRING:
497       return FMT_A;
498
499     case FMT_CAT_LEGACY:
500     case FMT_CAT_BINARY:
501     case FMT_CAT_HEXADECIMAL:
502       return FMT_F;
503
504     default:
505       return type;
506     }
507 }
508
509 /* Returns the SPSS format type corresponding to the given PSPP
510    format type. */
511 int
512 fmt_to_io (enum fmt_type type)
513 {
514   return get_fmt_desc (type)->io;
515 }
516
517 /* Determines the PSPP format corresponding to the given SPSS
518    format type.  If successful, sets *FMT_TYPE to the PSPP format
519    and returns true.  On failure, return false. */
520 bool
521 fmt_from_io (int io, enum fmt_type *fmt_type)
522 {
523   enum fmt_type type;
524
525   for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
526     if (get_fmt_desc (type)->io == io)
527       {
528         *fmt_type = type;
529         return true;
530       }
531   return false;
532 }
533
534 /* Returns true if TYPE may be used as an input format,
535    false otherwise. */
536 bool
537 fmt_usable_for_input (enum fmt_type type)
538 {
539   assert (is_fmt_type (type));
540   return fmt_get_category (type) != FMT_CAT_CUSTOM;
541 }
542
543 /* For time and date formats, returns a template used for input
544    and output. */
545 const char *
546 fmt_date_template (enum fmt_type type)
547 {
548   switch (type)
549     {
550     case FMT_DATE:
551       return "dd-mmm-yy";
552     case FMT_ADATE:
553       return "mm/dd/yy";
554     case FMT_EDATE:
555       return "dd.mm.yy";
556     case FMT_JDATE:
557       return "yyddd";
558     case FMT_SDATE:
559       return "yy/mm/dd";
560     case FMT_QYR:
561       return "q Q yy";
562     case FMT_MOYR:
563       return "mmm yy";
564     case FMT_WKYR:
565       return "ww WK yy";
566     case FMT_DATETIME:
567       return "dd-mmm-yyyy HH:MM";
568     case FMT_TIME:
569       return "h:MM";
570     case FMT_DTIME:
571       return "D HH:MM";
572     default:
573       NOT_REACHED ();
574     }
575 }
576 \f
577 /* Returns true if TYPE is a valid format type,
578    false otherwise. */
579 static bool
580 is_fmt_type (enum fmt_type type)
581 {
582   return type < FMT_NUMBER_OF_FORMATS;
583 }
584
585 /* Returns the minimum width of the given format TYPE,
586    for input if FOR_INPUT is true,
587    for output otherwise. */
588 static int
589 min_width (enum fmt_type type, bool for_input)
590 {
591   return for_input ? fmt_min_input_width (type) : fmt_min_output_width (type);
592 }
593
594 /* Returns the maximum width of the given format TYPE,
595    which is invariant between input and output. */
596 static int
597 max_width (enum fmt_type type)
598 {
599   assert (is_fmt_type (type));
600   switch (type)
601     {
602     case FMT_P:
603     case FMT_PK:
604     case FMT_PIBHEX:
605     case FMT_RBHEX:
606       return 16;
607
608     case FMT_IB:
609     case FMT_PIB:
610     case FMT_RB:
611       return 8;
612
613     case FMT_A:
614       return MAX_STRING;
615
616     case FMT_AHEX:
617       return 2 * MAX_STRING;
618
619     default:
620       return 40;
621     }
622 }
623
624 /* Returns true if WIDTH is a valid width for the given format
625    TYPE,
626    for input if FOR_INPUT is true,
627    for output otherwise. */
628 static bool
629 valid_width (enum fmt_type type, int width, bool for_input)
630 {
631   return (width >= min_width (type, for_input)
632           && width <= max_width (type));
633 }
634
635 /* Returns the maximum number of decimal places allowed for the
636    given format TYPE with a width of WIDTH places,
637    for input if FOR_INPUT is true,
638    for output otherwise. */
639 static int
640 max_decimals (enum fmt_type type, int width, bool for_input)
641 {
642   int max_d;
643
644   switch (type)
645     {
646     case FMT_F:
647     case FMT_COMMA:
648     case FMT_DOT:
649       max_d = for_input ? width : width - 1;
650       break;
651
652     case FMT_DOLLAR:
653     case FMT_PCT:
654       max_d = for_input ? width : width - 2;
655       break;
656
657     case FMT_E:
658       max_d = for_input ? width : width - 7;
659       break;
660
661     case FMT_CCA:
662     case FMT_CCB:
663     case FMT_CCC:
664     case FMT_CCD:
665     case FMT_CCE:
666       assert (!for_input);
667       max_d = width - 1;
668       break;
669
670     case FMT_N:
671     case FMT_Z:
672       max_d = width;
673       break;
674
675     case FMT_P:
676       max_d = width * 2 - 1;
677       break;
678
679     case FMT_PK:
680       max_d = width * 2;
681       break;
682
683     case FMT_IB:
684     case FMT_PIB:
685       max_d = max_digits_for_bytes (width);
686       break;
687
688     case FMT_PIBHEX:
689       max_d = 0;
690       break;
691
692     case FMT_RB:
693     case FMT_RBHEX:
694       max_d = 16;
695       break;
696
697     case FMT_DATE:
698     case FMT_ADATE:
699     case FMT_EDATE:
700     case FMT_JDATE:
701     case FMT_SDATE:
702     case FMT_QYR:
703     case FMT_MOYR:
704     case FMT_WKYR:
705       max_d = 0;
706       break;
707
708     case FMT_DATETIME:
709       max_d = width - 21;
710       break;
711
712     case FMT_TIME:
713       max_d = width - 9;
714       break;
715
716     case FMT_DTIME:
717       max_d = width - 12;
718       break;
719
720     case FMT_WKDAY:
721     case FMT_MONTH:
722     case FMT_A:
723     case FMT_AHEX:
724       max_d = 0;
725       break;
726
727     default:
728       NOT_REACHED ();
729     }
730
731   if (max_d < 0)
732     max_d = 0;
733   else if (max_d > 16)
734     max_d = 16;
735   return max_d;
736 }
737
738 /* Returns the maximum number of decimal digits in an unsigned
739    binary number that is BYTES bytes long. */
740 static int
741 max_digits_for_bytes (int bytes)
742 {
743   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
744   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
745   return map[bytes - 1];
746 }
747 \f
748 static struct fmt_number_style *styles[FMT_NUMBER_OF_FORMATS];
749
750 /* Creates and returns a new struct fmt_number_style,
751    initializing all affixes to empty strings. */
752 struct fmt_number_style *
753 fmt_number_style_create (void)
754 {
755   struct fmt_number_style *style = xmalloc (sizeof *style);
756   style->neg_prefix = ss_empty ();
757   style->prefix = ss_empty ();
758   style->suffix = ss_empty ();
759   style->neg_suffix = ss_empty ();
760   style->decimal = '.';
761   style->grouping = 0;
762   return style;
763 }
764
765 /* Destroys a struct fmt_number_style. */
766 void
767 fmt_number_style_destroy (struct fmt_number_style *style)
768 {
769   if (style != NULL)
770     {
771       ss_dealloc (&style->neg_prefix);
772       ss_dealloc (&style->prefix);
773       ss_dealloc (&style->suffix);
774       ss_dealloc (&style->neg_suffix);
775       free (style);
776     }
777 }
778
779 /* Returns the number formatting style associated with the given
780    format TYPE. */
781 const struct fmt_number_style *
782 fmt_get_style (enum fmt_type type)
783 {
784   assert (is_fmt_type (type));
785   assert (styles[type] != NULL);
786   return styles[type];
787 }
788
789 /* Sets STYLE as the number formatting style associated with the
790    given format TYPE, transferring ownership of STYLE.  */
791 void
792 fmt_set_style (enum fmt_type type, struct fmt_number_style *style)
793 {
794   assert (ss_length (style->neg_prefix) <= FMT_STYLE_AFFIX_MAX);
795   assert (ss_length (style->prefix) <= FMT_STYLE_AFFIX_MAX);
796   assert (ss_length (style->suffix) <= FMT_STYLE_AFFIX_MAX);
797   assert (ss_length (style->neg_suffix) <= FMT_STYLE_AFFIX_MAX);
798   assert (style->decimal == '.' || style->decimal == ',');
799   assert (style->grouping != style->decimal
800           && (style->grouping == '.' || style->grouping == ','
801               || style->grouping == 0));
802
803   assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
804   assert (styles[type] != NULL);
805
806   fmt_number_style_destroy (styles[type]);
807   styles[type] = style;
808 }
809
810 /* Returns the total width of the standard prefix and suffix for
811    STYLE. */
812 int
813 fmt_affix_width (const struct fmt_number_style *style)
814 {
815   return ss_length (style->prefix) + ss_length (style->suffix);
816 }
817
818 /* Returns the total width of the negative prefix and suffix for
819    STYLE. */
820 int
821 fmt_neg_affix_width (const struct fmt_number_style *style)
822 {
823   return ss_length (style->neg_prefix) + ss_length (style->neg_suffix);
824 }
825
826 /* Returns the decimal point character for the given format
827    TYPE. */
828 int
829 fmt_decimal_char (enum fmt_type type)
830 {
831   return fmt_get_style (type)->decimal;
832 }
833
834 /* Returns the grouping character for the given format TYPE, or 0
835    if the format type does not group digits. */
836 int
837 fmt_grouping_char (enum fmt_type type)
838 {
839   return fmt_get_style (type)->grouping;
840 }
841
842 /* Sets the number style for TYPE to have the given standard
843    PREFIX and SUFFIX, "-" as prefix suffix, an empty negative
844    suffix, DECIMAL as the decimal point character, and GROUPING
845    as the grouping character. */
846 static void
847 set_style (enum fmt_type type,
848            const char *prefix, const char *suffix,
849            char decimal, char grouping)
850 {
851   struct fmt_number_style *style;
852
853   assert (is_fmt_type (type));
854
855   fmt_number_style_destroy (styles[type]);
856
857   style = styles[type] = fmt_number_style_create ();
858   ss_alloc_substring (&style->neg_prefix, ss_cstr ("-"));
859   ss_alloc_substring (&style->prefix, ss_cstr (prefix));
860   ss_alloc_substring (&style->suffix, ss_cstr (suffix));
861   style->decimal = decimal;
862   style->grouping = grouping;
863 }
864
865 /* Sets the number style for TYPE as with set_style, but only if
866    TYPE has not already been initialized. */
867 static void
868 init_style (enum fmt_type type,
869             const char *prefix, const char *suffix,
870             char decimal, char grouping)
871 {
872   assert (is_fmt_type (type));
873   if (styles[type] == NULL)
874     set_style (type, prefix, suffix, decimal, grouping);
875 }
876
877 /* Sets the decimal point character to DECIMAL. */
878 void
879 fmt_set_decimal (char decimal)
880 {
881   int grouping = decimal == '.' ? ',' : '.';
882   assert (decimal == '.' || decimal == ',');
883
884   set_style (FMT_F, "", "", decimal, 0);
885   set_style (FMT_E, "", "", decimal, 0);
886   set_style (FMT_COMMA, "", "", decimal, grouping);
887   set_style (FMT_DOT, "", "", grouping, decimal);
888   set_style (FMT_DOLLAR, "$", "", decimal, grouping);
889   set_style (FMT_PCT, "", "%", decimal, 0);
890
891   init_style (FMT_CCA, "", "", decimal, grouping);
892   init_style (FMT_CCB, "", "", decimal, grouping);
893   init_style (FMT_CCC, "", "", decimal, grouping);
894   init_style (FMT_CCD, "", "", decimal, grouping);
895   init_style (FMT_CCE, "", "", decimal, grouping);
896 }
897 \f
898 /* Returns true if M is a valid variable measurement level,
899    false otherwise. */
900 bool
901 measure_is_valid (enum measure m)
902 {
903   return m > 0 && m < n_MEASURES;
904 }
905
906 /* Returns true if A is a valid alignment,
907    false otherwise. */
908 bool
909 alignment_is_valid (enum alignment a)
910 {
911   return a < n_ALIGN;
912 }
913 \f
914 /* Returns the struct fmt_desc for the given format TYPE. */
915 static const struct fmt_desc *
916 get_fmt_desc (enum fmt_type type)
917 {
918   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
919     {
920 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
921         {#NAME, IMIN, OMIN, IO, CATEGORY},
922 #include "format.def"
923     };
924
925   assert (is_fmt_type (type));
926   return &formats[type];
927 }