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