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