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