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