d95e3a14076a140d848d8ccad45c73e767592d47
[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.) */
368 void
369 settings_set_max_messages (enum msg_severity severity, int max)
370 {
371   assert (severity < MSG_N_SEVERITIES);
372   the_settings.max_messages[severity] = max;
373 }
374
375 /* Independent of get_printback, controls whether the commands
376    generated by macro invocations are displayed. */
377 bool
378 settings_get_mprint (void)
379 {
380   return the_settings.mprint;
381 }
382
383 /* Sets whether the commands generated by macro invocations are
384    displayed. */
385 void
386 settings_set_mprint ( bool mprint)
387 {
388   the_settings.mprint = mprint;
389 }
390
391 /* Implied limit of unbounded loop. */
392 int
393 settings_get_mxloops (void)
394 {
395   return the_settings.mxloops;
396 }
397
398 /* Set implied limit of unbounded loop. */
399 void
400 settings_set_mxloops ( int mxloops)
401 {
402   the_settings.mxloops = mxloops;
403 }
404
405 /* Whether a blank line is a command terminator. */
406 bool
407 settings_get_nulline (void)
408 {
409   return the_settings.nulline;
410 }
411
412 /* Set whether a blank line is a command terminator. */
413 void
414 settings_set_nulline ( bool nulline)
415 {
416   the_settings.nulline = nulline;
417 }
418
419 /* The character used to terminate commands. */
420 char
421 settings_get_endcmd (void)
422 {
423   return the_settings.endcmd;
424 }
425
426 /* Set the character used to terminate commands. */
427 void
428 settings_set_endcmd ( char endcmd)
429 {
430   the_settings.endcmd = endcmd;
431 }
432
433 /* Approximate maximum amount of memory to use for cases, in
434    bytes. */
435 size_t
436 settings_get_workspace (void)
437 {
438   return the_settings.workspace;
439 }
440
441 /* Approximate maximum number of cases to allocate in-core, given
442    that each case has the format given in PROTO. */
443 size_t
444 settings_get_workspace_cases (const struct caseproto *proto)
445 {
446   size_t n_cases = settings_get_workspace () / case_get_cost (proto);
447   return MAX (n_cases, 4);
448 }
449
450 /* Set approximate maximum amount of memory to use for cases, in
451    bytes. */
452
453 void
454 settings_set_workspace ( size_t workspace)
455 {
456   the_settings.workspace = workspace;
457 }
458
459 /* Default format for variables created by transformations and by
460    DATA LIST {FREE,LIST}. */
461 const struct fmt_spec *
462 settings_get_format (void)
463 {
464   return &the_settings.default_format;
465 }
466
467 /* Set default format for variables created by transformations
468    and by DATA LIST {FREE,LIST}. */
469 void
470 settings_set_format ( const struct fmt_spec *default_format)
471 {
472   the_settings.default_format = *default_format;
473 }
474
475 /* Are we in testing mode?  (e.g. --testing-mode command line
476    option) */
477 bool
478 settings_get_testing_mode (void)
479 {
480   return the_settings.testing_mode;
481 }
482
483 /* Set testing mode. */
484 void
485 settings_set_testing_mode ( bool testing_mode)
486 {
487   the_settings.testing_mode = testing_mode;
488 }
489
490 /* Return the current algorithm setting */
491 enum behavior_mode
492 settings_get_algorithm (void)
493 {
494   return *the_settings.algorithm;
495 }
496
497 /* Set the algorithm option globally. */
498 void
499 settings_set_algorithm (enum behavior_mode mode)
500 {
501   global_algorithm = mode;
502 }
503
504 /* Set the algorithm option for this command only */
505 void
506 settings_set_cmd_algorithm ( enum behavior_mode mode)
507 {
508   the_settings.cmd_algorithm = mode;
509   the_settings.algorithm = &the_settings.cmd_algorithm;
510 }
511
512 /* Unset the algorithm option for this command */
513 void
514 unset_cmd_algorithm (void)
515 {
516   the_settings.algorithm = &global_algorithm;
517 }
518
519 /* Get the current syntax setting */
520 enum behavior_mode
521 settings_get_syntax (void)
522 {
523   return the_settings.syntax;
524 }
525
526 /* Set the syntax option */
527 void
528 settings_set_syntax ( enum behavior_mode mode)
529 {
530   the_settings.syntax = mode;
531 }
532
533 \f
534
535 /* Find the grouping characters in CC_STRING and set CC's
536    grouping and decimal members appropriately.  Returns true if
537    successful, false otherwise. */
538 static bool
539 find_cc_separators (const char *cc_string, struct fmt_number_style *cc)
540 {
541   const char *sp;
542   int comma_cnt, dot_cnt;
543
544   /* Count commas and periods.  There must be exactly three of
545      one or the other, except that an apostrophe escapes a
546      following comma or period. */
547   comma_cnt = dot_cnt = 0;
548   for (sp = cc_string; *sp; sp++)
549     if (*sp == ',')
550       comma_cnt++;
551     else if (*sp == '.')
552       dot_cnt++;
553     else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
554       sp++;
555
556   if ((comma_cnt == 3) == (dot_cnt == 3))
557     return false;
558
559   if (comma_cnt == 3)
560     {
561       cc->decimal = '.';
562       cc->grouping = ',';
563     }
564   else
565     {
566       cc->decimal = ',';
567       cc->grouping = '.';
568     }
569   return true;
570 }
571
572 /* Extracts a token from IN into a newly allocated AFFIX.  Tokens
573    are delimited by GROUPING.  The token is truncated to at most
574    FMT_STYLE_AFFIX_MAX characters.  Returns the first character
575    following the token. */
576 static const char *
577 extract_cc_token (const char *in, int grouping, struct substring *affix)
578 {
579   size_t ofs = 0;
580   ss_alloc_uninit (affix, FMT_STYLE_AFFIX_MAX);
581   for (; *in != '\0' && *in != grouping; in++)
582     {
583       if (*in == '\'' && in[1] == grouping)
584         in++;
585       if (ofs < FMT_STYLE_AFFIX_MAX)
586         ss_data (*affix)[ofs++] = *in;
587     }
588   affix->length = ofs;
589
590   if (*in == grouping)
591     in++;
592   return in;
593 }
594
595
596 /* Sets custom currency specifier CC having name CC_NAME ('A' through
597    'E') to correspond to the settings in CC_STRING. */
598 bool
599 settings_set_cc (const char *cc_string, enum fmt_type type)
600 {
601   struct fmt_number_style *cc = &the_settings.styles[type];
602
603   assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
604
605   /* Determine separators. */
606   if (!find_cc_separators (cc_string, cc))
607     {
608       msg (SE, _("%s: Custom currency string `%s' does not contain "
609                  "exactly three periods or commas (or it contains both)."),
610            fmt_name (type), cc_string);
611       return false;
612     }
613
614   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_prefix);
615   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->prefix);
616   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->suffix);
617   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_suffix);
618
619   fmt_check_style (cc);
620
621   return true;
622 }
623
624 /* Returns the decimal point character for TYPE. */
625 int
626 settings_get_decimal_char (enum fmt_type type)
627 {
628   return fmt_get_style (the_settings.styles, type)->decimal;
629 }
630
631 void
632 settings_set_decimal_char (char decimal)
633 {
634   fmt_set_decimal (the_settings.styles, decimal);
635 }
636
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 &the_settings.styles[type];
646 }
647
648
649 /* Returns a string of the form "$#,###.##" according to FMT,
650    which must be of type FMT_DOLLAR.  The caller must free the
651    string. */
652 char *
653 settings_dollar_template (const struct fmt_spec *fmt)
654 {
655   const struct fmt_number_style *styles = the_settings.styles;
656   struct string str = DS_EMPTY_INITIALIZER;
657   int c;
658   const struct fmt_number_style *fns ;
659
660   assert (fmt->type == FMT_DOLLAR);
661
662   fns = fmt_get_style (styles, fmt->type);
663
664   ds_put_char (&str, '$');
665   for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
666     {
667       ds_put_char (&str, '#');
668       if (--c % 4 == 0 && c > 0)
669         {
670           ds_put_char (&str, fns->grouping);
671           --c;
672         }
673     }
674   if (fmt->d > 0)
675     {
676       ds_put_char (&str, fns->decimal);
677       ds_put_char_multiple (&str, '#', fmt->d);
678     }
679
680   return ds_cstr (&str);
681 }
682
683 void
684 settings_set_output_routing (enum settings_output_type type,
685                              enum settings_output_devices devices)
686 {
687   assert (type < SETTINGS_N_OUTPUT_TYPES);
688   the_settings.output_routing[type] = devices;
689 }
690
691 enum settings_output_devices
692 settings_get_output_routing (enum settings_output_type type)
693 {
694   assert (type < SETTINGS_N_OUTPUT_TYPES);
695   return the_settings.output_routing[type] | SETTINGS_DEVICE_UNFILTERED;
696 }