Separate settings and the SET command, for modularity.
[pspp-builds.git] / src / set.q
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "settings.h"
22 #include "error.h"
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <time.h>
27 #include "alloc.h"
28 #include "command.h"
29 #include "dictionary.h"
30 #include "lexer.h"
31 #include "error.h"
32 #include "magic.h"
33 #include "log.h"
34 #include "output.h"
35 #include "var.h"
36 #include "format.h"
37 #include "copyleft.h"
38 #include "var.h"
39
40
41 #if HAVE_LIBTERMCAP
42 #if HAVE_TERMCAP_H
43 #include <termcap.h>
44 #else /* !HAVE_TERMCAP_H */
45 int tgetent (char *, const char *);
46 int tgetnum (const char *);
47 #endif /* !HAVE_TERMCAP_H */
48 #endif /* !HAVE_LIBTERMCAP */
49
50 #include "gettext.h"
51 #define _(msgid) gettext (msgid)
52
53 /* (specification)
54    "SET" (stc_):
55      blanks=custom;
56      block=string "x==1" "one character long";
57      boxstring=string "x==3 || x==11" "3 or 11 characters long";
58      case=size:upper/uplow;
59      cca=string;
60      ccb=string;
61      ccc=string;
62      ccd=string;
63      cce=string;
64      compression=compress:on/off;
65      cpi=integer "x>0" "%s must be greater than 0";
66      cprompt=string;
67      decimal=dec:dot/comma;
68      disk=custom;
69      dprompt=string;
70      echo=echo:on/off;
71      endcmd=string "x==1" "one character long";
72      epoch=custom;
73      errorbreak=errbrk:on/off;
74      errors=errors:on/off/terminal/listing/both/none;
75      format=custom;
76      headers=headers:no/yes/blank;
77      highres=hires:on/off;
78      histogram=string "x==1" "one character long";
79      include=inc:on/off;
80      journal=custom;
81      length=custom;
82      listing=custom;
83      lowres=lores:auto/on/off;
84      lpi=integer "x>0" "%s must be greater than 0";
85      menus=menus:standard/extended;
86      messages=messages:on/off/terminal/listing/both/none;
87      mexpand=mexp:on/off;
88      miterate=integer "x>0" "%s must be greater than 0";
89      mnest=integer "x>0" "%s must be greater than 0";
90      mprint=mprint:on/off;
91      mxerrs=integer "x >= 1" "%s must be at least 1";
92      mxloops=integer "x >=1" "%s must be at least 1";
93      mxmemory=integer;
94      mxwarns=integer;
95      nulline=null:on/off;
96      printback=prtbck:on/off;
97      prompt=string;
98      results=res:on/off/terminal/listing/both/none;
99      safer=safe:on;
100      scompression=scompress:on/off;
101      scripttab=string "x==1" "one character long";
102      seed=custom;
103      tb1=string "x==3 || x==11" "3 or 11 characters long";
104      tbfonts=string;
105      undefined=undef:warn/nowarn;
106      width=custom;
107      workspace=integer "x>=1024" "%s must be at least 1 MB";
108      xsort=xsort:yes/no.
109 */
110
111 /* (declarations) */
112
113 /* (_functions) */
114
115 static bool do_cc (const char *cc_string, int idx);
116
117 int
118 cmd_set (void)
119 {
120   struct cmd_set cmd;
121   bool ok = true;
122
123   if (!parse_set (&cmd))
124     return CMD_FAILURE;
125
126   if (cmd.sbc_cca)
127     ok = ok && do_cc (cmd.s_cca, 0);
128   if (cmd.sbc_ccb)
129     ok = ok && do_cc (cmd.s_ccb, 1);
130   if (cmd.sbc_ccc)
131     ok = ok && do_cc (cmd.s_ccc, 2);
132   if (cmd.sbc_ccd)
133     ok = ok && do_cc (cmd.s_ccd, 3);
134   if (cmd.sbc_cce)
135     ok = ok && do_cc (cmd.s_cce, 4);
136
137   if (cmd.sbc_prompt)
138     set_prompt (cmd.s_prompt);
139   if (cmd.sbc_cprompt)
140     set_prompt (cmd.s_cprompt);
141   if (cmd.sbc_dprompt)
142     set_prompt (cmd.s_dprompt);
143
144   if (cmd.sbc_decimal)
145     set_decimal (cmd.dec == STC_DOT ? '.' : ',');
146   if (cmd.sbc_echo)
147     set_echo (cmd.echo == STC_ON);
148   if (cmd.sbc_endcmd)
149     set_endcmd (cmd.s_endcmd[0]);
150   if (cmd.sbc_errorbreak)
151     set_errorbreak (cmd.errbrk == STC_ON);
152   if (cmd.sbc_include)
153     set_include (cmd.inc == STC_ON);
154   if (cmd.sbc_mxerrs)
155     set_mxerrs (cmd.n_mxerrs[0]);
156   if (cmd.sbc_mxwarns)
157     set_mxwarns (cmd.n_mxwarns[0]);
158   if (cmd.sbc_nulline)
159     set_nulline (cmd.null == STC_ON);
160   if (cmd.sbc_safer)
161     set_safer_mode ();
162   if (cmd.sbc_scompression)
163     set_scompression (cmd.scompress == STC_ON);
164   if (cmd.sbc_undefined)
165     set_undefined (cmd.undef == STC_WARN);
166   if (cmd.sbc_workspace)
167     set_workspace (cmd.n_workspace[0] * 1024L);
168
169   if (cmd.sbc_block)
170     msg (SW, _("%s is obsolete."),"BLOCK");
171   if (cmd.sbc_boxstring)
172     msg (SW, _("%s is obsolete."),"BOXSTRING");
173   if (cmd.sbc_histogram)
174     msg (MW, _("%s is obsolete."),"HISTOGRAM");
175   if (cmd.sbc_menus )
176     msg (MW, _("%s is obsolete."),"MENUS");
177   if (cmd.sbc_xsort )
178     msg (SW, _("%s is obsolete."),"XSORT");
179   if (cmd.sbc_mxmemory )
180     msg (SE, _("%s is obsolete."),"MXMEMORY");
181   if (cmd.sbc_scripttab)
182     msg (SE, _("%s is obsolete."),"SCRIPTTAB");
183   if (cmd.sbc_tbfonts)
184     msg (SW, _("%s is obsolete."),"TBFONTS");
185   if (cmd.sbc_tb1 && cmd.s_tb1)
186     msg (SW, _("%s is obsolete."),"TB1");
187
188   if (cmd.sbc_case)
189     msg (SW, _("%s is not implemented."), "CASE");
190
191   if (cmd.sbc_compression)
192     msg (MW, _("Active file compression is not implemented."));
193
194   return CMD_SUCCESS;
195 }
196
197 /* Find the grouping characters in CC_STRING and set CC's
198    grouping and decimal members appropriately.  Returns true if
199    successful, false otherwise. */
200 static bool
201 find_cc_separators (const char *cc_string, struct custom_currency *cc)
202 {
203   const char *sp;
204   int comma_cnt, dot_cnt;
205   
206   /* Count commas and periods.  There must be exactly three of
207      one or the other, except that an apostrophe acts escapes a
208      following comma or period. */
209   comma_cnt = dot_cnt = 0;
210   for (sp = cc_string; *sp; sp++)
211     if (*sp == ',')
212       comma_cnt++;
213     else if (*sp == '.')
214       dot_cnt++;
215     else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
216       sp++;
217   
218   if ((comma_cnt == 3) == (dot_cnt == 3))
219     return false;
220
221   if (comma_cnt == 3)
222     {
223       cc->decimal = '.';
224       cc->grouping = ',';
225     }
226   else
227     {
228       cc->decimal = ',';
229       cc->grouping = '.';
230     }
231   return true;
232 }
233
234 /* Extracts a token from IN into TOKEn.  Tokens are delimited by
235    GROUPING.  The token is truncated to at most CC_WIDTH
236    characters (including null terminator).  Returns the first
237    character following the token. */
238 static const char *
239 extract_cc_token (const char *in, int grouping, char token[CC_WIDTH]) 
240 {
241   char *out = token;
242   
243   for (; *in != '\0' && *in != grouping; in++) 
244     {
245       if (*in == '\'' && in[1] == grouping)
246         in++;
247       if (out < &token[CC_WIDTH - 1])
248         *out++ = *in;
249     }
250   *out = '\0';
251
252   if (*in == grouping)
253     in++;
254   return in;
255 }
256
257 /* Sets custom currency specifier CC having name CC_NAME ('A' through
258    'E') to correspond to the settings in CC_STRING. */
259 static bool
260 do_cc (const char *cc_string, int idx)
261 {
262   struct custom_currency cc;
263   
264   /* Determine separators. */
265   if (!find_cc_separators (cc_string, &cc)) 
266     {
267       msg (SE, _("CC%c: Custom currency string `%s' does not contain "
268                  "exactly three periods or commas (not both)."),
269            "ABCDE"[idx], cc_string);
270       return false;
271     }
272   
273   cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_prefix);
274   cc_string = extract_cc_token (cc_string, cc.grouping, cc.prefix);
275   cc_string = extract_cc_token (cc_string, cc.grouping, cc.suffix);
276   cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_suffix);
277
278   set_cc (idx, &cc);
279   
280   return true;
281 }
282
283 /* Parses the BLANKS subcommand, which controls the value that
284    completely blank fields in numeric data imply.  X, Wnd: Syntax is
285    SYSMIS or a numeric value. */
286 static int
287 stc_custom_blanks (struct cmd_set *cmd UNUSED)
288 {
289   lex_match ('=');
290   if ((token == T_ID && lex_id_match ("SYSMIS", tokid)))
291     {
292       lex_get ();
293       set_blanks (SYSMIS);
294     }
295   else
296     {
297       if (!lex_force_num ())
298         return 0;
299       set_blanks (lex_number ());
300       lex_get ();
301     }
302   return 1;
303 }
304
305 /* Parses the EPOCH subcommand, which controls the epoch used for
306    parsing 2-digit years. */
307 static int
308 stc_custom_epoch (struct cmd_set *cmd UNUSED) 
309 {
310   lex_match ('=');
311   if (lex_match_id ("AUTOMATIC"))
312     set_epoch (-1);
313   else if (lex_is_integer ()) 
314     {
315       int new_epoch = lex_integer ();
316       lex_get ();
317       if (new_epoch < 1500) 
318         {
319           msg (SE, _("EPOCH must be 1500 or later."));
320           return 0;
321         }
322       set_epoch (new_epoch);
323     }
324   else 
325     {
326       lex_error (_("expecting AUTOMATIC or year"));
327       return 0;
328     }
329
330   return 1;
331 }
332
333 static int
334 stc_custom_length (struct cmd_set *cmd UNUSED)
335 {
336   int page_length;
337
338   lex_match ('=');
339   if (lex_match_id ("NONE"))
340     page_length = -1;
341   else
342     {
343       if (!lex_force_int ())
344         return 0;
345       if (lex_integer () < 1)
346         {
347           msg (SE, _("LENGTH must be at least 1."));
348           return 0;
349         }
350       page_length = lex_integer ();
351       lex_get ();
352     }
353
354   if (page_length != -1) 
355     set_viewlength (page_length);
356
357   return 1;
358 }
359
360 static int
361 stc_custom_seed (struct cmd_set *cmd UNUSED)
362 {
363   lex_match ('=');
364   if (lex_match_id ("RANDOM"))
365     set_rng (time (0));
366   else
367     {
368       if (!lex_force_num ())
369         return 0;
370       set_rng (lex_number ());
371       lex_get ();
372     }
373
374   return 1;
375 }
376
377 static int
378 stc_custom_width (struct cmd_set *cmd UNUSED)
379 {
380   lex_match ('=');
381   if (lex_match_id ("NARROW"))
382     set_viewwidth (79);
383   else if (lex_match_id ("WIDE"))
384     set_viewwidth (131);
385   else
386     {
387       if (!lex_force_int ())
388         return 0;
389       if (lex_integer () < 40)
390         {
391           msg (SE, _("WIDTH must be at least 40."));
392           return 0;
393         }
394       set_viewwidth (lex_integer ());
395       lex_get ();
396     }
397
398   return 1;
399 }
400
401 /* Parses FORMAT subcommand, which consists of a numeric format
402    specifier. */
403 static int
404 stc_custom_format (struct cmd_set *cmd UNUSED)
405 {
406   struct fmt_spec fmt;
407
408   lex_match ('=');
409   if (!parse_format_specifier (&fmt, 0))
410     return 0;
411   if ((formats[fmt.type].cat & FCAT_STRING) != 0)
412     {
413       msg (SE, _("FORMAT requires numeric output format as an argument.  "
414                  "Specified format %s is of type string."),
415            fmt_to_string (&fmt));
416       return 0;
417     }
418
419   set_format (&fmt);
420   return 1;
421 }
422
423 static int
424 stc_custom_journal (struct cmd_set *cmd UNUSED)
425 {
426   lex_match ('=');
427   if (!lex_match_id ("ON") && !lex_match_id ("OFF")) 
428     {
429       if (token == T_STRING)
430         lex_get ();
431       else
432         {
433           lex_error (NULL);
434           return 0;
435         }
436     }
437   return 1;
438 }
439
440 static int
441 stc_custom_listing (struct cmd_set *cmd UNUSED)
442 {
443   bool listing;
444
445   lex_match ('=');
446   if (lex_match_id ("ON") || lex_match_id ("YES"))
447     listing = true;
448   else if (lex_match_id ("OFF") || lex_match_id ("NO"))
449     listing = false;
450   else
451     {
452       /* FIXME */
453       return 0;
454     }
455   outp_enable_device (listing, OUTP_DEV_LISTING);
456
457   return 1;
458 }
459
460 static int
461 stc_custom_disk (struct cmd_set *cmd UNUSED)
462 {
463   return stc_custom_listing (cmd);
464 }
465 \f
466 static void
467 show_blanks (void) 
468 {
469   if (get_blanks () == SYSMIS)
470     msg (MM, _("BLANKS is SYSMIS."));
471   else
472     msg (MM, _("BLANKS is %g."), get_blanks ());
473
474 }
475
476 static char *
477 format_cc (const char *in, char grouping, char *out) 
478 {
479   while (*in != '\0') 
480     {
481       if (*in == grouping || *in == '\'')
482         *out++ = '\'';
483       *out++ = *in++;
484     }
485   return out;
486 }
487
488 static void
489 show_cc (int idx) 
490 {
491   const struct custom_currency *cc = get_cc (idx);
492   char cc_string[CC_WIDTH * 4 * 2 + 3 + 1];
493   char *out;
494
495   out = format_cc (cc->neg_prefix, cc->grouping, cc_string);
496   *out++ = cc->grouping;
497   out = format_cc (cc->prefix, cc->grouping, out);
498   *out++ = cc->grouping;
499   out = format_cc (cc->suffix, cc->grouping, out);
500   *out++ = cc->grouping;
501   out = format_cc (cc->neg_suffix, cc->grouping, out);
502   *out = '\0';
503   
504   msg (MM, _("CC%c is \"%s\"."), "ABCDE"[idx], cc_string);
505 }
506
507
508 static void
509 show_cca (void) 
510 {
511   show_cc (0);
512 }
513
514 static void
515 show_ccb (void) 
516 {
517   show_cc (1);
518 }
519
520 static void
521 show_ccc (void) 
522 {
523   show_cc (2);
524 }
525
526 static void
527 show_ccd (void) 
528 {
529   show_cc (3);
530 }
531
532 static void
533 show_cce (void) 
534 {
535   show_cc (4);
536 }
537
538 static void
539 show_decimals (void) 
540 {
541   msg (MM, _("DECIMAL is \"%c\"."), get_decimal ());
542 }
543
544 static void
545 show_endcmd (void) 
546 {
547   msg (MM, _("ENDCMD is \"%c\"."), get_endcmd ());
548 }
549
550 static void
551 show_format (void) 
552 {
553   msg (MM, _("FORMAT is %s."), fmt_to_string (get_format ()));
554 }
555
556 static void
557 show_length (void) 
558 {
559   msg (MM, _("LENGTH is %d."), get_viewlength ());
560 }
561
562 static void
563 show_mxerrs (void) 
564 {
565   msg (MM, _("MXERRS is %d."), get_mxerrs ());
566 }
567
568 static void
569 show_mxloops (void) 
570 {
571   msg (MM, _("MXLOOPS is %d."), get_mxloops ());
572 }
573
574 static void
575 show_mxwarns (void) 
576 {
577   msg (MM, _("MXWARNS is %d."), get_mxwarns ());
578 }
579
580 static void
581 show_scompression (void) 
582 {
583   if (get_scompression ())
584     msg (MM, _("SCOMPRESSION is ON."));
585   else
586     msg (MM, _("SCOMPRESSION is OFF."));
587 }
588
589 static void
590 show_undefined (void) 
591 {
592   if (get_undefined ())
593     msg (MM, _("UNDEFINED is WARN."));
594   else
595     msg (MM, _("UNDEFINED is NOWARN."));
596 }
597
598 static void
599 show_weight (void) 
600 {
601   struct variable *var = dict_get_weight (default_dict);
602   if (var == NULL)
603     msg (MM, _("WEIGHT is off."));
604   else
605     msg (MM, _("WEIGHT is variable %s."), var->name);
606 }
607
608 static void
609 show_width (void) 
610 {
611   msg (MM, _("WIDTH is %d."), get_viewwidth ());
612 }
613
614 struct show_sbc 
615   {
616     const char *name;
617     void (*function) (void);
618   };
619
620 struct show_sbc show_table[] = 
621   {
622     {"BLANKS", show_blanks},
623     {"CCA", show_cca},
624     {"CCB", show_ccb},
625     {"CCC", show_ccc},
626     {"CCD", show_ccd},
627     {"CCE", show_cce},
628     {"DECIMALS", show_decimals},
629     {"ENDCMD", show_endcmd},
630     {"FORMAT", show_format},
631     {"LENGTH", show_length},
632     {"MXERRS", show_mxerrs},
633     {"MXLOOPS", show_mxloops},
634     {"MXWARNS", show_mxwarns},
635     {"SCOMPRESSION", show_scompression},
636     {"UNDEFINED", show_undefined},
637     {"WEIGHT", show_weight},
638     {"WIDTH", show_width},
639   };
640
641 static void
642 show_all (void) 
643 {
644   size_t i;
645   
646   for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
647     show_table[i].function ();
648 }
649
650 static void
651 show_all_cc (void) 
652 {
653   int i;
654
655   for (i = 0; i < 5; i++)
656     show_cc (i);
657 }
658
659 static void
660 show_warranty (void) 
661 {
662   msg (MM, lack_of_warranty);
663 }
664
665 static void
666 show_copying (void) 
667 {
668   msg (MM, copyleft);
669 }
670
671 int
672 cmd_show (void) 
673 {
674   if (token == '.') 
675     {
676       show_all ();
677       return CMD_SUCCESS;
678     }
679
680   do 
681     {
682       if (lex_match (T_ALL))
683         show_all ();
684       else if (lex_match_id ("CC")) 
685         show_all_cc ();
686       else if (lex_match_id ("WARRANTY"))
687         show_warranty ();
688       else if (lex_match_id ("COPYING"))
689         show_copying ();
690       else if (token == T_ID)
691         {
692           int i;
693
694           for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
695             if (lex_match_id (show_table[i].name)) 
696               {
697                 show_table[i].function ();
698                 goto found;
699               }
700           lex_error (NULL);
701           return CMD_PART_SUCCESS_MAYBE;
702
703         found: ;
704         }
705       else 
706         {
707           lex_error (NULL);
708           return CMD_PART_SUCCESS_MAYBE;
709         }
710
711       lex_match ('/');
712     }
713   while (token != '.');
714
715   return CMD_SUCCESS;
716 }
717
718 /*
719    Local Variables:
720    mode: c
721    End:
722 */