ec026d4cbe7d883a9a1647492bc151f6e998de4f
[pspp-builds.git] / src / data / settings.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010 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 set CC's
530    grouping and decimal members appropriately.  Returns true if
531    successful, false otherwise. */
532 static bool
533 find_cc_separators (const char *cc_string, struct fmt_number_style *cc)
534 {
535   const char *sp;
536   int comma_cnt, dot_cnt;
537
538   /* Count commas and periods.  There must be exactly three of
539      one or the other, except that an apostrophe escapes a
540      following comma or period. */
541   comma_cnt = dot_cnt = 0;
542   for (sp = cc_string; *sp; sp++)
543     if (*sp == ',')
544       comma_cnt++;
545     else if (*sp == '.')
546       dot_cnt++;
547     else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
548       sp++;
549
550   if ((comma_cnt == 3) == (dot_cnt == 3))
551     return false;
552
553   if (comma_cnt == 3)
554     {
555       cc->decimal = '.';
556       cc->grouping = ',';
557     }
558   else
559     {
560       cc->decimal = ',';
561       cc->grouping = '.';
562     }
563   return true;
564 }
565
566 /* Extracts a token from IN into AFFIX, using BUFFER for storage.  BUFFER must
567    have at least FMT_STYLE_AFFIX_MAX + 1 bytes of space.  Tokens are delimited
568    by GROUPING.  The token is truncated to at most FMT_STYLE_AFFIX_MAX bytes,
569    followed by a null terminator.  Returns the first character following the
570    token. */
571 static const char *
572 extract_cc_token (const char *in, int grouping, struct substring *affix,
573                   char buffer[FMT_STYLE_AFFIX_MAX + 1])
574 {
575   size_t ofs = 0;
576
577   for (; *in != '\0' && *in != grouping; in++)
578     {
579       if (*in == '\'' && in[1] == grouping)
580         in++;
581       if (ofs < FMT_STYLE_AFFIX_MAX)
582         buffer[ofs++] = *in;
583     }
584   *affix = ss_buffer (buffer, ofs);
585
586   if (*in == grouping)
587     in++;
588   return in;
589 }
590
591 /* Sets custom currency specifier CC having name CC_NAME ('A' through
592    'E') to correspond to the settings in CC_STRING. */
593 bool
594 settings_set_cc (const char *cc_string, enum fmt_type type)
595 {
596   char a[FMT_STYLE_AFFIX_MAX + 1];
597   char b[FMT_STYLE_AFFIX_MAX + 1];
598   char c[FMT_STYLE_AFFIX_MAX + 1];
599   char d[FMT_STYLE_AFFIX_MAX + 1];
600   struct fmt_number_style cc;
601
602   assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
603
604   /* Determine separators. */
605   if (!find_cc_separators (cc_string, &cc))
606     {
607       msg (SE, _("%s: Custom currency string `%s' does not contain "
608                  "exactly three periods or commas (or it contains both)."),
609            fmt_name (type), cc_string);
610       return false;
611     }
612
613   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.neg_prefix, a);
614   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.prefix, b);
615   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.suffix, c);
616   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.neg_suffix, d);
617
618   fmt_settings_set_style (the_settings.styles, type, &cc);
619
620   return true;
621 }
622
623 /* Returns the decimal point character for TYPE. */
624 int
625 settings_get_decimal_char (enum fmt_type type)
626 {
627   return fmt_settings_get_style (the_settings.styles, type)->decimal;
628 }
629
630 void
631 settings_set_decimal_char (char decimal)
632 {
633   fmt_settings_set_decimal (the_settings.styles, decimal);
634 }
635
636 /* Returns the number formatting style associated with the given
637    format TYPE. */
638 const struct fmt_number_style *
639 settings_get_style (enum fmt_type type)
640 {
641   assert (is_fmt_type (type));
642   return fmt_settings_get_style (the_settings.styles, type);
643 }
644
645 /* Returns a string of the form "$#,###.##" according to FMT,
646    which must be of type FMT_DOLLAR.  The caller must free the
647    string. */
648 char *
649 settings_dollar_template (const struct fmt_spec *fmt)
650 {
651   struct string str = DS_EMPTY_INITIALIZER;
652   int c;
653   const struct fmt_number_style *fns ;
654
655   assert (fmt->type == FMT_DOLLAR);
656
657   fns = fmt_settings_get_style (the_settings.styles, fmt->type);
658
659   ds_put_byte (&str, '$');
660   for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
661     {
662       ds_put_byte (&str, '#');
663       if (--c % 4 == 0 && c > 0)
664         {
665           ds_put_byte (&str, fns->grouping);
666           --c;
667         }
668     }
669   if (fmt->d > 0)
670     {
671       ds_put_byte (&str, fns->decimal);
672       ds_put_byte_multiple (&str, '#', fmt->d);
673     }
674
675   return ds_cstr (&str);
676 }
677
678 void
679 settings_set_output_routing (enum settings_output_type type,
680                              enum settings_output_devices devices)
681 {
682   assert (type < SETTINGS_N_OUTPUT_TYPES);
683   the_settings.output_routing[type] = devices;
684 }
685
686 enum settings_output_devices
687 settings_get_output_routing (enum settings_output_type type)
688 {
689   assert (type < SETTINGS_N_OUTPUT_TYPES);
690   return the_settings.output_routing[type] | SETTINGS_DEVICE_UNFILTERED;
691 }