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