Fixed a bug where the random number generator always returned zero.
[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.
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 <assert.h>
64 #include <stdio.h>
65 #include <errno.h>
66 #include <stdlib.h>
67 #include "alloc.h"
68 #include "command.h"
69 #include "lexer.h"
70 #include "error.h"
71 #include "magic.h"
72 #include "log.h"
73 #include "output.h"
74 #include "var.h"
75 #include "format.h"
76
77 double set_blanks;
78 int set_compression;
79 struct set_cust_currency set_cc[5];
80 int set_cpi;
81 char *set_cprompt;
82 int set_decimal;
83 int set_grouping;
84 char *set_dprompt;
85 int set_echo;
86 int set_endcmd;
87 int set_errorbreak;
88 int set_errors, set_messages, set_results;
89 struct fmt_spec set_format;
90 int set_headers;
91 int set_include;
92 char *set_journal;
93 int set_journaling;
94 int set_lpi;
95 int set_messages;
96 int set_mexpand;
97 int set_miterate;
98 int set_mnest;
99 int set_more;
100 int set_mprint;
101 int set_mxerrs;
102 int set_mxloops;
103 int set_mxwarns;
104 int set_nullline;
105 int set_printback;
106 int set_output = 1;
107 #if !USE_INTERNAL_PAGER
108 char *set_pager;
109 #endif /* !USE_INTERNAL_PAGER */
110 int set_printer;
111 char *set_prompt;
112 char *set_results_file;
113 int set_safer;
114 int set_scompression;
115 int set_screen;
116 long set_seed;
117 int set_testing_mode;
118 int set_undefined;
119 int set_viewlength;
120 int set_viewwidth;
121
122 static void set_routing (int q, int *setting);
123 static int set_ccx (const char *cc_string, struct set_cust_currency * cc,
124                     int cc_name);
125
126 /* (specification)
127    "SET" (stc_):
128      automenu=automenu:on/off;
129      beep=beep:on/off;
130      blanks=custom;
131      block=string "x==1" "one character long";
132      boxstring=string "x==3 || x==11" "3 or 11 characters long";
133      case=size:upper/uplow;
134      cca=string;
135      ccb=string;
136      ccc=string;
137      ccd=string;
138      cce=string;
139      color=custom;
140      compression=compress:on/off;
141      cpi=integer;
142      cprompt=string;
143      decimal=dec:dot/_comma;
144      disk=custom;
145      dprompt=string;
146      echo=echo:on/off;
147      eject=eject:on/off;
148      endcmd=string "x==1" "one character long";
149      errorbreak=errbrk:on/off;
150      errors=errors:on/off/terminal/listing/both/none;
151      format=custom;
152      headers=headers:no/yes/blank;
153      helpwindows=helpwin:on/off;
154      highres=hires:on/off;
155      histogram=string "x==1" "one character long";
156      include=inc:on/off;
157      journal=custom;
158      length=custom;
159      listing=custom;
160      log=custom;
161      lowres=lores:auto/on/off;
162      lpi=integer;
163      menus=menus:standard/extended;
164      messages=messages:on/off/terminal/listing/both/none;
165      mexpand=mexp:on/off;
166      miterate=integer;
167      mnest=integer;
168      more=more:on/off;
169      mprint=mprint:on/off;
170      mxerrs=integer;
171      mxloops=integer;
172      mxmemory=integer;
173      mxwarns=integer;
174      nulline=null:on/off;
175      pager=custom;
176      printback=prtbck:on/off;
177      printer=prtr:on/off;
178      prompt=string;
179      ptranslate=ptrans:on/off;
180      rcolor=custom;
181      results=custom;
182      runreview=runrev:auto/manual;
183      safer=safe:on;
184      scompression=scompress:on/off;
185      screen=scrn:on/off;
186      scripttab=string "x==1" "one character long";
187      seed=custom;
188      tb1=string "x==3 || x==11" "3 or 11 characters long";
189      tbfonts=string;
190      undefined=undef:warn/nowarn;
191      viewlength=custom;
192      viewwidth=integer;
193      width=custom;
194      workdev=custom;
195      workspace=integer;
196      xsort=xsort:yes/no.
197 */
198
199 /* (declarations) */
200 /* (functions) */
201
202 int internal_cmd_set (void);
203
204 int
205 cmd_set (void)
206 {
207   struct cmd_set cmd;
208
209   lex_match_id ("SET");
210
211   if (!parse_set (&cmd))
212     return CMD_FAILURE;
213
214   if (cmd.sbc_block)
215     msg (SW, _("%s is obsolete."),"BLOCK");
216
217   if (cmd.sbc_boxstring)
218     msg (SW, _("%s is obsolete."),"BOXSTRING");
219
220   if (cmd.compress != -1)
221     {
222       msg (MW, _("Active file compression is not yet implemented "
223                  "(and probably won't be)."));
224       set_compression = cmd.compress == STC_OFF ? 0 : 1;
225     }
226   if (cmd.scompress != -1)
227     set_scompression = cmd.scompress == STC_OFF ? 0 : 1;
228   if (cmd.n_cpi != NOT_LONG)
229     {
230       if (cmd.n_cpi <= 0)
231         msg (SE, _("CPI must be greater than 0."));
232       else
233         set_cpi = cmd.n_cpi;
234     }
235   if (cmd.sbc_histogram)
236     msg (MW, _("%s is obsolete."),"HISTOGRAM");
237   if (cmd.n_lpi != NOT_LONG)
238     {
239       if (cmd.n_lpi <= 0)
240         msg (SE, _("LPI must be greater than 0."));
241       else
242         set_lpi = cmd.n_lpi;
243     }
244   
245   /* Windows compatible syntax. */
246   if (cmd.sbc_case)
247     msg (SW, _("CASE is not implemented and probably won't be.  If you care, "
248                "complain about it."));
249   if (cmd.sbc_cca)
250     set_ccx (cmd.s_cca, &set_cc[0], 'A');
251   if (cmd.sbc_ccb)
252     set_ccx (cmd.s_ccb, &set_cc[1], 'B');
253   if (cmd.sbc_ccc)
254     set_ccx (cmd.s_ccc, &set_cc[2], 'C');
255   if (cmd.sbc_ccd)
256     set_ccx (cmd.s_ccd, &set_cc[3], 'D');
257   if (cmd.sbc_cce)
258     set_ccx (cmd.s_cce, &set_cc[4], 'E');
259   if (cmd.dec != -1)
260     {
261       set_decimal = cmd.dec == STC_DOT ? '.' : ',';
262       set_grouping = cmd.dec == STC_DOT ? ',' : '.';
263     }
264   if (cmd.errors != -1)
265     set_routing (cmd.errors, &set_errors);
266   if (cmd.headers != -1)
267     set_headers = cmd.headers == STC_NO ? 0 : (cmd.headers == STC_YES ? 1 : 2);
268   if (cmd.messages != -1)
269     set_routing (cmd.messages, &set_messages);
270   if (cmd.mexp != -1)
271     set_mexpand = cmd.mexp == STC_OFF ? 0 : 1;
272   if (cmd.n_miterate != NOT_LONG)
273     {
274       if (cmd.n_miterate > 0)
275         set_miterate = cmd.n_miterate;
276       else
277         msg (SE, _("Value for MITERATE (%ld) must be greater than 0."),
278              cmd.n_miterate);
279     }
280   if (cmd.n_mnest != NOT_LONG)
281     {
282       if (cmd.n_mnest > 0)
283         set_mnest = cmd.n_mnest;
284       else
285         msg (SE, _("Value for MNEST (%ld) must be greater than 0."),
286              cmd.n_mnest);
287     }
288   if (cmd.mprint != -1)
289     set_mprint = cmd.mprint == STC_OFF ? 0 : 1;
290   if (cmd.n_mxerrs != NOT_LONG)
291     {
292       if (set_mxerrs < 1)
293         msg (SE, _("MXERRS must be at least 1."));
294       else
295         set_mxerrs = cmd.n_mxerrs;
296     }
297   if (cmd.n_mxloops != NOT_LONG)
298     {
299       if (set_mxloops < 1)
300         msg (SE, _("MXLOOPS must be at least 1."));
301       else
302         set_mxloops = cmd.n_mxloops;
303     }
304   if (cmd.n_mxmemory != NOT_LONG)
305     msg (SE, _("%s is obsolete."),"MXMEMORY");
306   if (cmd.n_mxwarns != NOT_LONG)
307     set_mxwarns = cmd.n_mxwarns;
308   if (cmd.prtbck != -1)
309     set_printback = cmd.prtbck == STC_OFF ? 0 : 1;
310   if (cmd.s_scripttab)
311     msg (SE, _("%s is obsolete."),"SCRIPTTAB");
312   if (cmd.s_tbfonts)
313     msg (SW, _("TBFONTS not implemented."));
314   if (cmd.s_tb1)
315     msg (SW, _("TB1 not implemented."));
316   if (cmd.undef != -1)
317     set_undefined = cmd.undef == STC_NOWARN ? 0 : 1;
318   if (cmd.n_workspace != NOT_LONG)
319     msg (SE, _("%s is obsolete."),"WORKSPACE");
320
321   /* PC+ compatible syntax. */
322   if (cmd.scrn != -1)
323     outp_enable_device (cmd.scrn == STC_OFF ? 0 : 1, OUTP_DEV_SCREEN);
324
325   if (cmd.automenu != -1)
326     msg (SW, _("%s is obsolete."),"AUTOMENU");
327   if (cmd.beep != -1)
328     msg (SW, _("%s is obsolete."),"BEEP");
329
330   if (cmd.s_cprompt)
331     {
332       free (set_cprompt);
333       set_cprompt = cmd.s_cprompt;
334       cmd.s_cprompt = NULL;
335     }
336   if (cmd.s_dprompt)
337     {
338       free (set_dprompt);
339       set_dprompt = cmd.s_dprompt;
340       cmd.s_dprompt = NULL;
341     }
342   if (cmd.echo != -1)
343     set_echo = cmd.echo == STC_OFF ? 0 : 1;
344   if (cmd.s_endcmd)
345     set_endcmd = cmd.s_endcmd[0];
346   if (cmd.eject != -1)
347     msg (SW, _("%s is obsolete."),"EJECT");
348   if (cmd.errbrk != -1)
349     set_errorbreak = cmd.errbrk == STC_OFF ? 0 : 1;
350   if (cmd.helpwin != -1)
351     msg (SW, _("%s is obsolete."),"HELPWINDOWS");
352   if (cmd.inc != -1)
353     set_include = cmd.inc == STC_OFF ? 0 : 1;
354   if (cmd.menus != -1)
355     msg (MW, _("%s is obsolete."),"MENUS");
356   if (cmd.null != -1)
357     set_nullline = cmd.null == STC_OFF ? 0 : 1;
358   if (cmd.more != -1)
359     set_more = cmd.more == STC_OFF ? 0 : 1;
360   if (cmd.prtr != -1)
361     outp_enable_device (cmd.prtr == STC_OFF ? 0 : 1, OUTP_DEV_PRINTER);
362   if (cmd.s_prompt)
363     {
364       free (set_prompt);
365       set_prompt = cmd.s_prompt;
366       cmd.s_prompt = NULL;
367     }
368   if (cmd.ptrans != -1)
369     msg (SW, _("%s is obsolete."),"PTRANSLATE");
370   if (cmd.runrev != -1)
371     msg (SW, _("%s is obsolete."),"RUNREVIEW");
372   if (cmd.safe == STC_ON)
373     set_safer = 1;
374   if (cmd.xsort != -1)
375     msg (SW, _("%s is obsolete."),"XSORT");
376
377   free_set (&cmd);
378
379   return CMD_SUCCESS;
380 }
381
382 /* Sets custom currency specifier CC having name CC_NAME ('A' through
383    'E') to correspond to the settings in CC_STRING. */
384 static int
385 set_ccx (const char *cc_string, struct set_cust_currency * cc, int cc_name)
386 {
387   if (strlen (cc_string) > 16)
388     {
389       msg (SE, _("CC%c: Length of custom currency string `%s' (%d) "
390                  "exceeds maximum length of 16."),
391            cc_name, cc_string, strlen (cc_string));
392       return 0;
393     }
394
395   /* Determine separators. */
396   {
397     const char *sp;
398     int n_commas, n_periods;
399   
400     /* Count the number of commas and periods.  There must be exactly
401        three of one or the other. */
402     n_commas = n_periods = 0;
403     for (sp = cc_string; *sp; sp++)
404       if (*sp == ',')
405         n_commas++;
406       else if (*sp == '.')
407         n_periods++;
408   
409     if (!((n_commas == 3) ^ (n_periods == 3)))
410       {
411         msg (SE, _("CC%c: Custom currency string `%s' does not contain "
412                    "exactly three periods or commas (not both)."),
413              cc_name, cc_string);
414         return 0;
415       }
416     else if (n_commas == 3)
417       {
418         cc->decimal = '.';
419         cc->grouping = ',';
420       }
421     else
422       {
423         cc->decimal = ',';
424         cc->grouping = '.';
425       }
426   }
427   
428   /* Copy cc_string to cc, changing separators to nulls. */
429   {
430     char *cp;
431     
432     strcpy (cc->buf, cc_string);
433     cp = cc->neg_prefix = cc->buf;
434
435     while (*cp++ != cc->grouping)
436       ;
437     cp[-1] = '\0';
438     cc->prefix = cp;
439
440     while (*cp++ != cc->grouping)
441       ;
442     cp[-1] = '\0';
443     cc->suffix = cp;
444
445     while (*cp++ != cc->grouping)
446       ;
447     cp[-1] = '\0';
448     cc->neg_suffix = cp;
449   }
450   
451   return 1;
452 }
453
454 /* Sets *SETTING, which is a combination of SET_ROUTE_* bits that
455    indicates what to do with some sort of output, to the value
456    indicated by Q, which is a value provided by the input parser. */
457 static void
458 set_routing (int q, int *setting)
459 {
460   switch (q)
461     {
462     case STC_ON:
463       *setting |= SET_ROUTE_DISABLE;
464       break;
465     case STC_OFF:
466       *setting &= ~SET_ROUTE_DISABLE;
467       break;
468     case STC_TERMINAL:
469       *setting &= ~(SET_ROUTE_LISTING | SET_ROUTE_OTHER);
470       *setting |= SET_ROUTE_SCREEN;
471       break;
472     case STC_LISTING:
473       *setting &= ~SET_ROUTE_SCREEN;
474       *setting |= SET_ROUTE_LISTING | SET_ROUTE_OTHER;
475       break;
476     case STC_BOTH:
477       *setting |= SET_ROUTE_SCREEN | SET_ROUTE_LISTING | SET_ROUTE_OTHER;
478       break;
479     case STC_NONE:
480       *setting &= ~(SET_ROUTE_SCREEN | SET_ROUTE_LISTING | SET_ROUTE_OTHER);
481       break;
482     default:
483       assert (0);
484     }
485 }
486
487 static int
488 stc_custom_pager (struct cmd_set *cmd unused)
489 {
490   lex_match ('=');
491 #if !USE_INTERNAL_PAGER
492   if (lex_match_id ("OFF"))
493     {
494       if (set_pager)
495         free (set_pager);
496       set_pager = NULL;
497     }
498   else
499     {
500       if (!lex_force_string ())
501         return 0;
502       if (set_pager)
503         free (set_pager);
504       set_pager = xstrdup (ds_value (&tokstr));
505       lex_get ();
506     }
507   return 1;
508 #else /* USE_INTERNAL_PAGER */
509   if (match_id (OFF))
510     return 1;
511   msg (SW, "External pagers not supported.");
512   return 0;
513 #endif /* USE_INTERNAL_PAGER */
514 }
515
516 /* Parses the BLANKS subcommand, which controls the value that
517    completely blank fields in numeric data imply.  X, Wnd: Syntax is
518    SYSMIS or a numeric value; PC+: Syntax is '.', which is equivalent
519    to SYSMIS, or a numeric value. */
520 static int
521 stc_custom_blanks (struct cmd_set *cmd unused)
522 {
523   lex_match ('=');
524   if ((token == T_ID && lex_id_match ("SYSMIS", tokid))
525       || (token == T_STRING && !strcmp (tokid, ".")))
526     {
527       lex_get ();
528       set_blanks = SYSMIS;
529     }
530   else
531     {
532       if (!lex_force_num ())
533         return 0;
534       set_blanks = tokval;
535       lex_get ();
536     }
537   return 1;
538 }
539
540 static int
541 stc_custom_length (struct cmd_set *cmd unused)
542 {
543   int page_length;
544
545   lex_match ('=');
546   if (lex_match_id ("NONE"))
547     page_length = -1;
548   else
549     {
550       if (!lex_force_int ())
551         return 0;
552       if (lex_integer () < 1)
553         {
554           msg (SE, _("LENGTH must be at least 1."));
555           return 0;
556         }
557       page_length = lex_integer ();
558       lex_get ();
559     }
560
561   /* FIXME: Set page length. */
562   return 1;
563 }
564
565 static int
566 stc_custom_results (struct cmd_set *cmd unused)
567 {
568   struct tuple
569     {   
570       const char *s;    
571       int v;
572     };
573
574   static struct tuple tab[] =
575     {
576       {"ON", STC_ON},
577       {"OFF", STC_OFF},
578       {"TERMINAL", STC_TERMINAL},
579       {"LISTING", STC_LISTING},
580       {"BOTH", STC_BOTH},
581       {"NONE", STC_NONE},
582       {NULL, 0},
583     };
584
585   struct tuple *t;
586
587   lex_match ('=');
588
589   if (token != T_ID)
590     {
591       msg (SE, _("Missing identifier in RESULTS subcommand."));
592       return 0;
593     }
594   
595   for (t = tab; t->s; t++)
596     if (lex_id_match (t->s, tokid))
597       {
598         lex_get ();
599         set_routing (t->v, &set_results);
600         return 1;
601       }
602   msg (SE, _("Unrecognized identifier in RESULTS subcommand."));
603   return 0;
604 }
605
606 static int
607 stc_custom_seed (struct cmd_set *cmd unused)
608 {
609   lex_match ('=');
610   if (lex_match_id ("RANDOM"))
611     set_seed = NOT_LONG;
612   else
613     {
614       if (!lex_force_num ())
615         return 0;
616       set_seed = tokval;
617       lex_get ();
618     }
619   return 1;
620 }
621
622 static int
623 stc_custom_width (struct cmd_set *cmd unused)
624 {
625   int page_width;
626
627   lex_match ('=');
628   if (lex_match_id ("NARROW"))
629     page_width = 79;
630   else if (lex_match_id ("WIDE"))
631     page_width = 131;
632   else
633     {
634       if (!lex_force_int ())
635         return 0;
636       if (lex_integer () < 1)
637         {
638           msg (SE, _("WIDTH must be at least 1."));
639           return 0;
640         }
641       page_width = lex_integer ();
642       lex_get ();
643     }
644
645   /* FIXME: Set page width. */
646   return 1;
647 }
648
649 /* Parses FORMAT subcommand, which consists of a numeric format
650    specifier. */
651 static int
652 stc_custom_format (struct cmd_set *cmd unused)
653 {
654   struct fmt_spec fmt;
655
656   lex_match ('=');
657   if (!parse_format_specifier (&fmt, 0))
658     return 0;
659   if ((formats[fmt.type].cat & FCAT_STRING) != 0)
660     {
661       msg (SE, _("FORMAT requires numeric output format as an argument.  "
662                  "Specified format %s is of type string."),
663            fmt_to_string (&fmt));
664       return 0;
665     }
666
667   set_format = fmt;
668   return 1;
669 }
670
671 static int
672 stc_custom_journal (struct cmd_set *cmd unused)
673 {
674   lex_match ('=');
675   if (lex_match_id ("ON"))
676     set_journaling = 1;
677   else if (lex_match_id ("OFF"))
678     set_journaling = 0;
679   if (token == T_STRING)
680     {
681       set_journal = xstrdup (ds_value (&tokstr));
682       lex_get ();
683     }
684   return 1;
685 }
686
687 /* Parses COLOR subcommand.  PC+: either ON or OFF or two or three
688    comma-delimited numbers inside parentheses. */
689 static int
690 stc_custom_color (struct cmd_set *cmd unused)
691 {
692   msg (MW, _("%s is obsolete."),"COLOR");
693
694   lex_match ('=');
695   if (!lex_match_id ("ON") && !lex_match_id ("YES") && !lex_match_id ("OFF") && !lex_match_id ("NO"))
696     {
697       if (!lex_force_match ('('))
698         return 0;
699       if (!lex_match ('*'))
700         {
701           if (!lex_force_int ())
702             return 0;
703           if (lex_integer () < 0 || lex_integer () > 15)
704             {
705               msg (SE, _("Text color must be in range 0-15."));
706               return 0;
707             }
708           lex_get ();
709         }
710       if (!lex_force_match (','))
711         return 0;
712       if (!lex_match ('*'))
713         {
714           if (!lex_force_int ())
715             return 0;
716           if (lex_integer () < 0 || lex_integer () > 7)
717             {
718               msg (SE, _("Background color must be in range 0-7."));
719               return 0;
720             }
721           lex_get ();
722         }
723       if (lex_match (',') && !lex_match ('*'))
724         {
725           if (!lex_force_int ())
726             return 0;
727           if (lex_integer () < 0 || lex_integer () > 7)
728             {
729               msg (SE, _("Border color must be in range 0-7."));
730               return 0;
731             }
732           lex_get ();
733         }
734       if (!lex_force_match (')'))
735         return 0;
736     }
737   return 1;
738 }
739
740 static int
741 stc_custom_listing (struct cmd_set *cmd unused)
742 {
743   lex_match ('=');
744   if (lex_match_id ("ON") || lex_match_id ("YES"))
745     outp_enable_device (1, OUTP_DEV_LISTING);
746   else if (lex_match_id ("OFF") || lex_match_id ("NO"))
747     outp_enable_device (0, OUTP_DEV_LISTING);
748   else
749     {
750       /* FIXME */
751     }
752
753   return 0;
754 }
755
756 static int
757 stc_custom_disk (struct cmd_set *cmd unused)
758 {
759   stc_custom_listing (cmd);
760   return 0;
761 }
762
763 static int
764 stc_custom_log (struct cmd_set *cmd unused)
765
766   stc_custom_journal (cmd);
767   return 0;
768 }
769
770 static int
771 stc_custom_rcolor (struct cmd_set *cmd unused)
772 {
773   msg (SW, _("%s is obsolete."),"RCOLOR");
774
775   lex_match ('=');
776   if (!lex_force_match ('('))
777     return 0;
778
779   if (!lex_match ('*'))
780     {
781       if (!lex_force_int ())
782         return 0;
783       if (lex_integer () < 0 || lex_integer () > 6)
784         {
785           msg (SE, _("Lower window color must be between 0 and 6."));
786           return 0;
787         }
788       lex_get ();
789     }
790   if (!lex_force_match (','))
791     return 0;
792
793   if (!lex_match ('*'))
794     {
795       if (!lex_force_int ())
796         return 0;
797       if (lex_integer () < 0 || lex_integer () > 6)
798         {
799           msg (SE, _("Upper window color must be between 0 and 6."));
800           return 0;
801         }
802       lex_get ();
803     }
804
805   if (lex_match (',') && !lex_match ('*'))
806     {
807       if (!lex_force_int ())
808         return 0;
809       if (lex_integer () < 0 || lex_integer () > 6)
810         {
811           msg (SE, _("Frame color must be between 0 and 6."));
812           return 0;
813         }
814       lex_get ();
815     }
816   return 1;
817 }
818
819 static int
820 stc_custom_viewlength (struct cmd_set *cmd unused)
821 {
822   if (lex_match_id ("MINIMUM"))
823     set_viewlength = 25;
824   else if (lex_match_id ("MEDIAN"))
825     set_viewlength = 43;        /* This is not correct for VGA displays. */
826   else if (lex_match_id ("MAXIMUM"))
827     set_viewlength = 43;
828   else
829     {
830       if (!lex_force_int ())
831         return 0;
832 #if __MSDOS__
833       if (lex_integer () >= (43 + 25) / 2)
834         set_viewlength = 43;
835       else
836         set_viewlength = 25;
837 #else /* not dos */
838       set_viewlength = lex_integer ();
839 #endif /* not dos */
840       lex_get ();
841     }
842
843 #if __MSDOS__
844   msg (SW, _("VIEWLENGTH not implemented."));
845 #endif /* dos */
846   return 1;
847 }
848
849 static int
850 stc_custom_workdev (struct cmd_set *cmd unused)
851 {
852   char c[2];
853
854   msg (SW, _("%s is obsolete."),"WORKDEV");
855
856   c[1] = 0;
857   for (*c = 'A'; *c <= 'Z'; (*c)++)
858     if (token == T_ID && lex_id_match (c, tokid))
859       {
860         lex_get ();
861         return 1;
862       }
863   msg (SE, _("Drive letter expected in WORKDEV subcommand."));
864   return 0;
865 }
866
867 /*
868    Local Variables:
869    mode: c
870    End:
871 */