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