Implement SET ERRORS, SHOW ERRORS.
[pspp-builds.git] / src / language / utilities / set.q
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 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:terminal/listing/both/on/none/off;
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/on/none/off;
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/on/none/off;
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, enum fmt_type);
121
122 int
123 cmd_set (struct dataset *ds)
124 {
125   struct cmd_set cmd;
126
127   if (!parse_set (ds, &cmd, NULL))
128     return CMD_FAILURE;
129
130   if (cmd.sbc_cca)
131     do_cc (cmd.s_cca, FMT_CCA);
132   if (cmd.sbc_ccb)
133     do_cc (cmd.s_ccb, FMT_CCB);
134   if (cmd.sbc_ccc)
135     do_cc (cmd.s_ccc, FMT_CCC);
136   if (cmd.sbc_ccd)
137     do_cc (cmd.s_ccd, FMT_CCD);
138   if (cmd.sbc_cce)
139     do_cc (cmd.s_cce, FMT_CCE);
140
141   if (cmd.sbc_prompt)
142     getl_set_prompt (GETL_PROMPT_FIRST, cmd.s_prompt);
143   if (cmd.sbc_cprompt)
144     getl_set_prompt (GETL_PROMPT_LATER, cmd.s_cprompt);
145   if (cmd.sbc_dprompt)
146     getl_set_prompt (GETL_PROMPT_DATA, cmd.s_dprompt);
147
148   if (cmd.sbc_decimal)
149     fmt_set_decimal (cmd.dec == STC_DOT ? '.' : ',');
150   if (cmd.sbc_echo)
151     set_echo (cmd.echo == STC_ON);
152   if (cmd.sbc_endcmd)
153     set_endcmd (cmd.s_endcmd[0]);
154   if (cmd.sbc_errorbreak)
155     set_errorbreak (cmd.errbrk == STC_ON);
156   if (cmd.sbc_errors)
157     {
158       bool both = cmd.errors == STC_BOTH || cmd.errors == STC_ON;
159       set_error_routing_to_terminal (cmd.errors == STC_TERMINAL || both);
160       set_error_routing_to_listing (cmd.errors == STC_LISTING || both);
161     }
162   if (cmd.sbc_include)
163     set_include (cmd.inc == STC_ON);
164   if (cmd.sbc_mxerrs)
165     set_mxerrs (cmd.n_mxerrs[0]);
166   if (cmd.sbc_mxwarns)
167     set_mxwarns (cmd.n_mxwarns[0]);
168   if (cmd.sbc_nulline)
169     set_nulline (cmd.null == STC_ON);
170   if (cmd.sbc_safer)
171     set_safer_mode ();
172   if (cmd.sbc_scompression)
173     set_scompression (cmd.scompress == STC_ON);
174   if (cmd.sbc_undefined)
175     set_undefined (cmd.undef == STC_WARN);
176   if (cmd.sbc_workspace)
177     set_workspace (cmd.n_workspace[0] * 1024L);
178
179   if (cmd.sbc_block)
180     msg (SW, _("%s is obsolete."), "BLOCK");
181   if (cmd.sbc_boxstring)
182     msg (SW, _("%s is obsolete."), "BOXSTRING");
183   if (cmd.sbc_histogram)
184     msg (SW, _("%s is obsolete."), "HISTOGRAM");
185   if (cmd.sbc_menus)
186     msg (SW, _("%s is obsolete."), "MENUS");
187   if (cmd.sbc_xsort)
188     msg (SW, _("%s is obsolete."), "XSORT");
189   if (cmd.sbc_mxmemory)
190     msg (SE, _("%s is obsolete."), "MXMEMORY");
191   if (cmd.sbc_scripttab)
192     msg (SE, _("%s is obsolete."), "SCRIPTTAB");
193   if (cmd.sbc_tbfonts)
194     msg (SW, _("%s is obsolete."), "TBFONTS");
195   if (cmd.sbc_tb1 && cmd.s_tb1)
196     msg (SW, _("%s is obsolete."), "TB1");
197
198   if (cmd.sbc_case)
199     msg (SW, _("%s is not implemented."), "CASE");
200
201   if (cmd.sbc_compression)
202     msg (SW, _("Active file compression is not implemented."));
203
204   return CMD_SUCCESS;
205 }
206
207 /* Find the grouping characters in CC_STRING and set CC's
208    grouping and decimal members appropriately.  Returns true if
209    successful, false otherwise. */
210 static bool
211 find_cc_separators (const char *cc_string, struct fmt_number_style *cc)
212 {
213   const char *sp;
214   int comma_cnt, dot_cnt;
215   
216   /* Count commas and periods.  There must be exactly three of
217      one or the other, except that an apostrophe escapes a
218      following comma or period. */
219   comma_cnt = dot_cnt = 0;
220   for (sp = cc_string; *sp; sp++)
221     if (*sp == ',')
222       comma_cnt++;
223     else if (*sp == '.')
224       dot_cnt++;
225     else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
226       sp++;
227   
228   if ((comma_cnt == 3) == (dot_cnt == 3))
229     return false;
230
231   if (comma_cnt == 3)
232     {
233       cc->decimal = '.';
234       cc->grouping = ',';
235     }
236   else
237     {
238       cc->decimal = ',';
239       cc->grouping = '.';
240     }
241   return true;
242 }
243
244 /* Extracts a token from IN into a newly allocated AFFIX.  Tokens
245    are delimited by GROUPING.  The token is truncated to at most
246    FMT_STYLE_AFFIX_MAX characters.  Returns the first character
247    following the token. */
248 static const char *
249 extract_cc_token (const char *in, int grouping, struct substring *affix) 
250 {
251   size_t ofs = 0;
252   ss_alloc_uninit (affix, FMT_STYLE_AFFIX_MAX);
253   for (; *in != '\0' && *in != grouping; in++) 
254     {
255       if (*in == '\'' && in[1] == grouping)
256         in++;
257       if (ofs < FMT_STYLE_AFFIX_MAX) 
258         ss_data (*affix)[ofs++] = *in;
259     }
260   affix->length = ofs;
261
262   if (*in == grouping)
263     in++;
264   return in;
265 }
266
267 /* Sets custom currency specifier CC having name CC_NAME ('A' through
268    'E') to correspond to the settings in CC_STRING. */
269 static bool
270 do_cc (const char *cc_string, enum fmt_type type)
271 {
272   struct fmt_number_style *cc = fmt_number_style_create ();
273   
274   /* Determine separators. */
275   if (!find_cc_separators (cc_string, cc)) 
276     {
277       fmt_number_style_destroy (cc);
278       msg (SE, _("%s: Custom currency string `%s' does not contain "
279                  "exactly three periods or commas (or it contains both)."),
280            fmt_name (type), cc_string);
281       return false;
282     }
283   
284   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_prefix);
285   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->prefix);
286   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->suffix);
287   cc_string = extract_cc_token (cc_string, cc->grouping, &cc->neg_suffix);
288
289   fmt_set_style (type, cc);
290   
291   return true;
292 }
293
294 /* Parses the BLANKS subcommand, which controls the value that
295    completely blank fields in numeric data imply.  X, Wnd: Syntax is
296    SYSMIS or a numeric value. */
297 static int
298 stc_custom_blanks (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
299 {
300   lex_match ('=');
301   if ((token == T_ID && lex_id_match ("SYSMIS", tokid)))
302     {
303       lex_get ();
304       set_blanks (SYSMIS);
305     }
306   else
307     {
308       if (!lex_force_num ())
309         return 0;
310       set_blanks (lex_number ());
311       lex_get ();
312     }
313   return 1;
314 }
315
316 /* Parses the EPOCH subcommand, which controls the epoch used for
317    parsing 2-digit years. */
318 static int
319 stc_custom_epoch (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED) 
320 {
321   lex_match ('=');
322   if (lex_match_id ("AUTOMATIC"))
323     set_epoch (-1);
324   else if (lex_is_integer ()) 
325     {
326       int new_epoch = lex_integer ();
327       lex_get ();
328       if (new_epoch < 1500) 
329         {
330           msg (SE, _("EPOCH must be 1500 or later."));
331           return 0;
332         }
333       set_epoch (new_epoch);
334     }
335   else 
336     {
337       lex_error (_("expecting AUTOMATIC or year"));
338       return 0;
339     }
340
341   return 1;
342 }
343
344 static int
345 stc_custom_length (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
346 {
347   int page_length;
348
349   lex_match ('=');
350   if (lex_match_id ("NONE"))
351     page_length = -1;
352   else
353     {
354       if (!lex_force_int ())
355         return 0;
356       if (lex_integer () < 1)
357         {
358           msg (SE, _("LENGTH must be at least 1."));
359           return 0;
360         }
361       page_length = lex_integer ();
362       lex_get ();
363     }
364
365   if (page_length != -1) 
366     set_viewlength (page_length);
367
368   return 1;
369 }
370
371 static int
372 stc_custom_seed (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
373 {
374   lex_match ('=');
375   if (lex_match_id ("RANDOM"))
376     set_rng (time (0));
377   else
378     {
379       if (!lex_force_num ())
380         return 0;
381       set_rng (lex_number ());
382       lex_get ();
383     }
384
385   return 1;
386 }
387
388 static int
389 stc_custom_width (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
390 {
391   lex_match ('=');
392   if (lex_match_id ("NARROW"))
393     set_viewwidth (79);
394   else if (lex_match_id ("WIDE"))
395     set_viewwidth (131);
396   else
397     {
398       if (!lex_force_int ())
399         return 0;
400       if (lex_integer () < 40)
401         {
402           msg (SE, _("WIDTH must be at least 40."));
403           return 0;
404         }
405       set_viewwidth (lex_integer ());
406       lex_get ();
407     }
408
409   return 1;
410 }
411
412 /* Parses FORMAT subcommand, which consists of a numeric format
413    specifier. */
414 static int
415 stc_custom_format (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
416 {
417   struct fmt_spec fmt;
418
419   lex_match ('=');
420   if (!parse_format_specifier (&fmt))
421     return 0;
422   if (fmt_is_string (fmt.type))
423     {
424       char str[FMT_STRING_LEN_MAX + 1];
425       msg (SE, _("FORMAT requires numeric output format as an argument.  "
426                  "Specified format %s is of type string."),
427            fmt_to_string (&fmt, str));
428       return 0;
429     }
430
431   set_format (&fmt);
432   return 1;
433 }
434
435 static int
436 stc_custom_journal (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
437 {
438   lex_match ('=');
439   if (!lex_match_id ("ON") && !lex_match_id ("OFF")) 
440     {
441       if (token == T_STRING)
442         lex_get ();
443       else
444         {
445           lex_error (NULL);
446           return 0;
447         }
448     }
449   return 1;
450 }
451
452 static int
453 stc_custom_listing (struct dataset *ds UNUSED, struct cmd_set *cmd UNUSED, void *aux UNUSED)
454 {
455   bool listing;
456
457   lex_match ('=');
458   if (lex_match_id ("ON") || lex_match_id ("YES"))
459     listing = true;
460   else if (lex_match_id ("OFF") || lex_match_id ("NO"))
461     listing = false;
462   else
463     {
464       /* FIXME */
465       return 0;
466     }
467   outp_enable_device (listing, OUTP_DEV_LISTING);
468
469   return 1;
470 }
471
472 static int
473 stc_custom_disk (struct dataset *ds, struct cmd_set *cmd UNUSED, void *aux)
474 {
475   return stc_custom_listing (ds, cmd, aux);
476 }
477 \f
478 static void
479 show_blanks (const struct dataset *ds UNUSED) 
480 {
481   if (get_blanks () == SYSMIS)
482     msg (SN, _("BLANKS is SYSMIS."));
483   else
484     msg (SN, _("BLANKS is %g."), get_blanks ());
485
486 }
487
488 static char *
489 format_cc (struct substring in, char grouping, char *out) 
490 {
491   while (!ss_is_empty (in)) 
492     {
493       char c = ss_get_char (&in);
494       if (c == grouping || c == '\'')
495         *out++ = '\'';
496       else if (c == '"')
497         *out++ = '"';
498       *out++ = c;
499     }
500   return out;
501 }
502
503 static void
504 show_cc (enum fmt_type type) 
505 {
506   const struct fmt_number_style *cc = fmt_get_style (type);
507   char cc_string[FMT_STYLE_AFFIX_MAX * 4 * 2 + 3 + 1];
508   char *out;
509
510   out = format_cc (cc->neg_prefix, cc->grouping, cc_string);
511   *out++ = cc->grouping;
512   out = format_cc (cc->prefix, cc->grouping, out);
513   *out++ = cc->grouping;
514   out = format_cc (cc->suffix, cc->grouping, out);
515   *out++ = cc->grouping;
516   out = format_cc (cc->neg_suffix, cc->grouping, out);
517   *out = '\0';
518   
519   msg (SN, _("%s is \"%s\"."), fmt_name (type), cc_string);
520 }
521
522 static void
523 show_cca (const struct dataset *ds UNUSED) 
524 {
525   show_cc (FMT_CCA);
526 }
527
528 static void
529 show_ccb (const struct dataset *ds UNUSED) 
530 {
531   show_cc (FMT_CCB);
532 }
533
534 static void
535 show_ccc (const struct dataset *ds UNUSED) 
536 {
537   show_cc (FMT_CCC);
538 }
539
540 static void
541 show_ccd (const struct dataset *ds UNUSED) 
542 {
543   show_cc (FMT_CCD);
544 }
545
546 static void
547 show_cce (const struct dataset *ds UNUSED) 
548 {
549   show_cc (FMT_CCE);
550 }
551
552 static void
553 show_decimals (const struct dataset *ds UNUSED) 
554 {
555   msg (SN, _("DECIMAL is \"%c\"."), fmt_decimal_char (FMT_F));
556 }
557
558 static void
559 show_endcmd (const struct dataset *ds UNUSED) 
560 {
561   msg (SN, _("ENDCMD is \"%c\"."), get_endcmd ());
562 }
563
564 static void
565 show_errors (const struct dataset *ds UNUSED) 
566 {
567   bool terminal = get_error_routing_to_terminal ();
568   bool listing = get_error_routing_to_listing ();
569   msg (SN, _("ERRORS is \"%s\"."),
570        terminal && listing ? "BOTH"
571        : terminal ? "TERMINAL"
572        : listing ? "LISTING"
573        : "NONE");
574 }
575
576 static void
577 show_format (const struct dataset *ds UNUSED) 
578 {
579   char str[FMT_STRING_LEN_MAX + 1];
580   msg (SN, _("FORMAT is %s."), fmt_to_string (get_format (), str));
581 }
582
583 static void
584 show_length (const struct dataset *ds UNUSED) 
585 {
586   msg (SN, _("LENGTH is %d."), get_viewlength ());
587 }
588
589 static void
590 show_mxerrs (const struct dataset *ds UNUSED) 
591 {
592   msg (SN, _("MXERRS is %d."), get_mxerrs ());
593 }
594
595 static void
596 show_mxloops (const struct dataset *ds UNUSED) 
597 {
598   msg (SN, _("MXLOOPS is %d."), get_mxloops ());
599 }
600
601 static void
602 show_mxwarns (const struct dataset *ds UNUSED) 
603 {
604   msg (SN, _("MXWARNS is %d."), get_mxwarns ());
605 }
606
607 static void
608 show_scompression (const struct dataset *ds UNUSED) 
609 {
610   if (get_scompression ())
611     msg (SN, _("SCOMPRESSION is ON."));
612   else
613     msg (SN, _("SCOMPRESSION is OFF."));
614 }
615
616 static void
617 show_undefined (const struct dataset *ds UNUSED) 
618 {
619   if (get_undefined ())
620     msg (SN, _("UNDEFINED is WARN."));
621   else
622     msg (SN, _("UNDEFINED is NOWARN."));
623 }
624
625 static void
626 show_weight (const struct dataset *ds) 
627 {
628   struct variable *var = dict_get_weight (dataset_dict (ds));
629   if (var == NULL)
630     msg (SN, _("WEIGHT is off."));
631   else
632     msg (SN, _("WEIGHT is variable %s."), var->name);
633 }
634
635 static void
636 show_width (const struct dataset *ds UNUSED) 
637 {
638   msg (SN, _("WIDTH is %d."), get_viewwidth ());
639 }
640
641 struct show_sbc 
642   {
643     const char *name;
644     void (*function) (const struct dataset *);
645   };
646
647 const struct show_sbc show_table[] = 
648   {
649     {"BLANKS", show_blanks},
650     {"CCA", show_cca},
651     {"CCB", show_ccb},
652     {"CCC", show_ccc},
653     {"CCD", show_ccd},
654     {"CCE", show_cce},
655     {"DECIMALS", show_decimals},
656     {"ENDCMD", show_endcmd},
657     {"ERRORS", show_errors},      
658     {"FORMAT", show_format},
659     {"LENGTH", show_length},
660     {"MXERRS", show_mxerrs},
661     {"MXLOOPS", show_mxloops},
662     {"MXWARNS", show_mxwarns},
663     {"SCOMPRESSION", show_scompression},
664     {"UNDEFINED", show_undefined},
665     {"WEIGHT", show_weight},
666     {"WIDTH", show_width},
667   };
668
669 static void
670 show_all (const struct dataset *ds) 
671 {
672   size_t i;
673   
674   for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
675     show_table[i].function (ds);
676 }
677
678 static void
679 show_all_cc (void) 
680 {
681   int i;
682
683   for (i = 0; i < 5; i++)
684     show_cc (i);
685 }
686
687 static void
688 show_warranty (const struct dataset *ds UNUSED) 
689 {
690   msg (MN, lack_of_warranty);
691 }
692
693 static void
694 show_copying (const struct dataset *ds UNUSED) 
695 {
696   msg (MN, copyleft);
697 }
698
699 int
700 cmd_show (struct dataset *ds) 
701 {
702   if (token == '.') 
703     {
704       show_all (ds);
705       return CMD_SUCCESS;
706     }
707
708   do 
709     {
710       if (lex_match (T_ALL))
711         show_all (ds);
712       else if (lex_match_id ("CC")) 
713         show_all_cc ();
714       else if (lex_match_id ("WARRANTY"))
715         show_warranty (ds);
716       else if (lex_match_id ("COPYING"))
717         show_copying (ds);
718       else if (token == T_ID)
719         {
720           int i;
721
722           for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
723             if (lex_match_id (show_table[i].name)) 
724               {
725                 show_table[i].function (ds);
726                 goto found;
727               }
728           lex_error (NULL);
729           return CMD_FAILURE;
730
731         found: ;
732         }
733       else 
734         {
735           lex_error (NULL);
736           return CMD_FAILURE;
737         }
738
739       lex_match ('/');
740     }
741   while (token != '.');
742
743   return CMD_SUCCESS;
744 }
745
746 /*
747    Local Variables:
748    mode: c
749    End:
750 */