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