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