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