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