Merge branch 'master'; commit 'origin/stable'
[pspp] / 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/i18n.h>
26 #include <libpspp/integer-format.h>
27 #include <libpspp/message.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   i18n_init ();
151   the_settings.styles = fmt_create ();
152
153   settings_set_decimal_char (get_system_decimal ());
154 }
155
156 void
157 settings_done (void)
158 {
159   fmt_done (the_settings.styles);
160   i18n_done ();
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 contains VALUE_CNT values. */
521 size_t
522 settings_get_workspace_cases (size_t value_cnt)
523 {
524   size_t case_size = sizeof (union value) * value_cnt + 4 * sizeof (void *);
525   size_t case_cnt = MAX (settings_get_workspace () / case_size, 4);
526   return case_cnt;
527 }
528
529 /* Set approximate maximum amount of memory to use for cases, in
530    bytes. */
531
532 void
533 settings_set_workspace ( size_t workspace)
534 {
535   the_settings.workspace = workspace;
536 }
537
538 /* Default format for variables created by transformations and by
539    DATA LIST {FREE,LIST}. */
540 const struct fmt_spec *
541 settings_get_format (void)
542 {
543   return &the_settings.default_format;
544 }
545
546 /* Set default format for variables created by transformations
547    and by DATA LIST {FREE,LIST}. */
548 void
549 settings_set_format ( const struct fmt_spec *default_format)
550 {
551   the_settings.default_format = *default_format;
552 }
553
554 /* Are we in testing mode?  (e.g. --testing-mode command line
555    option) */
556 bool
557 settings_get_testing_mode (void)
558 {
559   return the_settings.testing_mode;
560 }
561
562 /* Set testing mode. */
563 void
564 settings_set_testing_mode ( bool testing_mode)
565 {
566   the_settings.testing_mode = testing_mode;
567 }
568
569 /* Return the current algorithm setting */
570 enum behavior_mode
571 settings_get_algorithm (void)
572 {
573   return *the_settings.algorithm;
574 }
575
576 /* Set the algorithm option globally. */
577 void
578 settings_set_algorithm (enum behavior_mode mode)
579 {
580   global_algorithm = mode;
581 }
582
583 /* Set the algorithm option for this command only */
584 void
585 settings_set_cmd_algorithm ( enum behavior_mode mode)
586 {
587   the_settings.cmd_algorithm = mode;
588   the_settings.algorithm = &the_settings.cmd_algorithm;
589 }
590
591 /* Unset the algorithm option for this command */
592 void
593 unset_cmd_algorithm (void)
594 {
595   the_settings.algorithm = &global_algorithm;
596 }
597
598 /* Get the current syntax setting */
599 enum behavior_mode
600 settings_get_syntax (void)
601 {
602   return the_settings.syntax;
603 }
604
605 /* Set the syntax option */
606 void
607 settings_set_syntax ( enum behavior_mode mode)
608 {
609   the_settings.syntax = mode;
610 }
611
612 \f
613
614 /* Find the grouping characters in CC_STRING and set CC's
615    grouping and decimal members appropriately.  Returns true if
616    successful, false otherwise. */
617 static bool
618 find_cc_separators (const char *cc_string, struct fmt_number_style *cc)
619 {
620   const char *sp;
621   int comma_cnt, dot_cnt;
622
623   /* Count commas and periods.  There must be exactly three of
624      one or the other, except that an apostrophe escapes a
625      following comma or period. */
626   comma_cnt = dot_cnt = 0;
627   for (sp = cc_string; *sp; sp++)
628     if (*sp == ',')
629       comma_cnt++;
630     else if (*sp == '.')
631       dot_cnt++;
632     else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
633       sp++;
634
635   if ((comma_cnt == 3) == (dot_cnt == 3))
636     return false;
637
638   if (comma_cnt == 3)
639     {
640       cc->decimal = '.';
641       cc->grouping = ',';
642     }
643   else
644     {
645       cc->decimal = ',';
646       cc->grouping = '.';
647     }
648   return true;
649 }
650
651 /* Extracts a token from IN into a newly allocated AFFIX.  Tokens
652    are delimited by GROUPING.  The token is truncated to at most
653    FMT_STYLE_AFFIX_MAX characters.  Returns the first character
654    following the token. */
655 static const char *
656 extract_cc_token (const char *in, int grouping, struct substring *affix)
657 {
658   size_t ofs = 0;
659   ss_alloc_uninit (affix, FMT_STYLE_AFFIX_MAX);
660   for (; *in != '\0' && *in != grouping; in++)
661     {
662       if (*in == '\'' && in[1] == grouping)
663         in++;
664       if (ofs < FMT_STYLE_AFFIX_MAX)
665         ss_data (*affix)[ofs++] = *in;
666     }
667   affix->length = ofs;
668
669   if (*in == grouping)
670     in++;
671   return in;
672 }
673
674
675 /* Sets custom currency specifier CC having name CC_NAME ('A' through
676    'E') to correspond to the settings in CC_STRING. */
677 bool
678 settings_set_cc (const char *cc_string, enum fmt_type type)
679 {
680   struct fmt_number_style *cc = &the_settings.styles[type];
681
682   assert (fmt_get_category (type) == FMT_CAT_CUSTOM);
683
684   /* Determine separators. */
685   if (!find_cc_separators (cc_string, cc))
686     {
687       msg (SE, _("%s: Custom currency string `%s' does not contain "
688                  "exactly three periods or commas (or it contains both)."),
689            fmt_name (type), cc_string);
690       return false;
691     }
692
693   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_prefix);
694   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->prefix);
695   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->suffix);
696   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_suffix);
697
698   fmt_check_style (cc);
699
700   return true;
701 }
702
703 /* Returns the decimal point character for TYPE. */
704 int
705 settings_get_decimal_char (enum fmt_type type)
706 {
707   return fmt_get_style (the_settings.styles, type)->decimal;
708 }
709
710 void
711 settings_set_decimal_char (char decimal)
712 {
713   fmt_set_decimal (the_settings.styles, decimal);
714 }
715
716
717
718 /* Returns the number formatting style associated with the given
719    format TYPE. */
720 const struct fmt_number_style *
721 settings_get_style (enum fmt_type type)
722 {
723   assert (is_fmt_type (type));
724   return &the_settings.styles[type];
725 }
726
727
728 /* Returns a string of the form "$#,###.##" according to FMT,
729    which must be of type FMT_DOLLAR.  The caller must free the
730    string. */
731 char *
732 settings_dollar_template (const struct fmt_spec *fmt)
733 {
734   const struct fmt_number_style *styles = the_settings.styles;
735   struct string str = DS_EMPTY_INITIALIZER;
736   int c;
737   const struct fmt_number_style *fns ;
738
739   assert (fmt->type == FMT_DOLLAR);
740
741   fns = fmt_get_style (styles, fmt->type);
742
743   ds_put_char (&str, '$');
744   for (c = MAX (fmt->w - fmt->d - 1, 0); c > 0; )
745     {
746       ds_put_char (&str, '#');
747       if (--c % 4 == 0 && c > 0)
748         {
749           ds_put_char (&str, fns->grouping);
750           --c;
751         }
752     }
753   if (fmt->d > 0)
754     {
755       ds_put_char (&str, fns->decimal);
756       ds_put_char_multiple (&str, '#', fmt->d);
757     }
758
759   return ds_cstr (&str);
760 }