format: Make format settings structure smaller and cheaper.
[pspp] / src / data / settings.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011, 2015 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 "data/settings.h"
20
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <time.h>
24
25 #include "data/case.h"
26 #include "data/format.h"
27 #include "data/value.h"
28 #include "libpspp/i18n.h"
29 #include "libpspp/integer-format.h"
30 #include "libpspp/message.h"
31
32 #include "gl/minmax.h"
33 #include "gl/xalloc.h"
34
35 #include "gettext.h"
36 #define _(msgid) gettext (msgid)
37
38 struct settings
39 {
40   /* Integer format used for IB and PIB input. */
41   enum integer_format input_integer_format;
42
43   /* Floating-point format used for RB and RBHEX input. */
44   enum float_format input_float_format;
45
46   /* Format of integers in output (SET WIB). */
47   enum integer_format output_integer_format;
48
49   /* Format of reals in output (SET WRB). */
50   enum float_format output_float_format;
51
52   int viewlength;
53   int viewwidth;
54   bool safer_mode;
55   bool include;
56   int epoch;
57   bool route_errors_to_terminal;
58   bool route_errors_to_listing;
59   bool scompress;
60   bool undefined;
61   double blanks;
62   int max_messages[MSG_N_SEVERITIES];
63   bool printback;
64   bool mprint;
65   int mxloops;
66   size_t workspace;
67   struct fmt_spec default_format;
68   bool testing_mode;
69   int fuzzbits;
70
71   int cmd_algorithm;
72   int global_algorithm;
73   int syntax;
74
75   struct fmt_settings styles;
76
77   enum settings_output_devices output_routing[SETTINGS_N_OUTPUT_TYPES];
78
79   enum settings_value_show show_values;
80   enum settings_value_show show_variables;
81 };
82
83 static struct settings the_settings = {
84   INTEGER_NATIVE,               /* input_integer_format */
85   FLOAT_NATIVE_DOUBLE,          /* input_float_format */
86   INTEGER_NATIVE,               /* output_integer_format */
87   FLOAT_NATIVE_DOUBLE,          /* output_float_format */
88   24,                           /* viewlength */
89   79,                           /* viewwidth */
90   false,                        /* safer_mode */
91   true,                         /* include */
92   -1,                           /* epoch */
93   true,                         /* route_errors_to_terminal */
94   true,                         /* route_errors_to_listing */
95   true,                         /* scompress */
96   true,                         /* undefined */
97   SYSMIS,                       /* blanks */
98
99   /* max_messages */
100   {
101     100,                        /* MSG_S_ERROR */
102     100,                        /* MSG_S_WARNING */
103     100                         /* MSG_S_NOTE */
104   },
105
106   true,                         /* printback */
107   true,                         /* mprint */
108   40,                           /* mxloops */
109   64L * 1024 * 1024,            /* workspace */
110   {FMT_F, 8, 2},                /* default_format */
111   false,                        /* testing_mode */
112   6,                            /* fuzzbits */
113   ENHANCED,                     /* cmd_algorithm */
114   ENHANCED,                     /* global_algorithm */
115   ENHANCED,                     /* syntax */
116   FMT_SETTINGS_INIT,            /* styles */
117
118   /* output_routing */
119   {SETTINGS_DEVICE_LISTING | SETTINGS_DEVICE_TERMINAL,
120    SETTINGS_DEVICE_LISTING | SETTINGS_DEVICE_TERMINAL,
121    0,
122    SETTINGS_DEVICE_LISTING | SETTINGS_DEVICE_TERMINAL},
123
124   SETTINGS_VALUE_SHOW_LABEL,
125   SETTINGS_VALUE_SHOW_LABEL
126 };
127
128 /* Initializes the settings module. */
129 void
130 settings_init (void)
131 {
132   settings_set_epoch (-1);
133   settings_set_decimal_char (get_system_decimal ());
134 }
135
136 /* Cleans up the settings module. */
137 void
138 settings_done (void)
139 {
140   settings_destroy (&the_settings);
141 }
142
143 static void
144 settings_copy (struct settings *dst, const struct settings *src)
145 {
146   *dst = *src;
147   fmt_settings_copy (&dst->styles, &src->styles);
148 }
149
150 /* Returns a copy of the current settings. */
151 struct settings *
152 settings_get (void)
153 {
154   struct settings *s = xmalloc (sizeof *s);
155   settings_copy (s, &the_settings);
156   return s;
157 }
158
159 /* Replaces the current settings by those in S.  The caller retains ownership
160    of S. */
161 void
162 settings_set (const struct settings *s)
163 {
164   settings_destroy (&the_settings);
165   settings_copy (&the_settings, s);
166 }
167
168 /* Destroys S. */
169 void
170 settings_destroy (struct settings *s)
171 {
172   if (s != NULL)
173     {
174       fmt_settings_uninit (&s->styles);
175       if (s != &the_settings)
176         free (s);
177     }
178 }
179
180 /* Returns the floating-point format used for RB and RBHEX
181    input. */
182 enum float_format
183 settings_get_input_float_format (void)
184 {
185   return the_settings.input_float_format;
186 }
187
188 /* Sets the floating-point format used for RB and RBHEX input to
189    FORMAT. */
190 void
191 settings_set_input_float_format (enum float_format format)
192 {
193   the_settings.input_float_format = format;
194 }
195
196 /* Returns the integer format used for IB and PIB input. */
197 enum integer_format
198 settings_get_input_integer_format (void)
199 {
200   return the_settings.input_integer_format;
201 }
202
203 /* Sets the integer format used for IB and PIB input to
204    FORMAT. */
205 void
206 settings_set_input_integer_format (enum integer_format format)
207 {
208   the_settings.input_integer_format = format;
209 }
210
211 /* Returns the current output integer format. */
212 enum integer_format
213 settings_get_output_integer_format (void)
214 {
215   return the_settings.output_integer_format;
216 }
217
218 /* Sets the output integer format to INTEGER_FORMAT. */
219 void
220 settings_set_output_integer_format (
221                            enum integer_format integer_format)
222 {
223   the_settings.output_integer_format = integer_format;
224 }
225
226 /* Returns the current output float format. */
227 enum float_format
228 settings_get_output_float_format (void)
229 {
230   return the_settings.output_float_format;
231 }
232
233 /* Sets the output float format to FLOAT_FORMAT. */
234 void
235 settings_set_output_float_format (enum float_format float_format)
236 {
237   the_settings.output_float_format = float_format;
238 }
239
240 /* Screen length in lines. */
241 int
242 settings_get_viewlength (void)
243 {
244   return the_settings.viewlength;
245 }
246
247 /* Sets the view length. */
248 void
249 settings_set_viewlength (int viewlength_)
250 {
251   the_settings.viewlength = viewlength_;
252 }
253
254 /* Screen width. */
255 int
256 settings_get_viewwidth(void)
257 {
258   return the_settings.viewwidth;
259 }
260
261 /* Sets the screen width. */
262 void
263 settings_set_viewwidth (int viewwidth_)
264 {
265   the_settings.viewwidth = viewwidth_;
266 }
267
268 /* Whether PSPP can erase and overwrite files. */
269 bool
270 settings_get_safer_mode (void)
271 {
272   return the_settings.safer_mode;
273 }
274
275 /* Set safer mode. */
276 void
277 settings_set_safer_mode (void)
278 {
279   the_settings.safer_mode = true;
280 }
281
282 /* If echo is on, whether commands from include files are echoed. */
283 bool
284 settings_get_include (void)
285 {
286   return the_settings.include;
287 }
288
289 /* Set include file echo. */
290 void
291 settings_set_include (bool include)
292 {
293   the_settings.include = include;
294 }
295
296 /* What year to use as the start of the epoch. */
297 int
298 settings_get_epoch (void)
299 {
300   assert (the_settings.epoch >= 0);
301
302   return the_settings.epoch;
303 }
304
305 /* Sets the year that starts the epoch. */
306 void
307 settings_set_epoch (int epoch)
308 {
309   if (epoch < 0)
310     {
311       time_t t = time (0);
312       struct tm *tm = localtime (&t);
313       epoch = (tm != NULL ? tm->tm_year + 1900 : 2000) - 69;
314     }
315
316   the_settings.epoch = epoch;
317   assert (the_settings.epoch >= 0);
318 }
319
320 /* Compress system files by default? */
321 bool
322 settings_get_scompression (void)
323 {
324   return the_settings.scompress;
325 }
326
327 /* Set system file default compression. */
328 void
329 settings_set_scompression (bool scompress)
330 {
331   the_settings.scompress = scompress;
332 }
333
334 /* Whether to warn on undefined values in numeric data. */
335 bool
336 settings_get_undefined (void)
337 {
338   return the_settings.undefined;
339 }
340
341 /* Set whether to warn on undefined values. */
342 void
343 settings_set_undefined (bool undefined)
344 {
345   the_settings.undefined = undefined;
346 }
347
348 /* The value that blank numeric fields are set to when read in. */
349 double
350 settings_get_blanks (void)
351 {
352   return the_settings.blanks;
353 }
354
355 /* Set the value that blank numeric fields are set to when read
356    in. */
357 void
358 settings_set_blanks (double blanks)
359 {
360   the_settings.blanks = blanks;
361 }
362
363 /* Returns the maximum number of messages to show of the given SEVERITY before
364    aborting.  (The value for MSG_S_WARNING is interpreted as maximum number of
365    warnings and errors combined.) */
366 int
367 settings_get_max_messages (enum msg_severity severity)
368 {
369   assert (severity < MSG_N_SEVERITIES);
370   return the_settings.max_messages[severity];
371 }
372
373 /* Sets the maximum number of messages to show of the given SEVERITY before
374    aborting to MAX.  (The value for MSG_S_WARNING is interpreted as maximum
375    number of warnings and errors combined.)  In addition, in the case of
376    warnings the special value of zero indicates that no warnings are to be
377    issued.
378 */
379 void
380 settings_set_max_messages (enum msg_severity severity, int max)
381 {
382   assert (severity < MSG_N_SEVERITIES);
383
384   if (severity == MSG_S_WARNING)
385     {
386       if (max == 0)
387         {
388           msg (MW,
389                _("MXWARNS set to zero.  No further warnings will be given even when potentially problematic situations are encountered."));
390           msg_ui_disable_warnings (true);
391         }
392       else if (the_settings.max_messages [MSG_S_WARNING] == 0)
393         {
394           msg_ui_disable_warnings (false);
395           the_settings.max_messages[MSG_S_WARNING] = max;
396           msg (MW, _("Warnings re-enabled. %d warnings will be issued before aborting syntax processing."), max);
397         }
398     }
399
400   the_settings.max_messages[severity] = max;
401 }
402
403 /* Independent of get_printback, controls whether the commands
404    generated by macro invocations are displayed. */
405 bool
406 settings_get_mprint (void)
407 {
408   return the_settings.mprint;
409 }
410
411 /* Sets whether the commands generated by macro invocations are
412    displayed. */
413 void
414 settings_set_mprint (bool mprint)
415 {
416   the_settings.mprint = mprint;
417 }
418
419 /* Implied limit of unbounded loop. */
420 int
421 settings_get_mxloops (void)
422 {
423   return the_settings.mxloops;
424 }
425
426 /* Set implied limit of unbounded loop. */
427 void
428 settings_set_mxloops (int mxloops)
429 {
430   the_settings.mxloops = mxloops;
431 }
432
433 /* Approximate maximum amount of memory to use for cases, in
434    bytes. */
435 size_t
436 settings_get_workspace (void)
437 {
438   return the_settings.workspace;
439 }
440
441 /* Approximate maximum number of cases to allocate in-core, given
442    that each case has the format given in PROTO. */
443 size_t
444 settings_get_workspace_cases (const struct caseproto *proto)
445 {
446   size_t n_cases = settings_get_workspace () / case_get_cost (proto);
447   return MAX (n_cases, 4);
448 }
449
450 /* Set approximate maximum amount of memory to use for cases, in
451    bytes. */
452
453 void
454 settings_set_workspace (size_t workspace)
455 {
456   the_settings.workspace = workspace;
457 }
458
459 /* Default format for variables created by transformations and by
460    DATA LIST {FREE,LIST}. */
461 const struct fmt_spec *
462 settings_get_format (void)
463 {
464   return &the_settings.default_format;
465 }
466
467 /* Set default format for variables created by transformations
468    and by DATA LIST {FREE,LIST}. */
469 void
470 settings_set_format (const struct fmt_spec *default_format)
471 {
472   the_settings.default_format = *default_format;
473 }
474
475 /* Are we in testing mode?  (e.g. --testing-mode command line
476    option) */
477 bool
478 settings_get_testing_mode (void)
479 {
480   return the_settings.testing_mode;
481 }
482
483 /* Set testing mode. */
484 void
485 settings_set_testing_mode (bool testing_mode)
486 {
487   the_settings.testing_mode = testing_mode;
488 }
489
490 int
491 settings_get_fuzzbits (void)
492 {
493   return the_settings.fuzzbits;
494 }
495
496 void
497 settings_set_fuzzbits (int fuzzbits)
498 {
499   the_settings.fuzzbits = fuzzbits;
500 }
501
502 /* Return the current algorithm setting */
503 enum behavior_mode
504 settings_get_algorithm (void)
505 {
506   return the_settings.cmd_algorithm;
507 }
508
509 /* Set the algorithm option globally. */
510 void
511 settings_set_algorithm (enum behavior_mode mode)
512 {
513   the_settings.global_algorithm = the_settings.cmd_algorithm = mode;
514 }
515
516 /* Set the algorithm option for this command only */
517 void
518 settings_set_cmd_algorithm (enum behavior_mode mode)
519 {
520   the_settings.cmd_algorithm = mode;
521 }
522
523 /* Unset the algorithm option for this command */
524 void
525 unset_cmd_algorithm (void)
526 {
527   the_settings.cmd_algorithm = the_settings.global_algorithm;
528 }
529
530 /* Get the current syntax setting */
531 enum behavior_mode
532 settings_get_syntax (void)
533 {
534   return the_settings.syntax;
535 }
536
537 /* Set the syntax option */
538 void
539 settings_set_syntax (enum behavior_mode mode)
540 {
541   the_settings.syntax = mode;
542 }
543
544 \f
545 /* Sets custom currency specifier CC having name CC_NAME ('A' through
546    'E') to correspond to the settings in CC_STRING. */
547 bool
548 settings_set_cc (const char *cc_string, enum fmt_type type)
549 {
550   struct fmt_number_style *style = fmt_number_style_from_string (cc_string);
551   if (!style)
552     {
553       msg (SE, _("%s: Custom currency string `%s' does not contain "
554                  "exactly three periods or commas (or it contains both)."),
555            fmt_name (type), cc_string);
556       return false;
557     }
558
559   fmt_settings_set_cc (&the_settings.styles, type, style);
560   return true;
561 }
562
563 /* Returns the decimal point character for TYPE. */
564 int
565 settings_get_decimal_char (enum fmt_type type)
566 {
567   return fmt_settings_get_style (&the_settings.styles, type)->decimal;
568 }
569
570 void
571 settings_set_decimal_char (char decimal)
572 {
573   the_settings.styles.decimal = decimal;
574 }
575
576 /* Returns the number formatting style associated with the given
577    format TYPE. */
578 const struct fmt_number_style *
579 settings_get_style (enum fmt_type type)
580 {
581   assert (is_fmt_type (type));
582   return fmt_settings_get_style (&the_settings.styles, type);
583 }
584
585 /* Returns a string of the form "$#,###.##" according to FMT,
586    which must be of type FMT_DOLLAR.  The caller must free the
587    string. */
588 char *
589 settings_dollar_template (const struct fmt_spec *fmt)
590 {
591   struct string str = DS_EMPTY_INITIALIZER;
592   int c;
593   const struct fmt_number_style *fns ;
594
595   assert (fmt->type == FMT_DOLLAR);
596
597   fns = fmt_settings_get_style (&the_settings.styles, fmt->type);
598
599   ds_put_byte (&str, '$');
600   for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0;)
601     {
602       ds_put_byte (&str, '#');
603       if (--c % 4 == 0 && c > 0)
604         {
605           ds_put_byte (&str, fns->grouping);
606           --c;
607         }
608     }
609   if (fmt->d > 0)
610     {
611       ds_put_byte (&str, fns->decimal);
612       ds_put_byte_multiple (&str, '#', fmt->d);
613     }
614
615   return ds_cstr (&str);
616 }
617
618 void
619 settings_set_output_routing (enum settings_output_type type,
620                              enum settings_output_devices devices)
621 {
622   assert (type < SETTINGS_N_OUTPUT_TYPES);
623   the_settings.output_routing[type] = devices;
624 }
625
626 enum settings_output_devices
627 settings_get_output_routing (enum settings_output_type type)
628 {
629   assert (type < SETTINGS_N_OUTPUT_TYPES);
630   return the_settings.output_routing[type] | SETTINGS_DEVICE_UNFILTERED;
631 }
632
633 enum settings_value_show
634 settings_get_show_values (void)
635 {
636   return the_settings.show_values;
637 }
638
639 void
640 settings_set_show_values (enum settings_value_show s)
641 {
642   the_settings.show_values = s;
643 }
644
645
646 enum settings_value_show
647 settings_get_show_variables (void)
648 {
649   return the_settings.show_variables;
650 }
651
652 void
653 settings_set_show_variables (enum settings_value_show s)
654 {
655   the_settings.show_variables = s;
656 }