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