Fix assertion for proper Huffman merge pattern: 0 == 1 modulo 1.
[pspp] / 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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 /*
21    Categories of SET subcommands:
22
23    data input: BLANKS, DECIMAL, FORMAT.
24    
25    program input: ENDCMD, NULLINE.
26    
27    interaction: CPROMPT, DPROMPT, ERRORBREAK, MXERRS, MXWARNS, PROMPT.
28    
29    program execution: MEXPAND, MITERATE, MNEST, MPRINT,
30    MXLOOPS, SEED, UNDEFINED.
31
32    data output: CCA...CCE, DECIMAL, FORMAT, RESULTS-p.
33
34    output routing: ECHO, ERRORS, INCLUDE, MESSAGES, PRINTBACK, ERRORS,
35    RESULTS-rw.
36
37    output activation: LISTING (on/off), SCREEN, PRINTER.
38
39    output driver options: HEADERS, MORE, PAGER, VIEWLENGTH, VIEWWIDTH,
40    LISTING (filename).
41
42    logging: LOG, JOURNAL.
43
44    system files: COMP/COMPRESSION, SCOMP/SCOMPRESSION.
45
46    security: SAFER.
47 */
48
49 /*
50    FIXME
51
52    These subcommands remain to be implemented:
53      ECHO, PRINTBACK, INCLUDE
54      MORE, PAGER, VIEWLENGTH, VIEWWIDTH, HEADERS
55
56    These subcommands are not complete:
57      MESSAGES, ERRORS, RESULTS
58      LISTING/DISK, LOG/JOURNAL
59 */     
60    
61 #include <config.h>
62 #include "settings.h"
63 #include "error.h"
64 #include <stdio.h>
65 #include <errno.h>
66 #include <stdlib.h>
67 #include <time.h>
68 #include "alloc.h"
69 #include "command.h"
70 #include "lexer.h"
71 #include "error.h"
72 #include "magic.h"
73 #include "log.h"
74 #include "output.h"
75 #include "var.h"
76 #include "format.h"
77 #include "copyleft.h"
78
79 #include "signal.h"
80
81 #if HAVE_LIBTERMCAP
82 #if HAVE_TERMCAP_H
83 #include <termcap.h>
84 #else /* !HAVE_TERMCAP_H */
85 int tgetent (char *, const char *);
86 int tgetnum (const char *);
87 #endif /* !HAVE_TERMCAP_H */
88 #endif /* !HAVE_LIBTERMCAP */
89
90 static int set_errors;
91 static int set_messages;
92 static int set_results;
93
94 static double set_blanks=SYSMIS;
95
96 static struct fmt_spec set_format={FMT_F,8,2};
97
98 static struct set_cust_currency set_cc[5];
99   
100 static char *set_journal;
101 static int set_journaling;
102
103 static int set_listing=1;
104
105 #if !USE_INTERNAL_PAGER
106 static char *set_pager=0;
107 #endif /* !USE_INTERNAL_PAGER */
108
109 static gsl_rng *rng;
110
111 static int long_view=0;
112 int set_testing_mode=0;
113 static int set_viewlength;
114 static int set_viewwidth;
115
116 void aux_show_warranty(void);
117 void aux_show_copying(void);
118
119 static const char *route_to_string(int routing);
120 static void set_routing (int q, int *setting);
121
122 static int set_ccx (const char *cc_string, struct set_cust_currency * cc,
123                     int cc_name);
124 static void set_rng (unsigned long);
125 static unsigned long random_seed (void);
126
127 /* (specification)
128    "SET" (stc_):
129      automenu=automenu:on/off;
130      beep=beep:on/off;
131      blanks=custom;
132      block=string "x==1" "one character long";
133      boxstring=string "x==3 || x==11" "3 or 11 characters long";
134      case=size:upper/uplow;
135      cca=string;
136      ccb=string;
137      ccc=string;
138      ccd=string;
139      cce=string;
140      color=custom;
141      compression=compress:on/off;
142      cpi=integer "x>0" "%s must be greater than 0";
143      cprompt=string;
144      decimal=dec:dot/comma;
145      disk=custom;
146      dprompt=string;
147      echo=echo:on/off;
148      eject=eject:on/off;
149      endcmd=string "x==1" "one character long";
150      errorbreak=errbrk:on/off;
151      errors=errors:on/off/terminal/listing/both/none;
152      format=custom;
153      headers=headers:no/yes/blank;
154      helpwindows=helpwin:on/off;
155      highres=hires:on/off;
156      histogram=string "x==1" "one character long";
157      include=inc:on/off;
158      journal=custom;
159      length=custom;
160      listing=custom;
161      log=custom;
162      lowres=lores:auto/on/off;
163      lpi=integer "x>0" "%s must be greater than 0";
164      menus=menus:standard/extended;
165      messages=messages:on/off/terminal/listing/both/none;
166      mexpand=mexp:on/off;
167      miterate=integer "x>0" "%s must be greater than 0";
168      mnest=integer "x>0" "%s must be greater than 0";
169      more=more:on/off;
170      mprint=mprint:on/off;
171      mxerrs=integer "x >= 1" "%s must be at least 1";
172      mxloops=integer "x >=1" "%s must be at least 1";
173      mxmemory=integer;
174      mxwarns=integer;
175      nulline=null:on/off;
176      pager=custom;
177      printback=prtbck:on/off;
178      printer=prtr:on/off;
179      prompt=string;
180      ptranslate=ptrans:on/off;
181      rcolor=custom;
182      results=custom;
183      runreview=runrev:auto/manual;
184      safer=safe:on;
185      scompression=scompress:on/off;
186      screen=scrn:on/off;
187      scripttab=string "x==1" "one character long";
188      seed=custom;
189      tb1=string "x==3 || x==11" "3 or 11 characters long";
190      tbfonts=string;
191      undefined=undef:warn/nowarn;
192      viewlength=custom;
193      viewwidth=custom;
194      width=custom;
195      workdev=custom;
196      workspace=integer "x>=1024" "%s must be at least 1 MB";
197      xsort=xsort:yes/no.
198 */
199
200 /* (declarations) */
201
202 /* (_functions) */
203
204 static int
205 aux_stc_custom_blanks(struct cmd_set *cmd UNUSED)
206 {
207   if ( set_blanks == SYSMIS ) 
208     msg(MM, "SYSMIS");
209   else
210     msg(MM, "%g", set_blanks);
211   return 0;
212 }
213
214
215 static int
216 aux_stc_custom_color(struct cmd_set *cmd UNUSED)
217 {
218   msg (MW, _("%s is obsolete."),"COLOR");
219   return 0;
220 }
221
222 static int
223 aux_stc_custom_listing(struct cmd_set *cmd UNUSED)
224 {
225   if ( set_listing ) 
226     msg(MM, _("LISTING is ON"));
227   else
228     msg(MM, _("LISTING is OFF"));
229
230   return 0;
231 }
232
233 static int
234 aux_stc_custom_disk(struct cmd_set *cmd UNUSED)
235 {
236   return aux_stc_custom_listing(cmd);
237 }
238
239 static int
240 aux_stc_custom_format(struct cmd_set *cmd UNUSED)
241 {
242   msg(MM, fmt_to_string(&set_format));
243   return 0;
244 }
245
246
247
248 static int
249 aux_stc_custom_journal(struct cmd_set *cmd UNUSED)
250 {
251   if (set_journaling) 
252     msg(MM, set_journal);
253   else
254     msg(MM, _("Journalling is off") );
255         
256   return 0;
257 }
258
259 static int
260 aux_stc_custom_length(struct cmd_set *cmd UNUSED)
261 {
262   msg(MM, "%d", set_viewlength);
263   return 0;
264 }
265
266 static int
267 aux_stc_custom_log(struct cmd_set *cmd )
268 {
269   return aux_stc_custom_journal (cmd);
270 }
271
272 static int
273 aux_stc_custom_pager(struct cmd_set *cmd UNUSED)
274 {
275 #if !USE_INTERNAL_PAGER 
276   if ( set_pager ) 
277     msg(MM, set_pager);
278   else
279     msg(MM, "No pager");
280 #else /* USE_INTERNAL_PAGER */
281   msg (MM, "Internal pager.");
282 #endif /* USE_INTERNAL_PAGER */
283
284   return 0;
285 }
286
287 static int
288 aux_stc_custom_rcolor(struct cmd_set *cmd UNUSED)
289 {
290   msg (SW, _("%s is obsolete."),"RCOLOR");
291   return 0;
292 }
293
294 static int
295 aux_stc_custom_results(struct cmd_set *cmd UNUSED)
296 {
297   
298   msg(MM, route_to_string(set_results) );
299
300   return 0;
301 }
302
303 static int
304 aux_stc_custom_seed(struct cmd_set *cmd UNUSED)
305 {
306   return 0;
307 }
308
309 static int
310 aux_stc_custom_viewlength(struct cmd_set *cmd UNUSED)
311 {
312   msg(MM, "%d", set_viewlength);
313   return 0;
314 }
315
316 static int
317 aux_stc_custom_viewwidth(struct cmd_set *cmd UNUSED)
318 {
319   msg(MM, "%d", set_viewwidth);
320   return 0;
321 }
322
323 static int
324 aux_stc_custom_width(struct cmd_set *cmd UNUSED)
325 {
326   msg(MM, "%d", set_viewwidth);
327   return 0;
328 }
329
330 static int
331 aux_stc_custom_workdev(struct cmd_set *cmd UNUSED)
332 {
333   msg (SW, _("%s is obsolete."),"WORKDEV");
334   return 0;
335 }
336
337
338
339 /* (aux_functions) 
340      warranty=show_warranty;
341      copying=show_copying.
342 */
343
344
345 static struct cmd_set cmd;
346
347 int
348 cmd_show (void)
349 {
350   lex_match_id ("SHOW");
351
352   if (!aux_parse_set (&cmd))
353     return CMD_FAILURE;
354
355   return CMD_SUCCESS;
356 }
357
358 int
359 cmd_set (void)
360 {
361
362   if (!parse_set (&cmd))
363     return CMD_FAILURE;
364
365   if (cmd.sbc_cca)
366     set_ccx (cmd.s_cca, &set_cc[0], 'A');
367   if (cmd.sbc_ccb)
368     set_ccx (cmd.s_ccb, &set_cc[1], 'B');
369   if (cmd.sbc_ccc)
370     set_ccx (cmd.s_ccc, &set_cc[2], 'C');
371   if (cmd.sbc_ccd)
372     set_ccx (cmd.s_ccd, &set_cc[3], 'D');
373   if (cmd.sbc_cce)
374     set_ccx (cmd.s_cce, &set_cc[4], 'E');
375
376   if (cmd.sbc_errors)
377     set_routing (cmd.errors, &set_errors);
378   if (cmd.sbc_messages)
379     set_routing (cmd.messages, &set_messages);
380
381   /* PC+ compatible syntax. */
382   if (cmd.sbc_screen)
383     outp_enable_device (cmd.scrn == STC_OFF ? 0 : 1, OUTP_DEV_SCREEN);
384   if (cmd.sbc_printer)
385     outp_enable_device (cmd.prtr == STC_OFF ? 0 : 1, OUTP_DEV_PRINTER);
386
387   if (cmd.sbc_automenu )
388     msg (SW, _("%s is obsolete."),"AUTOMENU");
389   if (cmd.sbc_beep )
390     msg (SW, _("%s is obsolete."),"BEEP");
391   if (cmd.sbc_block)
392     msg (SW, _("%s is obsolete."),"BLOCK");
393   if (cmd.sbc_boxstring)
394     msg (SW, _("%s is obsolete."),"BOXSTRING");
395   if (cmd.sbc_eject )
396     msg (SW, _("%s is obsolete."),"EJECT");
397   if (cmd.sbc_helpwindows )
398     msg (SW, _("%s is obsolete."),"HELPWINDOWS");
399   if (cmd.sbc_histogram)
400     msg (MW, _("%s is obsolete."),"HISTOGRAM");
401   if (cmd.sbc_menus )
402     msg (MW, _("%s is obsolete."),"MENUS");
403   if (cmd.sbc_ptranslate )
404     msg (SW, _("%s is obsolete."),"PTRANSLATE");
405   if (cmd.sbc_runreview )
406     msg (SW, _("%s is obsolete."),"RUNREVIEW");
407   if (cmd.sbc_xsort )
408     msg (SW, _("%s is obsolete."),"XSORT");
409   if (cmd.sbc_mxmemory )
410     msg (SE, _("%s is obsolete."),"MXMEMORY");
411   if (cmd.sbc_scripttab)
412     msg (SE, _("%s is obsolete."),"SCRIPTTAB");
413
414   if (cmd.sbc_tbfonts)
415     msg (SW, _("%s is not yet implemented."),"TBFONTS");
416   if (cmd.sbc_tb1 && cmd.s_tb1)
417     msg (SW, _("%s is not yet implemented."),"TB1");
418
419   /* Windows compatible syntax. */
420   if (cmd.sbc_case)
421     msg (SW, _("CASE is not implemented and probably won't be.  "
422         "If you care, complain about it."));
423
424   if (cmd.sbc_compression)
425     {
426       msg (MW, _("Active file compression is not yet implemented "
427                  "(and probably won't be)."));
428     }
429
430   return CMD_SUCCESS;
431 }
432
433 /* Sets custom currency specifier CC having name CC_NAME ('A' through
434    'E') to correspond to the settings in CC_STRING. */
435 static int
436 set_ccx (const char *cc_string, struct set_cust_currency * cc, int cc_name)
437 {
438   if (strlen (cc_string) > 16)
439     {
440       msg (SE, _("CC%c: Length of custom currency string `%s' (%d) "
441                  "exceeds maximum length of 16."),
442            cc_name, cc_string, strlen (cc_string));
443       return 0;
444     }
445
446   /* Determine separators. */
447   {
448     const char *sp;
449     int n_commas, n_periods;
450   
451     /* Count the number of commas and periods.  There must be exactly
452        three of one or the other. */
453     n_commas = n_periods = 0;
454     for (sp = cc_string; *sp; sp++)
455       if (*sp == ',')
456         n_commas++;
457       else if (*sp == '.')
458         n_periods++;
459   
460     if (!((n_commas == 3) ^ (n_periods == 3)))
461       {
462         msg (SE, _("CC%c: Custom currency string `%s' does not contain "
463                    "exactly three periods or commas (not both)."),
464              cc_name, cc_string);
465         return 0;
466       }
467     else if (n_commas == 3)
468       {
469         cc->decimal = '.';
470         cc->grouping = ',';
471       }
472     else
473       {
474         cc->decimal = ',';
475         cc->grouping = '.';
476       }
477   }
478   
479   /* Copy cc_string to cc, changing separators to nulls. */
480   {
481     char *cp;
482     
483     strcpy (cc->buf, cc_string);
484     cp = cc->neg_prefix = cc->buf;
485
486     while (*cp++ != cc->grouping)
487       ;
488     cp[-1] = '\0';
489     cc->prefix = cp;
490
491     while (*cp++ != cc->grouping)
492       ;
493     cp[-1] = '\0';
494     cc->suffix = cp;
495
496     while (*cp++ != cc->grouping)
497       ;
498     cp[-1] = '\0';
499     cc->neg_suffix = cp;
500   }
501   
502   return 1;
503 }
504
505
506 const char *
507 route_to_string(int routing)
508 {
509   static char s[255];
510   
511   s[0]='\0';
512
513   if ( routing == 0 )
514     {
515       strcpy(s, _("None"));
516       return s;
517     }
518
519   if (routing & SET_ROUTE_DISABLE ) 
520     {
521     strcpy(s, _("Disabled") );
522     return s;
523     }
524
525   if (routing & SET_ROUTE_SCREEN)
526     strcat(s, _("Screen") );
527   
528   if (routing & SET_ROUTE_LISTING)
529     {
530       if(s[0] != '\0') 
531         strcat(s,", ");
532         
533       strcat(s, _("Listing") );
534     }
535
536   if (routing & SET_ROUTE_OTHER)
537     {
538       if(s[0] != '\0') 
539         strcat(s,", ");
540       strcat(s, _("Other") );
541     }
542  
543     
544   return s;
545   
546     
547 }
548
549 /* Sets *SETTING, which is a combination of SET_ROUTE_* bits that
550    indicates what to do with some sort of output, to the value
551    indicated by Q, which is a value provided by the input parser. */
552 static void
553 set_routing (int q, int *setting)
554 {
555   switch (q)
556     {
557     case STC_OFF:
558       *setting |= SET_ROUTE_DISABLE;
559       break;
560     case STC_ON:
561       *setting &= ~SET_ROUTE_DISABLE;
562       break;
563     case STC_TERMINAL:
564       *setting &= ~(SET_ROUTE_LISTING | SET_ROUTE_OTHER);
565       *setting |= SET_ROUTE_SCREEN;
566       break;
567     case STC_LISTING:
568       *setting &= ~SET_ROUTE_SCREEN;
569       *setting |= SET_ROUTE_LISTING | SET_ROUTE_OTHER;
570       break;
571     case STC_BOTH:
572       *setting |= SET_ROUTE_SCREEN | SET_ROUTE_LISTING | SET_ROUTE_OTHER;
573       break;
574     case STC_NONE:
575       *setting &= ~(SET_ROUTE_SCREEN | SET_ROUTE_LISTING | SET_ROUTE_OTHER);
576       break;
577     default:
578       assert (0);
579     }
580 }
581
582 static int
583 stc_custom_pager (struct cmd_set *cmd UNUSED)
584 {
585   lex_match ('=');
586 #if !USE_INTERNAL_PAGER
587   if (lex_match_id ("OFF"))
588     {
589       if (set_pager)
590         free (set_pager);
591       set_pager = NULL;
592     }
593   else
594     {
595       if (!lex_force_string ())
596         return 0;
597       if (set_pager)
598         free (set_pager);
599       set_pager = xstrdup (ds_c_str (&tokstr));
600       lex_get ();
601     }
602   return 1;
603 #else /* USE_INTERNAL_PAGER */
604   if (match_id (OFF))
605     return 1;
606   msg (SW, "External pagers not supported.");
607   return 0;
608 #endif /* USE_INTERNAL_PAGER */
609 }
610
611 /* Parses the BLANKS subcommand, which controls the value that
612    completely blank fields in numeric data imply.  X, Wnd: Syntax is
613    SYSMIS or a numeric value; PC+: Syntax is '.', which is equivalent
614    to SYSMIS, or a numeric value. */
615 static int
616 stc_custom_blanks (struct cmd_set *cmd UNUSED)
617 {
618   lex_match ('=');
619   if ((token == T_ID && lex_id_match ("SYSMIS", tokid))
620       || (token == T_STRING && !strcmp (tokid, ".")))
621     {
622       lex_get ();
623       set_blanks = SYSMIS;
624     }
625   else
626     {
627       if (!lex_force_num ())
628         return 0;
629       set_blanks = tokval;
630       lex_get ();
631     }
632   return 1;
633 }
634
635 static int
636 stc_custom_length (struct cmd_set *cmd UNUSED)
637 {
638   int page_length;
639
640   lex_match ('=');
641   if (lex_match_id ("NONE"))
642     page_length = -1;
643   else
644     {
645       if (!lex_force_int ())
646         return 0;
647       if (lex_integer () < 1)
648         {
649           msg (SE, _("LENGTH must be at least 1."));
650           return 0;
651         }
652       page_length = lex_integer ();
653       lex_get ();
654     }
655
656   if ( page_length != -1 ) 
657     set_viewlength = page_length;
658
659   return 1;
660 }
661
662 static int
663 stc_custom_results (struct cmd_set *cmd UNUSED)
664 {
665   struct tuple
666     {   
667       const char *s;    
668       int v;
669     };
670
671   static struct tuple tab[] =
672     {
673       {"ON", STC_ON},
674       {"OFF", STC_OFF},
675       {"TERMINAL", STC_TERMINAL},
676       {"LISTING", STC_LISTING},
677       {"BOTH", STC_BOTH},
678       {"NONE", STC_NONE},
679       {NULL, 0},
680     };
681
682   struct tuple *t;
683
684   lex_match ('=');
685
686   if (token != T_ID)
687     {
688       msg (SE, _("Missing identifier in RESULTS subcommand."));
689       return 0;
690     }
691   
692   for (t = tab; t->s; t++)
693     if (lex_id_match (t->s, tokid))
694       {
695         lex_get ();
696         set_routing (t->v, &set_results);
697         return 1;
698       }
699   msg (SE, _("Unrecognized identifier in RESULTS subcommand."));
700   return 0;
701 }
702
703 static int
704 stc_custom_seed (struct cmd_set *cmd UNUSED)
705 {
706   lex_match ('=');
707   if (lex_match_id ("RANDOM"))
708     set_rng (random_seed ());
709   else
710     {
711       if (!lex_force_num ())
712         return 0;
713       set_rng (tokval);
714       lex_get ();
715     }
716
717   return 1;
718 }
719
720 static int
721 stc_custom_width (struct cmd_set *cmd UNUSED)
722 {
723   int page_width;
724
725   lex_match ('=');
726   if (lex_match_id ("NARROW"))
727     page_width = 79;
728   else if (lex_match_id ("WIDE"))
729     page_width = 131;
730   else
731     {
732       if (!lex_force_int ())
733         return 0;
734       if (lex_integer () < 1)
735         {
736           msg (SE, _("WIDTH must be at least 1."));
737           return 0;
738         }
739       page_width = lex_integer ();
740       lex_get ();
741     }
742
743   set_viewwidth = page_width;
744   return 1;
745 }
746
747 /* Parses FORMAT subcommand, which consists of a numeric format
748    specifier. */
749 static int
750 stc_custom_format (struct cmd_set *cmd UNUSED)
751 {
752   struct fmt_spec fmt;
753
754   lex_match ('=');
755   if (!parse_format_specifier (&fmt, 0))
756     return 0;
757   if ((formats[fmt.type].cat & FCAT_STRING) != 0)
758     {
759       msg (SE, _("FORMAT requires numeric output format as an argument.  "
760                  "Specified format %s is of type string."),
761            fmt_to_string (&fmt));
762       return 0;
763     }
764
765   set_format = fmt;
766   return 1;
767 }
768
769 static int
770 stc_custom_journal (struct cmd_set *cmd UNUSED)
771 {
772   lex_match ('=');
773   if (lex_match_id ("ON"))
774     set_journaling = 1;
775   else if (lex_match_id ("OFF"))
776     set_journaling = 0;
777   if (token == T_STRING)
778     {
779       set_journal = xstrdup (ds_c_str (&tokstr));
780       lex_get ();
781     }
782   return 1;
783 }
784
785 /* Parses COLOR subcommand.  PC+: either ON or OFF or two or three
786    comma-delimited numbers inside parentheses. */
787 static int
788 stc_custom_color (struct cmd_set *cmd UNUSED)
789 {
790   msg (MW, _("%s is obsolete."),"COLOR");
791
792   lex_match ('=');
793   if (!lex_match_id ("ON") && !lex_match_id ("YES") && !lex_match_id ("OFF") && !lex_match_id ("NO"))
794     {
795       if (!lex_force_match ('('))
796         return 0;
797       if (!lex_match ('*'))
798         {
799           if (!lex_force_int ())
800             return 0;
801           if (lex_integer () < 0 || lex_integer () > 15)
802             {
803               msg (SE, _("Text color must be in range 0-15."));
804               return 0;
805             }
806           lex_get ();
807         }
808       if (!lex_force_match (','))
809         return 0;
810       if (!lex_match ('*'))
811         {
812           if (!lex_force_int ())
813             return 0;
814           if (lex_integer () < 0 || lex_integer () > 7)
815             {
816               msg (SE, _("Background color must be in range 0-7."));
817               return 0;
818             }
819           lex_get ();
820         }
821       if (lex_match (',') && !lex_match ('*'))
822         {
823           if (!lex_force_int ())
824             return 0;
825           if (lex_integer () < 0 || lex_integer () > 7)
826             {
827               msg (SE, _("Border color must be in range 0-7."));
828               return 0;
829             }
830           lex_get ();
831         }
832       if (!lex_force_match (')'))
833         return 0;
834     }
835   return 1;
836 }
837
838 static int
839 stc_custom_listing (struct cmd_set *cmd UNUSED)
840 {
841   lex_match ('=');
842   if (lex_match_id ("ON") || lex_match_id ("YES"))
843     set_listing = 1;
844   else if (lex_match_id ("OFF") || lex_match_id ("NO"))
845     set_listing = 0;
846   else
847     {
848       /* FIXME */
849       return 0;
850     }
851   outp_enable_device (set_listing, OUTP_DEV_LISTING);
852
853   return 1;
854 }
855
856 static int
857 stc_custom_disk (struct cmd_set *cmd UNUSED)
858 {
859   return stc_custom_listing (cmd);
860 }
861
862 static int
863 stc_custom_log (struct cmd_set *cmd UNUSED)
864
865   return stc_custom_journal (cmd);
866 }
867
868 static int
869 stc_custom_rcolor (struct cmd_set *cmd UNUSED)
870 {
871   msg (SW, _("%s is obsolete."),"RCOLOR");
872
873   lex_match ('=');
874   if (!lex_force_match ('('))
875     return 0;
876
877   if (!lex_match ('*'))
878     {
879       if (!lex_force_int ())
880         return 0;
881       if (lex_integer () < 0 || lex_integer () > 6)
882         {
883           msg (SE, _("Lower window color must be between 0 and 6."));
884           return 0;
885         }
886       lex_get ();
887     }
888   if (!lex_force_match (','))
889     return 0;
890
891   if (!lex_match ('*'))
892     {
893       if (!lex_force_int ())
894         return 0;
895       if (lex_integer () < 0 || lex_integer () > 6)
896         {
897           msg (SE, _("Upper window color must be between 0 and 6."));
898           return 0;
899         }
900       lex_get ();
901     }
902
903   if (lex_match (',') && !lex_match ('*'))
904     {
905       if (!lex_force_int ())
906         return 0;
907       if (lex_integer () < 0 || lex_integer () > 6)
908         {
909           msg (SE, _("Frame color must be between 0 and 6."));
910           return 0;
911         }
912       lex_get ();
913     }
914   return 1;
915 }
916
917 static int
918 stc_custom_viewwidth (struct cmd_set *cmd UNUSED)
919 {
920   lex_match ('=');
921
922   if ( !lex_force_int() ) 
923     return 0;
924
925   set_viewwidth = lex_integer();
926   lex_get();
927   
928   return 1;
929 }
930
931 static int
932 stc_custom_viewlength (struct cmd_set *cmd UNUSED)
933 {
934   if (lex_match_id ("MINIMUM"))
935     set_viewlength = 25;
936   else if (lex_match_id ("MEDIAN"))
937     set_viewlength = 43;        /* This is not correct for VGA displays. */
938   else if (lex_match_id ("MAXIMUM"))
939     set_viewlength = 43;
940   else
941     {
942       if (!lex_force_int ())
943         return 0;
944 #ifdef __MSDOS__
945       if (lex_integer () >= (43 + 25) / 2)
946         set_viewlength = 43;
947       else
948         set_viewlength = 25;
949 #else /* not dos */
950       set_viewlength = lex_integer ();
951 #endif /* not dos */
952       lex_get ();
953     }
954
955 #ifdef __MSDOS__
956   msg (SW, _("%s is not yet implemented."),"VIEWLENGTH");
957 #endif /* dos */
958   return 1;
959 }
960
961 static int
962 stc_custom_workdev (struct cmd_set *cmd UNUSED)
963 {
964   char c[2];
965
966   msg (SW, _("%s is obsolete."),"WORKDEV");
967
968   c[1] = 0;
969   for (*c = 'A'; *c <= 'Z'; (*c)++)
970     if (token == T_ID && lex_id_match (c, tokid))
971       {
972         lex_get ();
973         return 1;
974       }
975   msg (SE, _("Drive letter expected in WORKDEV subcommand."));
976   return 0;
977 }
978
979
980
981 static void 
982 set_viewport(int sig_num UNUSED)
983 {
984 #if HAVE_LIBTERMCAP
985   static char term_buffer[16384];
986 #endif
987
988   set_viewwidth = -1;
989   set_viewlength = -1;
990
991 #if __DJGPP__ || __BORLANDC__
992   {
993     struct text_info ti;
994
995     gettextinfo (&ti);
996     set_viewlength = max (ti.screenheight, 25);
997     set_viewwidth = max (ti.screenwidth, 79);
998   }
999 #elif HAVE_LIBTERMCAP
1000   {
1001     char *termtype;
1002     int success;
1003
1004     /* This code stolen from termcap.info, though modified. */
1005     termtype = getenv ("TERM");
1006     if (!termtype)
1007       msg (FE, _("Specify a terminal type with the TERM environment variable."));
1008
1009     success = tgetent (term_buffer, termtype);
1010     if (success <= 0)
1011       {
1012         if (success < 0)
1013           msg (IE, _("Could not access the termcap data base."));
1014         else
1015           msg (IE, _("Terminal type `%s' is not defined."), termtype);
1016       }
1017     else
1018       {
1019         /* NOTE: Do not rely upon tgetnum returning -1 if the value is 
1020            not available. It's supposed to do it, but not all platforms 
1021            do (eg Cygwin) .
1022         */
1023         if ( -1 != tgetnum("li")) 
1024           set_viewlength = tgetnum ("li");
1025
1026         if ( -1 != tgetnum("co")) 
1027           set_viewwidth = tgetnum ("co") - 1;
1028       }
1029   }
1030 #endif /* HAVE_LIBTERMCAP */
1031
1032   /* Try the environment variables */
1033   if ( -1 ==  set_viewwidth ) 
1034     { 
1035       char *s = getenv("COLUMNS");
1036       if ( s )  set_viewwidth = atoi(s);
1037     }
1038
1039   if ( -1 ==  set_viewwidth ) 
1040     {
1041       char *s = getenv("LINES");
1042       if ( s )  set_viewlength = atoi(s);
1043     }
1044
1045
1046   /* Last resort.  Use hard coded values */
1047   if ( 0  >  set_viewwidth ) set_viewwidth = 79;
1048   if ( 0  >  set_viewlength ) set_viewlength = 24;
1049
1050 }
1051
1052 /* Public functions */
1053
1054 void
1055 done_settings(void)
1056 {
1057   free(set_pager);
1058   free(set_journal);
1059
1060   free(cmd.s_endcmd);
1061   free(cmd.s_prompt);
1062   free(cmd.s_cprompt);
1063   free(cmd.s_dprompt);
1064 }
1065
1066
1067
1068 void
1069 init_settings(void)
1070 {
1071   cmd.s_dprompt = xstrdup (_("data> "));
1072   cmd.s_cprompt = xstrdup ("    > ");  
1073   cmd.s_prompt = xstrdup ("PSPP> ");
1074   cmd.s_endcmd = xstrdup (".");
1075
1076   assert(cmd.safe == 0 );
1077   cmd.safe = STC_OFF;
1078
1079   cmd.dec = STC_DOT;
1080   cmd.n_cpi[0] = 6;
1081   cmd.n_lpi[0] = 10;
1082   cmd.echo = STC_OFF;
1083   cmd.more = STC_ON;
1084   cmd.headers = STC_YES;
1085   cmd.errbrk = STC_OFF;
1086
1087   cmd.scompress = STC_OFF;
1088   cmd.undef = STC_WARN;
1089   cmd.mprint = STC_ON ;
1090   cmd.prtbck = STC_ON ;
1091   cmd.null = STC_ON ;
1092   cmd.inc = STC_ON ;
1093
1094   set_journal = xstrdup ("pspp.jnl");
1095   set_journaling = 1;
1096
1097   cmd.n_mxwarns[0] = 100;
1098   cmd.n_mxerrs[0] = 100;
1099   cmd.n_mxloops[0] = 1;
1100   cmd.n_workspace[0] = 4L * 1024 * 1024;
1101
1102
1103 #if !USE_INTERNAL_PAGER
1104   {
1105     char *pager;
1106
1107     pager = getenv ("STAT_PAGER");
1108     if (!pager)  set_pager = getenv ("PAGER");
1109
1110     if (pager)  
1111       set_pager = xstrdup (pager);
1112 #if DEFAULT_PAGER
1113     else
1114       set_pager = xstrdup (DEFAULT_PAGER);
1115 #endif /* DEFAULT_PAGER */
1116   }
1117 #endif /* !USE_INTERNAL_PAGER */
1118
1119
1120   {
1121     int i;
1122     
1123     for (i = 0; i < 5; i++)
1124       {
1125         struct set_cust_currency *cc = &set_cc[i];
1126         strcpy (cc->buf, "-");
1127         cc->neg_prefix = cc->buf;
1128         cc->prefix = &cc->buf[1];
1129         cc->suffix = &cc->buf[1];
1130         cc->neg_suffix = &cc->buf[1];
1131         cc->decimal = '.';
1132         cc->grouping = ',';
1133       }
1134   }
1135
1136   if ( ! long_view )
1137     {
1138       set_viewport (0);
1139       signal (SIGWINCH, set_viewport);
1140     }
1141
1142 }
1143
1144 void
1145 force_long_view(void)
1146 {
1147   long_view = 1;
1148   set_viewwidth=9999;
1149 }
1150
1151 int 
1152 safer_mode(void)
1153 {
1154   return !(cmd.safe != STC_ON) ;
1155 }
1156
1157
1158 /* Set safer mode */
1159 void
1160 make_safe(void)
1161 {
1162   cmd.safe = STC_ON;
1163 }
1164
1165
1166 char 
1167 get_decimal(void)
1168 {
1169   return (cmd.dec == STC_DOT ? '.' : ',');
1170 }
1171
1172
1173 char
1174 get_grouping(void)
1175 {
1176   return (cmd.dec == STC_DOT ? ',' : '.');
1177 }
1178  
1179
1180 char * 
1181 get_prompt(void)
1182 {
1183   return cmd.s_prompt;
1184 }
1185
1186 char * 
1187 get_dprompt(void)
1188 {
1189   return cmd.s_dprompt;
1190 }
1191
1192 char * 
1193 get_cprompt(void)
1194 {
1195   return cmd.s_cprompt;
1196 }
1197
1198
1199 int
1200 get_echo(void)
1201 {
1202     return (cmd.echo != STC_OFF );
1203 }
1204
1205
1206 int 
1207 get_errorbreak(void)
1208 {
1209   return (cmd.errbrk != STC_OFF);
1210 }
1211
1212
1213 int 
1214 get_scompression(void)
1215 {
1216   return (cmd.scompress != STC_OFF );
1217 }
1218
1219 int
1220 get_undefined(void)
1221 {
1222   return (cmd.undef != STC_NOWARN);
1223 }
1224
1225 int
1226 get_mxwarns(void)
1227 {  
1228   return cmd.n_mxwarns[0];
1229 }
1230
1231 int
1232 get_mxerrs(void)
1233 {
1234   return cmd.n_mxerrs[0];
1235 }
1236
1237 int
1238 get_mprint(void)
1239 {
1240   return ( cmd.mprint != STC_OFF );
1241 }
1242
1243 int
1244 get_printback(void)
1245 {
1246   return (cmd.prtbck != STC_OFF );
1247 }
1248
1249 int
1250 get_mxloops(void)
1251 {
1252   return cmd.n_mxloops[0];
1253 }
1254
1255 int
1256 get_nullline(void)
1257 {
1258   return (cmd.null != STC_OFF );
1259 }
1260
1261 int
1262 get_include(void)
1263 {
1264  return (cmd.inc != STC_OFF );
1265 }
1266
1267 unsigned char
1268 get_endcmd(void)
1269 {
1270   return cmd.s_endcmd[0];
1271 }
1272
1273
1274 size_t
1275 get_max_workspace(void)
1276 {
1277   return cmd.n_workspace[0];
1278 }
1279
1280 double
1281 get_blanks(void)
1282 {
1283   return set_blanks;
1284 }
1285
1286 struct fmt_spec 
1287 get_format(void)
1288
1289   return set_format;
1290 }
1291
1292 /* CCA through CCE. */
1293 const struct set_cust_currency *
1294 get_cc(int i)
1295 {
1296   return &set_cc[i];
1297 }
1298
1299 void
1300 aux_show_warranty(void)
1301 {
1302   msg(MM,lack_of_warranty);
1303 }
1304
1305 void
1306 aux_show_copying(void)
1307 {
1308   msg(MM,copyleft);
1309 }
1310
1311
1312 int
1313 get_viewlength(void)
1314 {
1315   return set_viewlength;
1316 }
1317
1318 int
1319 get_viewwidth(void)
1320 {
1321   return set_viewwidth;
1322 }
1323
1324 const char *
1325 get_pager(void)
1326 {
1327   return set_pager;
1328 }
1329
1330 gsl_rng *
1331 get_rng (void)
1332 {
1333   if (rng == NULL)
1334     set_rng (random_seed ());
1335   return rng;
1336 }
1337
1338 static void
1339 set_rng (unsigned long seed) 
1340 {
1341   rng = gsl_rng_alloc (gsl_rng_mt19937);
1342   if (rng == NULL)
1343     out_of_memory ();
1344   gsl_rng_set (rng, seed);
1345 }
1346
1347 static unsigned long
1348 random_seed (void) 
1349 {
1350   return time (0);
1351 }
1352
1353 static int global_algorithm = ENHANCED;
1354 static int cmd_algorithm = ENHANCED;
1355 static int *algorithm = &global_algorithm;
1356
1357 static int syntax = ENHANCED;
1358
1359 /* Set the algorithm option globally */
1360 void 
1361 set_algorithm(int x)
1362 {
1363   global_algorithm = x;
1364 }
1365
1366 /* Set the algorithm option for this command only */
1367 void 
1368 set_cmd_algorithm(int x)
1369 {
1370   cmd_algorithm = x; 
1371   algorithm = &cmd_algorithm;
1372 }
1373
1374 /* Unset the algorithm option for this command */
1375 void
1376 unset_cmd_algorithm(void)
1377 {
1378   algorithm = &global_algorithm;
1379 }
1380
1381 /* Return the current algorithm setting */
1382 int
1383 get_algorithm(void)
1384 {
1385   return *algorithm;
1386 }
1387
1388 /* Set the syntax option */
1389 void 
1390 set_syntax(int x)
1391 {
1392   syntax = x;
1393 }
1394
1395 /* Get the current syntax setting */
1396 int
1397 get_syntax(void)
1398 {
1399   return syntax;
1400 }
1401
1402
1403 /*
1404    Local Variables:
1405    mode: c
1406    End:
1407 */