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