format: Introduce a new type, struct fmt_settings.
[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     /* 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_settings_create ();
155
156   settings_set_decimal_char (get_system_decimal ());
157 }
158
159 void
160 settings_done (void)
161 {
162   fmt_settings_destroy (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 AFFIX, using BUFFER for storage.  BUFFER must
593    have at least FMT_STYLE_AFFIX_MAX + 1 bytes of space.  Tokens are delimited
594    by GROUPING.  The token is truncated to at most FMT_STYLE_AFFIX_MAX bytes,
595    followed by a null terminator.  Returns the first character following the
596    token. */
597 static const char *
598 extract_cc_token (const char *in, int grouping, struct substring *affix,
599                   char buffer[FMT_STYLE_AFFIX_MAX + 1])
600 {
601   size_t ofs = 0;
602
603   for (; *in != '\0' && *in != grouping; in++)
604     {
605       if (*in == '\'' && in[1] == grouping)
606         in++;
607       if (ofs < FMT_STYLE_AFFIX_MAX)
608         buffer[ofs++] = *in;
609     }
610   *affix = ss_buffer (buffer, ofs);
611
612   if (*in == grouping)
613     in++;
614   return in;
615 }
616
617 /* Sets custom currency specifier CC having name CC_NAME ('A' through
618    'E') to correspond to the settings in CC_STRING. */
619 bool
620 settings_set_cc (const char *cc_string, enum fmt_type type)
621 {
622   char a[FMT_STYLE_AFFIX_MAX + 1];
623   char b[FMT_STYLE_AFFIX_MAX + 1];
624   char c[FMT_STYLE_AFFIX_MAX + 1];
625   char d[FMT_STYLE_AFFIX_MAX + 1];
626   struct fmt_number_style cc;
627
628   assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
629
630   /* Determine separators. */
631   if (!find_cc_separators (cc_string, &cc))
632     {
633       msg (SE, _("%s: Custom currency string `%s' does not contain "
634                  "exactly three periods or commas (or it contains both)."),
635            fmt_name (type), cc_string);
636       return false;
637     }
638
639   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.neg_prefix, a);
640   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.prefix, b);
641   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.suffix, c);
642   cc_string = extract_cc_token (cc_string, cc.grouping, &cc.neg_suffix, d);
643
644   fmt_settings_set_style (the_settings.styles, type, &cc);
645
646   return true;
647 }
648
649 /* Returns the decimal point character for TYPE. */
650 int
651 settings_get_decimal_char (enum fmt_type type)
652 {
653   return fmt_settings_get_style (the_settings.styles, type)->decimal;
654 }
655
656 void
657 settings_set_decimal_char (char decimal)
658 {
659   fmt_settings_set_decimal (the_settings.styles, decimal);
660 }
661
662 /* Returns the number formatting style associated with the given
663    format TYPE. */
664 const struct fmt_number_style *
665 settings_get_style (enum fmt_type type)
666 {
667   assert (is_fmt_type (type));
668   return fmt_settings_get_style (the_settings.styles, type);
669 }
670
671 /* Returns a string of the form "$#,###.##" according to FMT,
672    which must be of type FMT_DOLLAR.  The caller must free the
673    string. */
674 char *
675 settings_dollar_template (const struct fmt_spec *fmt)
676 {
677   struct string str = DS_EMPTY_INITIALIZER;
678   int c;
679   const struct fmt_number_style *fns ;
680
681   assert (fmt->type == FMT_DOLLAR);
682
683   fns = fmt_settings_get_style (the_settings.styles, fmt->type);
684
685   ds_put_char (&str, '$');
686   for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
687     {
688       ds_put_char (&str, '#');
689       if (--c % 4 == 0 && c > 0)
690         {
691           ds_put_char (&str, fns->grouping);
692           --c;
693         }
694     }
695   if (fmt->d > 0)
696     {
697       ds_put_char (&str, fns->decimal);
698       ds_put_char_multiple (&str, '#', fmt->d);
699     }
700
701   return ds_cstr (&str);
702 }
703
704 void
705 settings_set_output_routing (enum settings_output_type type,
706                              enum settings_output_devices devices)
707 {
708   assert (type < SETTINGS_N_OUTPUT_TYPES);
709   the_settings.output_routing[type] = devices;
710 }
711
712 enum settings_output_devices
713 settings_get_output_routing (enum settings_output_type type)
714 {
715   assert (type < SETTINGS_N_OUTPUT_TYPES);
716   return the_settings.output_routing[type] | SETTINGS_DEVICE_UNFILTERED;
717 }