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