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