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