5a347d4d58e18acc904abf03f099252c14f71a1b
[pspp] / src / data / format.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2010, 2011, 2012 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 #include <uniwidth.h>
24
25 #include "data/identifier.h"
26 #include "data/settings.h"
27 #include "data/value.h"
28 #include "data/variable.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/cast.h"
31 #include "libpspp/compiler.h"
32 #include "libpspp/message.h"
33 #include "libpspp/misc.h"
34 #include "libpspp/str.h"
35
36 #include "gl/c-strcase.h"
37 #include "gl/minmax.h"
38 #include "gl/xalloc.h"
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42
43 struct fmt_settings
44   {
45     struct fmt_number_style styles[FMT_NUMBER_OF_FORMATS];
46   };
47
48 bool is_fmt_type (enum fmt_type);
49
50 static bool valid_width (enum fmt_type, int width, enum fmt_use);
51
52 static int max_digits_for_bytes (int bytes);
53 static void fmt_clamp_width (struct fmt_spec *, enum fmt_use);
54 static void fmt_clamp_decimals (struct fmt_spec *, enum fmt_use);
55
56 static void fmt_affix_set (struct fmt_affix *, const char *);
57 static void fmt_affix_free (struct fmt_affix *);
58
59 static void fmt_number_style_init (struct fmt_number_style *);
60 static void fmt_number_style_clone (struct fmt_number_style *,
61                                     const struct fmt_number_style *);
62 static void fmt_number_style_destroy (struct fmt_number_style *);
63
64 /* Creates and returns a new struct fmt_settings with default format styles. */
65 struct fmt_settings *
66 fmt_settings_create (void)
67 {
68   struct fmt_settings *settings;
69   int t;
70
71   settings = xzalloc (sizeof *settings);
72   for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
73     fmt_number_style_init (&settings->styles[t]);
74   fmt_settings_set_decimal (settings, '.');
75
76   return settings;
77 }
78
79 /* Destroys SETTINGS. */
80 void
81 fmt_settings_destroy (struct fmt_settings *settings)
82 {
83   if (settings != NULL)
84     {
85       int t;
86
87       for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
88         fmt_number_style_destroy (&settings->styles[t]);
89
90       free (settings->styles);
91     }
92 }
93
94 /* Returns a copy of SETTINGS. */
95 struct fmt_settings *
96 fmt_settings_clone (const struct fmt_settings *old)
97 {
98   struct fmt_settings *new;
99   int t;
100
101   new = xmalloc (sizeof *new);
102   for (t = 0 ; t < FMT_NUMBER_OF_FORMATS ; ++t )
103     fmt_number_style_clone (&new->styles[t], &old->styles[t]);
104
105   return new;
106 }
107
108 /* Returns the number formatting style associated with the given
109    format TYPE. */
110 const struct fmt_number_style *
111 fmt_settings_get_style (const struct fmt_settings *settings,
112                         enum fmt_type type)
113 {
114   assert (is_fmt_type (type));
115   return &settings->styles[type];
116 }
117
118 /* Sets the number style for TYPE to have the given DECIMAL and GROUPING
119    characters, negative prefix NEG_PREFIX, prefix PREFIX, suffix SUFFIX, and
120    negative suffix NEG_SUFFIX.  All of the strings are UTF-8 encoded. */
121 void
122 fmt_settings_set_style (struct fmt_settings *settings, enum fmt_type type,
123                         char decimal, char grouping,
124                         const char *neg_prefix, const char *prefix,
125                         const char *suffix, const char *neg_suffix)
126 {
127   struct fmt_number_style *style = &settings->styles[type];
128   int total_bytes, total_width;
129
130   assert (grouping == '.' || grouping == ',' || grouping == 0);
131   assert (decimal == '.' || decimal == ',');
132   assert (decimal != grouping);
133
134   fmt_number_style_destroy (style);
135
136   fmt_affix_set (&style->neg_prefix, neg_prefix);
137   fmt_affix_set (&style->prefix, prefix);
138   fmt_affix_set (&style->suffix, suffix);
139   fmt_affix_set (&style->neg_suffix, neg_suffix);
140   style->decimal = decimal;
141   style->grouping = grouping;
142
143   total_bytes = (strlen (neg_prefix) + strlen (prefix)
144                  + strlen (suffix) + strlen (neg_suffix));
145   total_width = (style->neg_prefix.width + style->prefix.width
146                  + style->suffix.width + style->neg_suffix.width);
147   style->extra_bytes = MAX (0, total_bytes - total_width);
148 }
149
150 /* Sets the decimal point character for the settings in S to DECIMAL.
151
152    This has no effect on custom currency formats. */
153 void
154 fmt_settings_set_decimal (struct fmt_settings *s, char decimal)
155 {
156   int grouping = decimal == '.' ? ',' : '.';
157   assert (decimal == '.' || decimal == ',');
158
159   fmt_settings_set_style (s, FMT_F,      decimal,        0, "-",  "",  "", "");
160   fmt_settings_set_style (s, FMT_E,      decimal,        0, "-",  "",  "", "");
161   fmt_settings_set_style (s, FMT_COMMA,  decimal, grouping, "-",  "",  "", "");
162   fmt_settings_set_style (s, FMT_DOT,   grouping,  decimal, "-",  "",  "", "");
163   fmt_settings_set_style (s, FMT_DOLLAR, decimal, grouping, "-", "$",  "", "");
164   fmt_settings_set_style (s, FMT_PCT,    decimal,        0, "-",  "", "%", "");
165 }
166
167 /* Returns an input format specification with type TYPE, width W,
168    and D decimals. */
169 struct fmt_spec
170 fmt_for_input (enum fmt_type type, int w, int d)
171 {
172   struct fmt_spec f;
173   f.type = type;
174   f.w = w;
175   f.d = d;
176   assert (fmt_check_input (&f));
177   return f;
178 }
179
180 /* Returns an output format specification with type TYPE, width
181    W, and D decimals. */
182 struct fmt_spec
183 fmt_for_output (enum fmt_type type, int w, int d)
184 {
185   struct fmt_spec f;
186   f.type = type;
187   f.w = w;
188   f.d = d;
189   assert (fmt_check_output (&f));
190   return f;
191 }
192
193 /* Returns the output format specifier corresponding to input
194    format specifier INPUT. */
195 struct fmt_spec
196 fmt_for_output_from_input (const struct fmt_spec *input)
197 {
198   struct fmt_spec output;
199
200   assert (fmt_check_input (input));
201
202   output.type = fmt_input_to_output (input->type);
203   output.w = input->w;
204   if (output.w > fmt_max_output_width (output.type))
205     output.w = fmt_max_output_width (output.type);
206   else if (output.w < fmt_min_output_width (output.type))
207     output.w = fmt_min_output_width (output.type);
208   output.d = input->d;
209
210   switch (input->type)
211     {
212     case FMT_Z:
213       output.w++;
214       if (output.d > 0)
215         output.w++;
216       break;
217
218     case FMT_F:
219     case FMT_COMMA:
220     case FMT_DOT:
221     case FMT_DOLLAR:
222     case FMT_PCT:
223       {
224         const struct fmt_number_style *style =
225           settings_get_style (input->type);
226
227         output.w += fmt_affix_width (style);
228         if (style->grouping != 0 && input->w - input->d >= 3)
229           output.w += (input->w - input->d - 1) / 3;
230         if (output.d > 0)
231           output.w++;
232       }
233       break;
234
235     case FMT_N:
236       if (output.d > 0)
237         output.w++;
238       break;
239
240     case FMT_E:
241       output.d = MAX (input->d, 3);
242       output.w = MAX (input->w, output.d + 7);
243       break;
244
245     case FMT_PIBHEX:
246       output.w = max_digits_for_bytes (input->w / 2) + 1;
247       break;
248
249     case FMT_RB:
250     case FMT_RBHEX:
251       output.w = 8;
252       output.d = 2;
253       break;
254
255     case FMT_P:
256     case FMT_PK:
257       output.w = 2 * input->w + (input->d > 0);
258       break;
259
260     case FMT_IB:
261     case FMT_PIB:
262       output.w = max_digits_for_bytes (input->w) + 1;
263       if (output.d > 0)
264         output.w++;
265       break;
266
267     case FMT_CCA:
268     case FMT_CCB:
269     case FMT_CCC:
270     case FMT_CCD:
271     case FMT_CCE:
272       NOT_REACHED ();
273
274     case FMT_A:
275       break;
276
277     case FMT_AHEX:
278       output.w = input->w / 2;
279       break;
280
281     case FMT_DATE:
282     case FMT_EDATE:
283     case FMT_SDATE:
284     case FMT_ADATE:
285     case FMT_JDATE:
286     case FMT_QYR:
287     case FMT_MOYR:
288     case FMT_WKYR:
289     case FMT_TIME:
290     case FMT_DTIME:
291     case FMT_DATETIME:
292     case FMT_WKDAY:
293     case FMT_MONTH:
294       break;
295
296     case FMT_MTIME:
297       if (input->d)
298         output.w = MAX (input->w, input->d + 6);
299       break;
300
301     case FMT_YMDHMS:
302       if (input->w)
303         output.w = MAX (input->w, input->d + 20);
304       break;
305
306     default:
307       NOT_REACHED ();
308     }
309
310   if (output.w > fmt_max_output_width (output.type))
311     output.w = fmt_max_output_width (output.type);
312
313   assert (fmt_check_output (&output));
314   return output;
315 }
316
317 /* Returns the default format for the given WIDTH: F8.2 format
318    for a numeric value, A format for a string value. */
319 struct fmt_spec
320 fmt_default_for_width (int width)
321 {
322   return (width == 0
323           ? fmt_for_output (FMT_F, 8, 2)
324           : fmt_for_output (FMT_A, width, 0));
325 }
326
327 /* Checks whether SPEC is valid for USE and returns nonzero if so.
328    Otherwise, emits an error message and returns zero. */
329 bool
330 fmt_check (const struct fmt_spec *spec, enum fmt_use use)
331 {
332   const char *io_fmt;
333   char str[FMT_STRING_LEN_MAX + 1];
334   int min_w, max_w, max_d;
335
336   assert (is_fmt_type (spec->type));
337   fmt_to_string (spec, str);
338
339   io_fmt = use == FMT_FOR_INPUT ? _("Input format") : _("Output format");
340   if (use == FMT_FOR_INPUT && !fmt_usable_for_input (spec->type))
341     {
342       msg (SE, _("Format %s may not be used for input."), str);
343       return false;
344     }
345
346   if (spec->w % fmt_step_width (spec->type))
347     {
348       assert (fmt_step_width (spec->type) == 2);
349       msg (SE, _("%s specifies width %d, but %s requires an even width."),
350            str, spec->w, fmt_name (spec->type));
351       return false;
352     }
353
354   min_w = fmt_min_width (spec->type, use);
355   max_w = fmt_max_width (spec->type, use);
356   if (spec->w < min_w || spec->w > max_w)
357     {
358       msg (SE, _("%s %s specifies width %d, but "
359                  "%s requires a width between %d and %d."),
360            io_fmt, str, spec->w, fmt_name (spec->type), min_w, max_w);
361       return false;
362     }
363
364   max_d = fmt_max_decimals (spec->type, spec->w, use);
365   if (!fmt_takes_decimals (spec->type) && spec->d != 0)
366     {
367       msg (SE, ngettext ("%s %s specifies %d decimal place, but "
368                          "%s does not allow any decimals.",
369                          "%s %s specifies %d decimal places, but "
370                          "%s does not allow any decimals.",
371                          spec->d),
372            io_fmt, str, spec->d, fmt_name (spec->type));
373       return false;
374     }
375   else if (spec->d > max_d)
376     {
377       if (max_d > 0)
378         msg (SE, ngettext ("%s %s specifies %d decimal place, but "
379                            "the given width allows at most %d decimals.",
380                            "%s %s specifies %d decimal places, but "
381                            "the given width allows at most %d decimals.",
382                            spec->d),
383              io_fmt, str, spec->d, max_d);
384       else
385         msg (SE, ngettext ("%s %s specifies %d decimal place, but "
386                            "the given width does not allow for any decimals.",
387                            "%s %s specifies %d decimal places, but "
388                            "the given width does not allow for any decimals.",
389                            spec->d),
390              io_fmt, str, spec->d);
391       return false;
392     }
393
394   return true;
395 }
396
397 /* Checks whether SPEC is valid as an input format and returns
398    nonzero if so.  Otherwise, emits an error message and returns
399    zero. */
400 bool
401 fmt_check_input (const struct fmt_spec *spec)
402 {
403   return fmt_check (spec, FMT_FOR_INPUT);
404 }
405
406 /* Checks whether SPEC is valid as an output format and returns
407    true if so.  Otherwise, emits an error message and returns false. */
408 bool
409 fmt_check_output (const struct fmt_spec *spec)
410 {
411   return fmt_check (spec, FMT_FOR_OUTPUT);
412 }
413
414 /* Checks that FORMAT is appropriate for a variable of the given
415    VAR_TYPE and returns true if so.  Otherwise returns false and
416    emits an error message. */
417 bool
418 fmt_check_type_compat (const struct fmt_spec *format, enum val_type var_type)
419 {
420   assert (val_type_is_valid (var_type));
421   if ((var_type == VAL_STRING) != (fmt_is_string (format->type) != 0))
422     {
423       char str[FMT_STRING_LEN_MAX + 1];
424       msg (SE, _("%s variables are not compatible with %s format %s."),
425            var_type == VAL_STRING ? _("String") : _("Numeric"),
426            var_type == VAL_STRING ? _("numeric") : _("string"),
427            fmt_to_string (format, str));
428       return false;
429     }
430   return true;
431 }
432
433 /* Checks that FORMAT is appropriate for a variable of the given
434    WIDTH and returns true if so.  Otherwise returns false and
435    emits an error message. */
436 bool
437 fmt_check_width_compat (const struct fmt_spec *format, int width)
438 {
439   if (!fmt_check_type_compat (format, val_type_from_width (width)))
440     return false;
441   if (fmt_var_width (format) != width)
442     {
443       char str[FMT_STRING_LEN_MAX + 1];
444       msg (SE, _("String variable with width %d is not compatible with "
445                  "format %s."),
446            width, fmt_to_string (format, str));
447       return false;
448     }
449   return true;
450 }
451
452 /* Returns the width corresponding to FORMAT.  The return value
453    is the width of the `union value's required by FORMAT. */
454 int
455 fmt_var_width (const struct fmt_spec *format)
456 {
457   return (format->type == FMT_AHEX ? format->w / 2
458           : format->type == FMT_A ? format->w
459           : 0);
460 }
461
462 /* Converts F to its string representation (for instance, "F8.2")
463    in BUFFER.  Returns BUFFER.
464
465    If F has decimals, they are included in the output string,
466    even if F's format type does not allow decimals, to allow
467    accurately presenting incorrect formats to the user. */
468 char *
469 fmt_to_string (const struct fmt_spec *f, char buffer[FMT_STRING_LEN_MAX + 1])
470 {
471   if (fmt_takes_decimals (f->type) || f->d > 0)
472     snprintf (buffer, FMT_STRING_LEN_MAX + 1,
473               "%s%d.%d", fmt_name (f->type), f->w, f->d);
474   else
475     snprintf (buffer, FMT_STRING_LEN_MAX + 1,
476               "%s%d", fmt_name (f->type), f->w);
477   return buffer;
478 }
479
480 /* Returns true if A and B are identical formats,
481    false otherwise. */
482 bool
483 fmt_equal (const struct fmt_spec *a, const struct fmt_spec *b)
484 {
485   return a->type == b->type && a->w == b->w && a->d == b->d;
486 }
487
488 /* Adjusts FMT to be valid for a value of the given WIDTH if necessary.
489    If nothing needed to be changed the return value is false
490  */
491 bool
492 fmt_resize (struct fmt_spec *fmt, int width)
493 {
494   if ((width > 0) != fmt_is_string (fmt->type))
495     {
496       /* Changed from numeric to string or vice versa.  Set to
497          default format for new width. */
498       *fmt = fmt_default_for_width (width);
499     }
500   else if (width > 0)
501     {
502       /* Changed width of string.  Preserve format type, adjust
503          width. */
504       fmt->w = fmt->type == FMT_AHEX ? width * 2 : width;
505     }
506   else
507     {
508       /* Still numeric. */
509       return false;
510     }
511   return true;
512 }
513
514 /* Adjusts FMT's width and decimal places to be valid for USE.  */
515 void
516 fmt_fix (struct fmt_spec *fmt, enum fmt_use use)
517 {
518   /* Clamp width to those allowed by format. */
519   fmt_clamp_width (fmt, use);
520
521   /* If FMT has more decimal places than allowed, attempt to increase FMT's
522      width until that number of decimal places can be achieved. */
523   if (fmt->d > fmt_max_decimals (fmt->type, fmt->w, use)
524       && fmt_takes_decimals (fmt->type))
525     {
526       int max_w = fmt_max_width (fmt->type, use);
527       for (; fmt->w < max_w; fmt->w++)
528         if (fmt->d <= fmt_max_decimals (fmt->type, fmt->w, use))
529           break;
530     }
531
532   /* Clamp decimals to those allowed by format and width. */
533   fmt_clamp_decimals (fmt, use);
534 }
535
536 /* Adjusts FMT's width and decimal places to be valid for an
537    input format.  */
538 void
539 fmt_fix_input (struct fmt_spec *fmt)
540 {
541   fmt_fix (fmt, FMT_FOR_INPUT);
542 }
543
544 /* Adjusts FMT's width and decimal places to be valid for an
545    output format.  */
546 void
547 fmt_fix_output (struct fmt_spec *fmt)
548 {
549   fmt_fix (fmt, FMT_FOR_OUTPUT);
550 }
551
552 /* Sets FMT's width to WIDTH (or the nearest width allowed by FMT's type) and
553    reduces its decimal places as necessary (if necessary) for that width.  */
554 void
555 fmt_change_width (struct fmt_spec *fmt, int width, enum fmt_use use)
556 {
557   fmt->w = width;
558   fmt_clamp_width (fmt, use);
559   fmt_clamp_decimals (fmt, use);
560 }
561
562 /* Sets FMT's decimal places to DECIMALS (or the nearest number of decimal
563    places allowed by FMT's type) and increases its width as necessary (if
564    necessary) for that number of decimal places.  */
565 void
566 fmt_change_decimals (struct fmt_spec *fmt, int decimals, enum fmt_use use)
567 {
568   fmt->d = decimals;
569   fmt_fix (fmt, use);
570 }
571 \f
572 /* Describes a display format. */
573 struct fmt_desc
574   {
575     char name[9];
576     int min_input_width, min_output_width;
577     int io;
578     enum fmt_category category;
579   };
580
581 static const struct fmt_desc *get_fmt_desc (enum fmt_type type);
582
583 /* Returns the name of the given format TYPE. */
584 const char *
585 fmt_name (enum fmt_type type)
586 {
587   return get_fmt_desc (type)->name;
588 }
589
590 /* Tries to parse NAME as a format type.
591    If successful, stores the type in *TYPE and returns true.
592    On failure, returns false. */
593 bool
594 fmt_from_name (const char *name, enum fmt_type *type)
595 {
596   int i;
597
598   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
599     if (!c_strcasecmp (name, get_fmt_desc (i)->name))
600       {
601         *type = i;
602         return true;
603       }
604   return false;
605 }
606
607 /* Returns true if TYPE accepts decimal places,
608    false otherwise. */
609 bool
610 fmt_takes_decimals (enum fmt_type type)
611 {
612   return fmt_max_output_decimals (type, fmt_max_output_width (type)) > 0;
613 }
614
615 /* Returns the minimum width of the given format TYPE for the given USE. */
616 int
617 fmt_min_width (enum fmt_type type, enum fmt_use use)
618 {
619   return (use == FMT_FOR_INPUT
620           ? fmt_min_input_width (type)
621           : fmt_min_output_width (type));
622 }
623
624 /* Returns the maximum width of the given format TYPE,
625    for input if FOR_INPUT is true,
626    for output otherwise. */
627 int
628 fmt_max_width (enum fmt_type type, enum fmt_use use UNUSED)
629 {
630   /* Maximum width is actually invariant of whether the format is
631      for input or output, so FOR_INPUT is unused. */
632   assert (is_fmt_type (type));
633   switch (type)
634     {
635     case FMT_P:
636     case FMT_PK:
637     case FMT_PIBHEX:
638     case FMT_RBHEX:
639       return 16;
640
641     case FMT_IB:
642     case FMT_PIB:
643     case FMT_RB:
644       return 8;
645
646     case FMT_A:
647       return MAX_STRING;
648
649     case FMT_AHEX:
650       return 2 * MAX_STRING;
651
652     default:
653       return 40;
654     }
655 }
656
657 /* Returns the maximum number of decimal places allowed for the
658    given format TYPE with a width of WIDTH places, for the given USE. */
659 int
660 fmt_max_decimals (enum fmt_type type, int width, enum fmt_use use)
661 {
662   int max_d;
663
664   switch (type)
665     {
666     case FMT_F:
667     case FMT_COMMA:
668     case FMT_DOT:
669       max_d = use == FMT_FOR_INPUT ? width : width - 1;
670       break;
671
672     case FMT_DOLLAR:
673     case FMT_PCT:
674       max_d = use == FMT_FOR_INPUT ? width : width - 2;
675       break;
676
677     case FMT_E:
678       max_d = use == FMT_FOR_INPUT ? width : width - 7;
679       break;
680
681     case FMT_CCA:
682     case FMT_CCB:
683     case FMT_CCC:
684     case FMT_CCD:
685     case FMT_CCE:
686       assert (use == FMT_FOR_OUTPUT);
687       max_d = width - 1;
688       break;
689
690     case FMT_N:
691     case FMT_Z:
692       max_d = width;
693       break;
694
695     case FMT_P:
696       max_d = width * 2 - 1;
697       break;
698
699     case FMT_PK:
700       max_d = width * 2;
701       break;
702
703     case FMT_IB:
704     case FMT_PIB:
705       max_d = max_digits_for_bytes (width);
706       break;
707
708     case FMT_PIBHEX:
709       max_d = 0;
710       break;
711
712     case FMT_RB:
713     case FMT_RBHEX:
714       max_d = 16;
715       break;
716
717     case FMT_DATE:
718     case FMT_ADATE:
719     case FMT_EDATE:
720     case FMT_JDATE:
721     case FMT_SDATE:
722     case FMT_QYR:
723     case FMT_MOYR:
724     case FMT_WKYR:
725       max_d = 0;
726       break;
727
728     case FMT_DATETIME:
729       max_d = width - 21;
730       break;
731
732     case FMT_YMDHMS:
733       max_d = width - 20;
734       break;
735
736     case FMT_MTIME:
737       max_d = width - 6;
738       break;
739
740     case FMT_TIME:
741       max_d = width - 9;
742       break;
743
744     case FMT_DTIME:
745       max_d = width - 12;
746       break;
747
748     case FMT_WKDAY:
749     case FMT_MONTH:
750     case FMT_A:
751     case FMT_AHEX:
752       max_d = 0;
753       break;
754
755     default:
756       NOT_REACHED ();
757     }
758
759   if (max_d < 0)
760     max_d = 0;
761   else if (max_d > 16)
762     max_d = 16;
763   return max_d;
764 }
765
766 /* Returns the minimum acceptable width for an input field
767    formatted with the given TYPE. */
768 int
769 fmt_min_input_width (enum fmt_type type)
770 {
771   return get_fmt_desc (type)->min_input_width;
772 }
773
774 /* Returns the maximum acceptable width for an input field
775    formatted with the given TYPE. */
776 int
777 fmt_max_input_width (enum fmt_type type)
778 {
779   return fmt_max_width (type, FMT_FOR_INPUT);
780 }
781
782 /* Returns the maximum number of decimal places allowed in an
783    input field of the given TYPE and WIDTH. */
784 int
785 fmt_max_input_decimals (enum fmt_type type, int width)
786 {
787   assert (valid_width (type, width, true));
788   return fmt_max_decimals (type, width, FMT_FOR_INPUT);
789 }
790
791 /* Returns the minimum acceptable width for an output field
792    formatted with the given TYPE. */
793 int
794 fmt_min_output_width (enum fmt_type type)
795 {
796   return get_fmt_desc (type)->min_output_width;
797 }
798
799 /* Returns the maximum acceptable width for an output field
800    formatted with the given TYPE. */
801 int
802 fmt_max_output_width (enum fmt_type type)
803 {
804   return fmt_max_width (type, FMT_FOR_OUTPUT);
805 }
806
807 /* Returns the maximum number of decimal places allowed in an
808    output field of the given TYPE and WIDTH. */
809 int
810 fmt_max_output_decimals (enum fmt_type type, int width)
811 {
812   assert (valid_width (type, width, false));
813   return fmt_max_decimals (type, width, FMT_FOR_OUTPUT);
814 }
815
816 /* Returns the width step for a field formatted with the given
817    TYPE.  Field width must be a multiple of the width step. */
818 int
819 fmt_step_width (enum fmt_type type)
820 {
821   return (fmt_get_category (type) == FMT_CAT_HEXADECIMAL || type == FMT_AHEX
822           ? 2 : 1);
823 }
824
825 /* Returns true if TYPE is used for string fields,
826    false if it is used for numeric fields. */
827 bool
828 fmt_is_string (enum fmt_type type)
829 {
830   return fmt_get_category (type) == FMT_CAT_STRING;
831 }
832
833 /* Returns true if TYPE is used for numeric fields,
834    false if it is used for string fields. */
835 bool
836 fmt_is_numeric (enum fmt_type type)
837 {
838   return !fmt_is_string (type);
839 }
840
841 /* Returns the format TYPE's category.
842    Each format type is in exactly one category,
843    and each category's value is bitwise disjoint from every other
844    category.  Thus, the return value may be tested for equality
845    or compared bitwise against a mask of FMT_CAT_* values. */
846 enum fmt_category
847 fmt_get_category (enum fmt_type type)
848 {
849   return get_fmt_desc (type)->category;
850 }
851
852 /* Returns the output format selected by default when TYPE is
853    used as an input format. */
854 enum fmt_type
855 fmt_input_to_output (enum fmt_type type)
856 {
857   switch (fmt_get_category (type))
858     {
859     case FMT_CAT_STRING:
860       return FMT_A;
861
862     case FMT_CAT_LEGACY:
863     case FMT_CAT_BINARY:
864     case FMT_CAT_HEXADECIMAL:
865       return FMT_F;
866
867     default:
868       return type;
869     }
870 }
871
872 /* Returns the SPSS format type corresponding to the given PSPP
873    format type. */
874 int
875 fmt_to_io (enum fmt_type type)
876 {
877   return get_fmt_desc (type)->io;
878 }
879
880 /* Determines the PSPP format corresponding to the given SPSS
881    format type.  If successful, sets *FMT_TYPE to the PSPP format
882    and returns true.  On failure, return false. */
883 bool
884 fmt_from_io (int io, enum fmt_type *fmt_type)
885 {
886   switch (io)
887     {
888 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY)     \
889     case IO:                                          \
890       *fmt_type = FMT_##NAME;                           \
891       return true;
892 #include "format.def"
893     default:
894       return false;
895     }
896 }
897
898 /* Returns true if TYPE may be used as an input format,
899    false otherwise. */
900 bool
901 fmt_usable_for_input (enum fmt_type type)
902 {
903   assert (is_fmt_type (type));
904   return fmt_get_category (type) != FMT_CAT_CUSTOM;
905 }
906
907 /* For time and date formats, returns a template used for input and output in a
908    field of the given WIDTH.
909
910    WIDTH only affects whether a 2-digit year or a 4-digit year is used, that
911    is, whether the returned string contains "yy" or "yyyy", and whether seconds
912    are include, that is, whether the returned string contains ":SS".  A caller
913    that doesn't care whether the returned string contains "yy" or "yyyy" or
914    ":SS" can just specify 0 to omit them. */
915 const char *
916 fmt_date_template (enum fmt_type type, int width)
917 {
918   const char *s1, *s2;
919
920   switch (type)
921     {
922     case FMT_DATE:
923       s1 = "dd-mmm-yy";
924       s2 = "dd-mmm-yyyy";
925       break;
926
927     case FMT_ADATE:
928       s1 = "mm/dd/yy";
929       s2 = "mm/dd/yyyy";
930       break;
931
932     case FMT_EDATE:
933       s1 = "dd.mm.yy";
934       s2 = "dd.mm.yyyy";
935       break;
936
937     case FMT_JDATE:
938       s1 = "yyddd";
939       s2 = "yyyyddd";
940       break;
941
942     case FMT_SDATE:
943       s1 = "yy/mm/dd";
944       s2 = "yyyy/mm/dd";
945       break;
946
947     case FMT_QYR:
948       s1 = "q Q yy";
949       s2 = "q Q yyyy";
950       break;
951
952     case FMT_MOYR:
953       s1 = "mmm yy";
954       s2 = "mmm yyyy";
955       break;
956
957     case FMT_WKYR:
958       s1 = "ww WK yy";
959       s2 = "ww WK yyyy";
960       break;
961
962     case FMT_DATETIME:
963       s1 = "dd-mmm-yyyy HH:MM";
964       s2 = "dd-mmm-yyyy HH:MM:SS";
965       break;
966
967     case FMT_YMDHMS:
968       s1 = "yyyy-mm-dd HH:MM";
969       s2 = "yyyy-mm-dd HH:MM:SS";
970       break;
971
972     case FMT_MTIME:
973       s1 = "MM";
974       s2 = "MM:SS";
975       break;
976
977     case FMT_TIME:
978       s1 = "H:MM";
979       s2 = "H:MM:SS";
980       break;
981
982     case FMT_DTIME:
983       s1 = "D HH:MM";
984       s2 = "D HH:MM:SS";
985       break;
986
987     default:
988       NOT_REACHED ();
989     }
990
991   return width >= strlen (s2) ? s2 : s1;
992 }
993
994 /* Returns a string representing the format TYPE for use in a GUI dialog. */
995 const char *
996 fmt_gui_name (enum fmt_type type)
997 {
998   switch (type)
999     {
1000     case FMT_F:
1001       return _("Numeric");
1002
1003     case FMT_COMMA:
1004       return _("Comma");
1005
1006     case FMT_DOT:
1007       return _("Dot");
1008
1009     case FMT_E:
1010       return _("Scientific");
1011
1012     case FMT_DATE:
1013     case FMT_EDATE:
1014     case FMT_SDATE:
1015     case FMT_ADATE:
1016     case FMT_JDATE:
1017     case FMT_QYR:
1018     case FMT_MOYR:
1019     case FMT_WKYR:
1020     case FMT_DATETIME:
1021     case FMT_YMDHMS:
1022     case FMT_MTIME:
1023     case FMT_TIME:
1024     case FMT_DTIME:
1025     case FMT_WKDAY:
1026     case FMT_MONTH:
1027       return _("Date");
1028
1029     case FMT_DOLLAR:
1030       return _("Dollar");
1031
1032     case FMT_CCA:
1033     case FMT_CCB:
1034     case FMT_CCC:
1035     case FMT_CCD:
1036     case FMT_CCE:
1037       return _("Custom");
1038
1039     case FMT_A:
1040       return _("String");
1041
1042     default:
1043       return fmt_name (type);
1044     }
1045 }
1046 \f
1047 /* Returns true if TYPE is a valid format type,
1048    false otherwise. */
1049 bool
1050 is_fmt_type (enum fmt_type type)
1051 {
1052   return type < FMT_NUMBER_OF_FORMATS;
1053 }
1054
1055 /* Returns true if WIDTH is a valid width for the given format
1056    TYPE, for the given USE. */
1057 static bool
1058 valid_width (enum fmt_type type, int width, enum fmt_use use)
1059 {
1060   return (width >= fmt_min_width (type, use)
1061           && width <= fmt_max_width (type, use));
1062 }
1063
1064 /* Returns the maximum number of decimal digits in an unsigned
1065    binary number that is BYTES bytes long. */
1066 static int
1067 max_digits_for_bytes (int bytes)
1068 {
1069   int map[8] = {3, 5, 8, 10, 13, 15, 17, 20};
1070   assert (bytes > 0 && bytes <= sizeof map / sizeof *map);
1071   return map[bytes - 1];
1072 }
1073
1074 /* Clamp FMT's width to the range and values allowed by FMT's type. */
1075 static void
1076 fmt_clamp_width (struct fmt_spec *fmt, enum fmt_use use)
1077 {
1078   unsigned int step;
1079   int min_w, max_w;
1080
1081   min_w = fmt_min_width (fmt->type, use);
1082   max_w = fmt_max_width (fmt->type, use);
1083   if (fmt->w < min_w)
1084     fmt->w = min_w;
1085   else if (fmt->w > max_w)
1086     fmt->w = max_w;
1087
1088   /* Round width to step. */
1089   step = fmt_step_width (fmt->type);
1090   fmt->w = ROUND_DOWN (fmt->w, step);
1091 }
1092
1093 /* Clamp FMT's decimal places to the range allowed by FMT's type and width. */
1094 static void
1095 fmt_clamp_decimals (struct fmt_spec *fmt, enum fmt_use use)
1096 {
1097   int max_d;
1098
1099   max_d = fmt_max_decimals (fmt->type, fmt->w, use);
1100   if (fmt->d < 0)
1101     fmt->d = 0;
1102   else if (fmt->d > max_d)
1103     fmt->d = max_d;
1104 }
1105 \f
1106 /* Sets AFFIX's string value to S, a UTF-8 encoded string. */
1107 static void
1108 fmt_affix_set (struct fmt_affix *affix, const char *s)
1109 {
1110   affix->s = s[0] == '\0' ? CONST_CAST (char *, "") : xstrdup (s);
1111   affix->width = u8_strwidth (CHAR_CAST (const uint8_t *, s), "UTF-8");
1112 }
1113
1114 /* Frees data in AFFIX. */
1115 static void
1116 fmt_affix_free (struct fmt_affix *affix)
1117 {
1118   if (affix->s[0])
1119     free (affix->s);
1120 }
1121
1122 static void
1123 fmt_number_style_init (struct fmt_number_style *style)
1124 {
1125   fmt_affix_set (&style->neg_prefix, "");
1126   fmt_affix_set (&style->prefix, "");
1127   fmt_affix_set (&style->suffix, "");
1128   fmt_affix_set (&style->neg_suffix, "");
1129   style->decimal = '.';
1130   style->grouping = 0;
1131 }
1132
1133 static void
1134 fmt_number_style_clone (struct fmt_number_style *new,
1135                         const struct fmt_number_style *old)
1136 {
1137   fmt_affix_set (&new->neg_prefix, old->neg_prefix.s);
1138   fmt_affix_set (&new->prefix, old->prefix.s);
1139   fmt_affix_set (&new->suffix, old->suffix.s);
1140   fmt_affix_set (&new->neg_suffix, old->neg_suffix.s);
1141   new->decimal = old->decimal;
1142   new->grouping = old->grouping;
1143   new->extra_bytes = old->extra_bytes;
1144 }
1145
1146 /* Destroys a struct fmt_number_style. */
1147 static void
1148 fmt_number_style_destroy (struct fmt_number_style *style)
1149 {
1150   if (style != NULL)
1151     {
1152       fmt_affix_free (&style->neg_prefix);
1153       fmt_affix_free (&style->prefix);
1154       fmt_affix_free (&style->suffix);
1155       fmt_affix_free (&style->neg_suffix);
1156     }
1157 }
1158
1159 /* Returns the total width of the standard prefix and suffix for STYLE, in
1160    display columns (e.g. as returned by u8_strwidth()). */
1161 int
1162 fmt_affix_width (const struct fmt_number_style *style)
1163 {
1164   return style->prefix.width + style->suffix.width;
1165 }
1166
1167 /* Returns the total width of the negative prefix and suffix for STYLE, in
1168    display columns (e.g. as returned by u8_strwidth()). */
1169 int
1170 fmt_neg_affix_width (const struct fmt_number_style *style)
1171 {
1172   return style->neg_prefix.width + style->neg_suffix.width;
1173 }
1174
1175 /* Returns the struct fmt_desc for the given format TYPE. */
1176 static const struct fmt_desc *
1177 get_fmt_desc (enum fmt_type type)
1178 {
1179   static const struct fmt_desc formats[FMT_NUMBER_OF_FORMATS] =
1180     {
1181 #define FMT(NAME, METHOD, IMIN, OMIN, IO, CATEGORY) \
1182         {#NAME, IMIN, OMIN, IO, CATEGORY},
1183 #include "format.def"
1184     };
1185
1186   assert (is_fmt_type (type));
1187   return &formats[type];
1188 }
1189
1190 const struct fmt_spec F_8_0 = {FMT_F, 8, 0};
1191 const struct fmt_spec F_8_2 = {FMT_F, 8, 2};
1192 const struct fmt_spec F_4_3 = {FMT_F, 4, 3};
1193 const struct fmt_spec F_5_1 = {FMT_F, 5, 1};