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