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