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