Added src/ui/terminal/automake.mk and src/ui/gui/automake.mk
[pspp] / src / set.q
index 89d7ce2c494fb05a4f07b19793ded7a20e86bf29..8df84654710ac041d282ceecf59bb5ea6b653e0f 100644 (file)
--- a/src/set.q
+++ b/src/set.q
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
 
-/*
-   Categories of SET subcommands:
-
-   data input: BLANKS, DECIMAL, FORMAT.
-   
-   program input: ENDCMD, NULLINE.
-   
-   interaction: CPROMPT, DPROMPT, ERRORBREAK, MXERRS, MXWARNS, PROMPT.
-   
-   program execution: MEXPAND, MITERATE, MNEST, MPRINT,
-   MXLOOPS, SEED, UNDEFINED.
-
-   data output: CCA...CCE, DECIMAL, FORMAT, RESULTS-p.
-
-   output routing: ECHO, ERRORS, INCLUDE, MESSAGES, PRINTBACK, ERRORS,
-   RESULTS-rw.
-
-   output activation: LISTING (on/off), SCREEN, PRINTER.
-
-   output driver options: HEADERS, MORE, PAGER, VIEWLENGTH, VIEWWIDTH,
-   LISTING (filename).
-
-   logging: LOG, JOURNAL.
-
-   system files: COMP/COMPRESSION, SCOMP/SCOMPRESSION.
-
-   security: SAFER.
-*/
-
-/*
-   FIXME
-
-   These subcommands remain to be implemented:
-     ECHO, PRINTBACK, INCLUDE
-     MORE, PAGER, VIEWLENGTH, VIEWWIDTH, HEADERS
-
-   These subcommands are not complete:
-     MESSAGES, ERRORS, RESULTS
-     LISTING/DISK, LOG/JOURNAL
-*/     
-   
 #include <config.h>
 #include "settings.h"
-#include <assert.h>
+#include "error.h"
 #include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <time.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
-#include "log.h"
 #include "output.h"
+#include "random.h"
 #include "var.h"
 #include "format.h"
+#include "copyleft.h"
+#include "var.h"
+
+
+#if HAVE_LIBTERMCAP
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#else /* !HAVE_TERMCAP_H */
+int tgetent (char *, const char *);
+int tgetnum (const char *);
+#endif /* !HAVE_TERMCAP_H */
+#endif /* !HAVE_LIBTERMCAP */
 
-double set_blanks;
-int set_compression;
-struct set_cust_currency set_cc[5];
-int set_cpi;
-char *set_cprompt;
-int set_decimal;
-int set_grouping;
-char *set_dprompt;
-int set_echo;
-int set_endcmd;
-int set_errorbreak;
-int set_errors, set_messages, set_results;
-struct fmt_spec set_format;
-int set_headers;
-int set_include;
-char *set_journal;
-int set_journaling;
-int set_lpi;
-int set_messages;
-int set_mexpand;
-int set_miterate;
-int set_mnest;
-int set_more;
-int set_mprint;
-int set_mxerrs;
-int set_mxloops;
-int set_mxwarns;
-int set_nullline;
-int set_printback;
-int set_output = 1;
-#if !USE_INTERNAL_PAGER
-char *set_pager;
-#endif /* !USE_INTERNAL_PAGER */
-int set_printer;
-char *set_prompt;
-char *set_results_file;
-int set_safer;
-int set_scompression;
-int set_screen;
-long set_seed;
-int set_seed_used;
-int set_testing_mode;
-int set_undefined;
-int set_viewlength;
-int set_viewwidth;
-
-static void set_routing (int q, int *setting);
-static int set_ccx (const char *cc_string, struct set_cust_currency * cc,
-                   int cc_name);
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 
 /* (specification)
    "SET" (stc_):
-     automenu=automenu:on/off;
-     beep=beep:on/off;
      blanks=custom;
      block=string "x==1" "one character long";
      boxstring=string "x==3 || x==11" "3 or 11 characters long";
@@ -137,409 +61,277 @@ static int set_ccx (const char *cc_string, struct set_cust_currency * cc,
      ccc=string;
      ccd=string;
      cce=string;
-     color=custom;
      compression=compress:on/off;
-     cpi=integer;
+     cpi=integer "x>0" "%s must be greater than 0";
      cprompt=string;
-     decimal=dec:dot/_comma;
+     decimal=dec:dot/comma;
      disk=custom;
      dprompt=string;
      echo=echo:on/off;
-     eject=eject:on/off;
      endcmd=string "x==1" "one character long";
+     epoch=custom;
      errorbreak=errbrk:on/off;
      errors=errors:on/off/terminal/listing/both/none;
      format=custom;
      headers=headers:no/yes/blank;
-     helpwindows=helpwin:on/off;
      highres=hires:on/off;
      histogram=string "x==1" "one character long";
      include=inc:on/off;
      journal=custom;
      length=custom;
      listing=custom;
-     log=custom;
      lowres=lores:auto/on/off;
-     lpi=integer;
+     lpi=integer "x>0" "%s must be greater than 0";
      menus=menus:standard/extended;
      messages=messages:on/off/terminal/listing/both/none;
      mexpand=mexp:on/off;
-     miterate=integer;
-     mnest=integer;
-     more=more:on/off;
+     miterate=integer "x>0" "%s must be greater than 0";
+     mnest=integer "x>0" "%s must be greater than 0";
      mprint=mprint:on/off;
-     mxerrs=integer;
-     mxloops=integer;
+     mxerrs=integer "x >= 1" "%s must be at least 1";
+     mxloops=integer "x >=1" "%s must be at least 1";
      mxmemory=integer;
      mxwarns=integer;
      nulline=null:on/off;
-     pager=custom;
      printback=prtbck:on/off;
-     printer=prtr:on/off;
      prompt=string;
-     ptranslate=ptrans:on/off;
-     rcolor=custom;
-     results=custom;
-     runreview=runrev:auto/manual;
+     results=res:on/off/terminal/listing/both/none;
      safer=safe:on;
      scompression=scompress:on/off;
-     screen=scrn:on/off;
      scripttab=string "x==1" "one character long";
      seed=custom;
      tb1=string "x==3 || x==11" "3 or 11 characters long";
      tbfonts=string;
      undefined=undef:warn/nowarn;
-     viewlength=custom;
-     viewwidth=integer;
      width=custom;
-     workdev=custom;
-     workspace=integer;
+     workspace=integer "x>=1024" "%s must be at least 1 MB";
      xsort=xsort:yes/no.
 */
 
 /* (declarations) */
-/* (functions) */
 
-int internal_cmd_set (void);
+/* (_functions) */
+
+static bool do_cc (const char *cc_string, int idx);
 
 int
 cmd_set (void)
 {
   struct cmd_set cmd;
-
-  lex_match_id ("SET");
+  bool ok = true;
 
   if (!parse_set (&cmd))
     return CMD_FAILURE;
 
+  if (cmd.sbc_cca)
+    ok = ok && do_cc (cmd.s_cca, 0);
+  if (cmd.sbc_ccb)
+    ok = ok && do_cc (cmd.s_ccb, 1);
+  if (cmd.sbc_ccc)
+    ok = ok && do_cc (cmd.s_ccc, 2);
+  if (cmd.sbc_ccd)
+    ok = ok && do_cc (cmd.s_ccd, 3);
+  if (cmd.sbc_cce)
+    ok = ok && do_cc (cmd.s_cce, 4);
+
+  if (cmd.sbc_prompt)
+    set_prompt (cmd.s_prompt);
+  if (cmd.sbc_cprompt)
+    set_prompt (cmd.s_cprompt);
+  if (cmd.sbc_dprompt)
+    set_prompt (cmd.s_dprompt);
+
+  if (cmd.sbc_decimal)
+    set_decimal (cmd.dec == STC_DOT ? '.' : ',');
+  if (cmd.sbc_echo)
+    set_echo (cmd.echo == STC_ON);
+  if (cmd.sbc_endcmd)
+    set_endcmd (cmd.s_endcmd[0]);
+  if (cmd.sbc_errorbreak)
+    set_errorbreak (cmd.errbrk == STC_ON);
+  if (cmd.sbc_include)
+    set_include (cmd.inc == STC_ON);
+  if (cmd.sbc_mxerrs)
+    set_mxerrs (cmd.n_mxerrs[0]);
+  if (cmd.sbc_mxwarns)
+    set_mxwarns (cmd.n_mxwarns[0]);
+  if (cmd.sbc_nulline)
+    set_nulline (cmd.null == STC_ON);
+  if (cmd.sbc_safer)
+    set_safer_mode ();
+  if (cmd.sbc_scompression)
+    set_scompression (cmd.scompress == STC_ON);
+  if (cmd.sbc_undefined)
+    set_undefined (cmd.undef == STC_WARN);
+  if (cmd.sbc_workspace)
+    set_workspace (cmd.n_workspace[0] * 1024L);
+
   if (cmd.sbc_block)
     msg (SW, _("%s is obsolete."),"BLOCK");
-
   if (cmd.sbc_boxstring)
     msg (SW, _("%s is obsolete."),"BOXSTRING");
-
-  if (cmd.compress != -1)
-    {
-      msg (MW, _("Active file compression is not yet implemented "
-                "(and probably won't be)."));
-      set_compression = cmd.compress == STC_OFF ? 0 : 1;
-    }
-  if (cmd.scompress != -1)
-    set_scompression = cmd.scompress == STC_OFF ? 0 : 1;
-  if (cmd.n_cpi != NOT_LONG)
-    {
-      if (cmd.n_cpi <= 0)
-       msg (SE, _("CPI must be greater than 0."));
-      else
-       set_cpi = cmd.n_cpi;
-    }
   if (cmd.sbc_histogram)
     msg (MW, _("%s is obsolete."),"HISTOGRAM");
-  if (cmd.n_lpi != NOT_LONG)
-    {
-      if (cmd.n_lpi <= 0)
-       msg (SE, _("LPI must be greater than 0."));
-      else
-       set_lpi = cmd.n_lpi;
-    }
-  
-  /* Windows compatible syntax. */
-  if (cmd.sbc_case)
-    msg (SW, _("CASE is not implemented and probably won't be.  If you care, "
-              "complain about it."));
-  if (cmd.sbc_cca)
-    set_ccx (cmd.s_cca, &set_cc[0], 'A');
-  if (cmd.sbc_ccb)
-    set_ccx (cmd.s_ccb, &set_cc[1], 'B');
-  if (cmd.sbc_ccc)
-    set_ccx (cmd.s_ccc, &set_cc[2], 'C');
-  if (cmd.sbc_ccd)
-    set_ccx (cmd.s_ccd, &set_cc[3], 'D');
-  if (cmd.sbc_cce)
-    set_ccx (cmd.s_cce, &set_cc[4], 'E');
-  if (cmd.dec != -1)
-    {
-      set_decimal = cmd.dec == STC_DOT ? '.' : ',';
-      set_grouping = cmd.dec == STC_DOT ? ',' : '.';
-    }
-  if (cmd.errors != -1)
-    set_routing (cmd.errors, &set_errors);
-  if (cmd.headers != -1)
-    set_headers = cmd.headers == STC_NO ? 0 : (cmd.headers == STC_YES ? 1 : 2);
-  if (cmd.messages != -1)
-    set_routing (cmd.messages, &set_messages);
-  if (cmd.mexp != -1)
-    set_mexpand = cmd.mexp == STC_OFF ? 0 : 1;
-  if (cmd.n_miterate != NOT_LONG)
-    {
-      if (cmd.n_miterate > 0)
-       set_miterate = cmd.n_miterate;
-      else
-       msg (SE, _("Value for MITERATE (%ld) must be greater than 0."),
-            cmd.n_miterate);
-    }
-  if (cmd.n_mnest != NOT_LONG)
-    {
-      if (cmd.n_mnest > 0)
-       set_mnest = cmd.n_mnest;
-      else
-       msg (SE, _("Value for MNEST (%ld) must be greater than 0."),
-            cmd.n_mnest);
-    }
-  if (cmd.mprint != -1)
-    set_mprint = cmd.mprint == STC_OFF ? 0 : 1;
-  if (cmd.n_mxerrs != NOT_LONG)
-    {
-      if (set_mxerrs < 1)
-       msg (SE, _("MXERRS must be at least 1."));
-      else
-       set_mxerrs = cmd.n_mxerrs;
-    }
-  if (cmd.n_mxloops != NOT_LONG)
-    {
-      if (set_mxloops < 1)
-       msg (SE, _("MXLOOPS must be at least 1."));
-      else
-       set_mxloops = cmd.n_mxloops;
-    }
-  if (cmd.n_mxmemory != NOT_LONG)
+  if (cmd.sbc_menus )
+    msg (MW, _("%s is obsolete."),"MENUS");
+  if (cmd.sbc_xsort )
+    msg (SW, _("%s is obsolete."),"XSORT");
+  if (cmd.sbc_mxmemory )
     msg (SE, _("%s is obsolete."),"MXMEMORY");
-  if (cmd.n_mxwarns != NOT_LONG)
-    set_mxwarns = cmd.n_mxwarns;
-  if (cmd.prtbck != -1)
-    set_printback = cmd.prtbck == STC_OFF ? 0 : 1;
-  if (cmd.s_scripttab)
+  if (cmd.sbc_scripttab)
     msg (SE, _("%s is obsolete."),"SCRIPTTAB");
-  if (cmd.s_tbfonts)
-    msg (SW, _("TBFONTS not implemented."));
-  if (cmd.s_tb1)
-    msg (SW, _("TB1 not implemented."));
-  if (cmd.undef != -1)
-    set_undefined = cmd.undef == STC_NOWARN ? 0 : 1;
-  if (cmd.n_workspace != NOT_LONG)
-    msg (SE, _("%s is obsolete."),"WORKSPACE");
-
-  /* PC+ compatible syntax. */
-  if (cmd.scrn != -1)
-    outp_enable_device (cmd.scrn == STC_OFF ? 0 : 1, OUTP_DEV_SCREEN);
-
-  if (cmd.automenu != -1)
-    msg (SW, _("%s is obsolete."),"AUTOMENU");
-  if (cmd.beep != -1)
-    msg (SW, _("%s is obsolete."),"BEEP");
-
-  if (cmd.s_cprompt)
+  if (cmd.sbc_tbfonts)
+    msg (SW, _("%s is obsolete."),"TBFONTS");
+  if (cmd.sbc_tb1 && cmd.s_tb1)
+    msg (SW, _("%s is obsolete."),"TB1");
+
+  if (cmd.sbc_case)
+    msg (SW, _("%s is not implemented."), "CASE");
+
+  if (cmd.sbc_compression)
+    msg (MW, _("Active file compression is not implemented."));
+
+  return CMD_SUCCESS;
+}
+
+/* Find the grouping characters in CC_STRING and set CC's
+   grouping and decimal members appropriately.  Returns true if
+   successful, false otherwise. */
+static bool
+find_cc_separators (const char *cc_string, struct custom_currency *cc)
+{
+  const char *sp;
+  int comma_cnt, dot_cnt;
+  
+  /* Count commas and periods.  There must be exactly three of
+     one or the other, except that an apostrophe acts escapes a
+     following comma or period. */
+  comma_cnt = dot_cnt = 0;
+  for (sp = cc_string; *sp; sp++)
+    if (*sp == ',')
+      comma_cnt++;
+    else if (*sp == '.')
+      dot_cnt++;
+    else if (*sp == '\'' && (sp[1] == '.' || sp[1] == ',' || sp[1] == '\''))
+      sp++;
+  
+  if ((comma_cnt == 3) == (dot_cnt == 3))
+    return false;
+
+  if (comma_cnt == 3)
     {
-      free (set_cprompt);
-      set_cprompt = cmd.s_cprompt;
-      cmd.s_cprompt = NULL;
+      cc->decimal = '.';
+      cc->grouping = ',';
     }
-  if (cmd.s_dprompt)
+  else
     {
-      free (set_dprompt);
-      set_dprompt = cmd.s_dprompt;
-      cmd.s_dprompt = NULL;
+      cc->decimal = ',';
+      cc->grouping = '.';
     }
-  if (cmd.echo != -1)
-    set_echo = cmd.echo == STC_OFF ? 0 : 1;
-  if (cmd.s_endcmd)
-    set_endcmd = cmd.s_endcmd[0];
-  if (cmd.eject != -1)
-    msg (SW, _("%s is obsolete."),"EJECT");
-  if (cmd.errbrk != -1)
-    set_errorbreak = cmd.errbrk == STC_OFF ? 0 : 1;
-  if (cmd.helpwin != -1)
-    msg (SW, _("%s is obsolete."),"HELPWINDOWS");
-  if (cmd.inc != -1)
-    set_include = cmd.inc == STC_OFF ? 0 : 1;
-  if (cmd.menus != -1)
-    msg (MW, _("%s is obsolete."),"MENUS");
-  if (cmd.null != -1)
-    set_nullline = cmd.null == STC_OFF ? 0 : 1;
-  if (cmd.more != -1)
-    set_more = cmd.more == STC_OFF ? 0 : 1;
-  if (cmd.prtr != -1)
-    outp_enable_device (cmd.prtr == STC_OFF ? 0 : 1, OUTP_DEV_PRINTER);
-  if (cmd.s_prompt)
+  return true;
+}
+
+/* Extracts a token from IN into TOKEn.  Tokens are delimited by
+   GROUPING.  The token is truncated to at most CC_WIDTH
+   characters (including null terminator).  Returns the first
+   character following the token. */
+static const char *
+extract_cc_token (const char *in, int grouping, char token[CC_WIDTH]) 
+{
+  char *out = token;
+  
+  for (; *in != '\0' && *in != grouping; in++) 
     {
-      free (set_prompt);
-      set_prompt = cmd.s_prompt;
-      cmd.s_prompt = NULL;
+      if (*in == '\'' && in[1] == grouping)
+        in++;
+      if (out < &token[CC_WIDTH - 1])
+        *out++ = *in;
     }
-  if (cmd.ptrans != -1)
-    msg (SW, _("%s is obsolete."),"PTRANSLATE");
-  if (cmd.runrev != -1)
-    msg (SW, _("%s is obsolete."),"RUNREVIEW");
-  if (cmd.safe == STC_ON)
-    set_safer = 1;
-  if (cmd.xsort != -1)
-    msg (SW, _("%s is obsolete."),"XSORT");
+  *out = '\0';
 
-  free_set (&cmd);
-
-  return CMD_SUCCESS;
+  if (*in == grouping)
+    in++;
+  return in;
 }
 
 /* Sets custom currency specifier CC having name CC_NAME ('A' through
    'E') to correspond to the settings in CC_STRING. */
-static int
-set_ccx (const char *cc_string, struct set_cust_currency * cc, int cc_name)
+static bool
+do_cc (const char *cc_string, int idx)
 {
-  if (strlen (cc_string) > 16)
+  struct custom_currency cc;
+  
+  /* Determine separators. */
+  if (!find_cc_separators (cc_string, &cc)) 
     {
-      msg (SE, _("CC%c: Length of custom currency string `%s' (%d) "
-                "exceeds maximum length of 16."),
-          cc_name, cc_string, strlen (cc_string));
-      return 0;
+      msg (SE, _("CC%c: Custom currency string `%s' does not contain "
+                 "exactly three periods or commas (not both)."),
+           "ABCDE"[idx], cc_string);
+      return false;
     }
-
-  /* Determine separators. */
-  {
-    const char *sp;
-    int n_commas, n_periods;
   
-    /* Count the number of commas and periods.  There must be exactly
-       three of one or the other. */
-    n_commas = n_periods = 0;
-    for (sp = cc_string; *sp; sp++)
-      if (*sp == ',')
-       n_commas++;
-      else if (*sp == '.')
-       n_periods++;
-  
-    if (!((n_commas == 3) ^ (n_periods == 3)))
-      {
-       msg (SE, _("CC%c: Custom currency string `%s' does not contain "
-                  "exactly three periods or commas (not both)."),
-            cc_name, cc_string);
-       return 0;
-      }
-    else if (n_commas == 3)
-      {
-       cc->decimal = '.';
-       cc->grouping = ',';
-      }
-    else
-      {
-       cc->decimal = ',';
-       cc->grouping = '.';
-      }
-  }
-  
-  /* Copy cc_string to cc, changing separators to nulls. */
-  {
-    char *cp;
-    
-    strcpy (cc->buf, cc_string);
-    cp = cc->neg_prefix = cc->buf;
-
-    while (*cp++ != cc->grouping)
-      ;
-    cp[-1] = '\0';
-    cc->prefix = cp;
-
-    while (*cp++ != cc->grouping)
-      ;
-    cp[-1] = '\0';
-    cc->suffix = cp;
-
-    while (*cp++ != cc->grouping)
-      ;
-    cp[-1] = '\0';
-    cc->neg_suffix = cp;
-  }
-  
-  return 1;
-}
+  cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_prefix);
+  cc_string = extract_cc_token (cc_string, cc.grouping, cc.prefix);
+  cc_string = extract_cc_token (cc_string, cc.grouping, cc.suffix);
+  cc_string = extract_cc_token (cc_string, cc.grouping, cc.neg_suffix);
 
-/* Sets *SETTING, which is a combination of SET_ROUTE_* bits that
-   indicates what to do with some sort of output, to the value
-   indicated by Q, which is a value provided by the input parser. */
-static void
-set_routing (int q, int *setting)
-{
-  switch (q)
-    {
-    case STC_ON:
-      *setting |= SET_ROUTE_DISABLE;
-      break;
-    case STC_OFF:
-      *setting &= ~SET_ROUTE_DISABLE;
-      break;
-    case STC_TERMINAL:
-      *setting &= ~(SET_ROUTE_LISTING | SET_ROUTE_OTHER);
-      *setting |= SET_ROUTE_SCREEN;
-      break;
-    case STC_LISTING:
-      *setting &= ~SET_ROUTE_SCREEN;
-      *setting |= SET_ROUTE_LISTING | SET_ROUTE_OTHER;
-      break;
-    case STC_BOTH:
-      *setting |= SET_ROUTE_SCREEN | SET_ROUTE_LISTING | SET_ROUTE_OTHER;
-      break;
-    case STC_NONE:
-      *setting &= ~(SET_ROUTE_SCREEN | SET_ROUTE_LISTING | SET_ROUTE_OTHER);
-      break;
-    default:
-      assert (0);
-    }
+  set_cc (idx, &cc);
+  
+  return true;
 }
 
+/* Parses the BLANKS subcommand, which controls the value that
+   completely blank fields in numeric data imply.  X, Wnd: Syntax is
+   SYSMIS or a numeric value. */
 static int
-stc_custom_pager (struct cmd_set *cmd unused)
+stc_custom_blanks (struct cmd_set *cmd UNUSED)
 {
   lex_match ('=');
-#if !USE_INTERNAL_PAGER
-  if (lex_match_id ("OFF"))
+  if ((token == T_ID && lex_id_match ("SYSMIS", tokid)))
     {
-      if (set_pager)
-       free (set_pager);
-      set_pager = NULL;
+      lex_get ();
+      set_blanks (SYSMIS);
     }
   else
     {
-      if (!lex_force_string ())
+      if (!lex_force_num ())
        return 0;
-      if (set_pager)
-       free (set_pager);
-      set_pager = xstrdup (ds_value (&tokstr));
+      set_blanks (lex_number ());
       lex_get ();
     }
   return 1;
-#else /* USE_INTERNAL_PAGER */
-  if (match_id (OFF))
-    return 1;
-  msg (SW, "External pagers not supported.");
-  return 0;
-#endif /* USE_INTERNAL_PAGER */
 }
 
-/* Parses the BLANKS subcommand, which controls the value that
-   completely blank fields in numeric data imply.  X, Wnd: Syntax is
-   SYSMIS or a numeric value; PC+: Syntax is '.', which is equivalent
-   to SYSMIS, or a numeric value. */
+/* Parses the EPOCH subcommand, which controls the epoch used for
+   parsing 2-digit years. */
 static int
-stc_custom_blanks (struct cmd_set *cmd unused)
+stc_custom_epoch (struct cmd_set *cmd UNUSED) 
 {
   lex_match ('=');
-  if ((token == T_ID && lex_id_match ("SYSMIS", tokid))
-      || (token == T_STRING && !strcmp (tokid, ".")))
+  if (lex_match_id ("AUTOMATIC"))
+    set_epoch (-1);
+  else if (lex_is_integer ()) 
     {
+      int new_epoch = lex_integer ();
       lex_get ();
-      set_blanks = SYSMIS;
+      if (new_epoch < 1500) 
+        {
+          msg (SE, _("EPOCH must be 1500 or later."));
+          return 0;
+        }
+      set_epoch (new_epoch);
     }
-  else
+  else 
     {
-      if (!lex_force_num ())
-       return 0;
-      set_blanks = tokval;
-      lex_get ();
+      lex_error (_("expecting AUTOMATIC or year"));
+      return 0;
     }
+
   return 1;
 }
 
 static int
-stc_custom_length (struct cmd_set *cmd unused)
+stc_custom_length (struct cmd_set *cmd UNUSED)
 {
   int page_length;
 
@@ -559,99 +351,57 @@ stc_custom_length (struct cmd_set *cmd unused)
       lex_get ();
     }
 
-  /* FIXME: Set page length. */
-  return 1;
-}
-
-static int
-stc_custom_results (struct cmd_set *cmd unused)
-{
-  struct tuple
-    {  
-      const char *s;   
-      int v;
-    };
-
-  static struct tuple tab[] =
-    {
-      {"ON", STC_ON},
-      {"OFF", STC_OFF},
-      {"TERMINAL", STC_TERMINAL},
-      {"LISTING", STC_LISTING},
-      {"BOTH", STC_BOTH},
-      {"NONE", STC_NONE},
-      {NULL, 0},
-    };
+  if (page_length != -1) 
+    set_viewlength (page_length);
 
-  struct tuple *t;
-
-  lex_match ('=');
-
-  if (token != T_ID)
-    {
-      msg (SE, _("Missing identifier in RESULTS subcommand."));
-      return 0;
-    }
-  
-  for (t = tab; t->s; t++)
-    if (lex_id_match (t->s, tokid))
-      {
-       lex_get ();
-       set_routing (t->v, &set_results);
-       return 1;
-      }
-  msg (SE, _("Unrecognized identifier in RESULTS subcommand."));
-  return 0;
+  return 1;
 }
 
 static int
-stc_custom_seed (struct cmd_set *cmd unused)
+stc_custom_seed (struct cmd_set *cmd UNUSED)
 {
   lex_match ('=');
   if (lex_match_id ("RANDOM"))
-    set_seed = NOT_LONG;
+    set_rng (time (0));
   else
     {
       if (!lex_force_num ())
        return 0;
-      set_seed = tokval;
+      set_rng (lex_number ());
       lex_get ();
     }
-  set_seed_used=1;
+
   return 1;
 }
 
 static int
-stc_custom_width (struct cmd_set *cmd unused)
+stc_custom_width (struct cmd_set *cmd UNUSED)
 {
-  int page_width;
-
   lex_match ('=');
   if (lex_match_id ("NARROW"))
-    page_width = 79;
+    set_viewwidth (79);
   else if (lex_match_id ("WIDE"))
-    page_width = 131;
+    set_viewwidth (131);
   else
     {
       if (!lex_force_int ())
        return 0;
-      if (lex_integer () < 1)
+      if (lex_integer () < 40)
        {
-         msg (SE, _("WIDTH must be at least 1."));
+         msg (SE, _("WIDTH must be at least 40."));
          return 0;
        }
-      page_width = lex_integer ();
+      set_viewwidth (lex_integer ());
       lex_get ();
     }
 
-  /* FIXME: Set page width. */
   return 1;
 }
 
 /* Parses FORMAT subcommand, which consists of a numeric format
    specifier. */
 static int
-stc_custom_format (struct cmd_set *cmd unused)
+stc_custom_format (struct cmd_set *cmd UNUSED)
 {
   struct fmt_spec fmt;
 
@@ -666,204 +416,303 @@ stc_custom_format (struct cmd_set *cmd unused)
       return 0;
     }
 
-  set_format = fmt;
+  set_format (&fmt);
   return 1;
 }
 
 static int
-stc_custom_journal (struct cmd_set *cmd unused)
+stc_custom_journal (struct cmd_set *cmd UNUSED)
 {
   lex_match ('=');
-  if (lex_match_id ("ON"))
-    set_journaling = 1;
-  else if (lex_match_id ("OFF"))
-    set_journaling = 0;
-  if (token == T_STRING)
+  if (!lex_match_id ("ON") && !lex_match_id ("OFF")) 
     {
-      set_journal = xstrdup (ds_value (&tokstr));
-      lex_get ();
+      if (token == T_STRING)
+        lex_get ();
+      else
+        {
+          lex_error (NULL);
+          return 0;
+        }
     }
   return 1;
 }
 
-/* Parses COLOR subcommand.  PC+: either ON or OFF or two or three
-   comma-delimited numbers inside parentheses. */
 static int
-stc_custom_color (struct cmd_set *cmd unused)
+stc_custom_listing (struct cmd_set *cmd UNUSED)
 {
-  msg (MW, _("%s is obsolete."),"COLOR");
+  bool listing;
 
   lex_match ('=');
-  if (!lex_match_id ("ON") && !lex_match_id ("YES") && !lex_match_id ("OFF") && !lex_match_id ("NO"))
+  if (lex_match_id ("ON") || lex_match_id ("YES"))
+    listing = true;
+  else if (lex_match_id ("OFF") || lex_match_id ("NO"))
+    listing = false;
+  else
     {
-      if (!lex_force_match ('('))
-       return 0;
-      if (!lex_match ('*'))
-       {
-         if (!lex_force_int ())
-           return 0;
-         if (lex_integer () < 0 || lex_integer () > 15)
-           {
-             msg (SE, _("Text color must be in range 0-15."));
-             return 0;
-           }
-         lex_get ();
-       }
-      if (!lex_force_match (','))
-       return 0;
-      if (!lex_match ('*'))
-       {
-         if (!lex_force_int ())
-           return 0;
-         if (lex_integer () < 0 || lex_integer () > 7)
-           {
-             msg (SE, _("Background color must be in range 0-7."));
-             return 0;
-           }
-         lex_get ();
-       }
-      if (lex_match (',') && !lex_match ('*'))
-       {
-         if (!lex_force_int ())
-           return 0;
-         if (lex_integer () < 0 || lex_integer () > 7)
-           {
-             msg (SE, _("Border color must be in range 0-7."));
-             return 0;
-           }
-         lex_get ();
-       }
-      if (!lex_force_match (')'))
-       return 0;
+      /* FIXME */
+      return 0;
     }
+  outp_enable_device (listing, OUTP_DEV_LISTING);
+
   return 1;
 }
 
 static int
-stc_custom_listing (struct cmd_set *cmd unused)
+stc_custom_disk (struct cmd_set *cmd UNUSED)
 {
-  lex_match ('=');
-  if (lex_match_id ("ON") || lex_match_id ("YES"))
-    outp_enable_device (1, OUTP_DEV_LISTING);
-  else if (lex_match_id ("OFF") || lex_match_id ("NO"))
-    outp_enable_device (0, OUTP_DEV_LISTING);
+  return stc_custom_listing (cmd);
+}
+\f
+static void
+show_blanks (void) 
+{
+  if (get_blanks () == SYSMIS)
+    msg (MM, _("BLANKS is SYSMIS."));
   else
+    msg (MM, _("BLANKS is %g."), get_blanks ());
+
+}
+
+static char *
+format_cc (const char *in, char grouping, char *out) 
+{
+  while (*in != '\0') 
     {
-      /* FIXME */
+      if (*in == grouping || *in == '\'')
+        *out++ = '\'';
+      *out++ = *in++;
     }
+  return out;
+}
 
-  return 0;
+static void
+show_cc (int idx) 
+{
+  const struct custom_currency *cc = get_cc (idx);
+  char cc_string[CC_WIDTH * 4 * 2 + 3 + 1];
+  char *out;
+
+  out = format_cc (cc->neg_prefix, cc->grouping, cc_string);
+  *out++ = cc->grouping;
+  out = format_cc (cc->prefix, cc->grouping, out);
+  *out++ = cc->grouping;
+  out = format_cc (cc->suffix, cc->grouping, out);
+  *out++ = cc->grouping;
+  out = format_cc (cc->neg_suffix, cc->grouping, out);
+  *out = '\0';
+  
+  msg (MM, _("CC%c is \"%s\"."), "ABCDE"[idx], cc_string);
 }
 
-static int
-stc_custom_disk (struct cmd_set *cmd unused)
+
+static void
+show_cca (void) 
 {
-  stc_custom_listing (cmd);
-  return 0;
+  show_cc (0);
 }
 
-static int
-stc_custom_log (struct cmd_set *cmd unused)
-{ 
-  stc_custom_journal (cmd);
-  return 0;
+static void
+show_ccb (void) 
+{
+  show_cc (1);
 }
 
-static int
-stc_custom_rcolor (struct cmd_set *cmd unused)
+static void
+show_ccc (void) 
 {
-  msg (SW, _("%s is obsolete."),"RCOLOR");
+  show_cc (2);
+}
 
-  lex_match ('=');
-  if (!lex_force_match ('('))
-    return 0;
+static void
+show_ccd (void) 
+{
+  show_cc (3);
+}
 
-  if (!lex_match ('*'))
-    {
-      if (!lex_force_int ())
-       return 0;
-      if (lex_integer () < 0 || lex_integer () > 6)
-       {
-         msg (SE, _("Lower window color must be between 0 and 6."));
-         return 0;
-       }
-      lex_get ();
-    }
-  if (!lex_force_match (','))
-    return 0;
+static void
+show_cce (void) 
+{
+  show_cc (4);
+}
 
-  if (!lex_match ('*'))
-    {
-      if (!lex_force_int ())
-       return 0;
-      if (lex_integer () < 0 || lex_integer () > 6)
-       {
-         msg (SE, _("Upper window color must be between 0 and 6."));
-         return 0;
-       }
-      lex_get ();
-    }
+static void
+show_decimals (void) 
+{
+  msg (MM, _("DECIMAL is \"%c\"."), get_decimal ());
+}
 
-  if (lex_match (',') && !lex_match ('*'))
-    {
-      if (!lex_force_int ())
-       return 0;
-      if (lex_integer () < 0 || lex_integer () > 6)
-       {
-         msg (SE, _("Frame color must be between 0 and 6."));
-         return 0;
-       }
-      lex_get ();
-    }
-  return 1;
+static void
+show_endcmd (void) 
+{
+  msg (MM, _("ENDCMD is \"%c\"."), get_endcmd ());
 }
 
-static int
-stc_custom_viewlength (struct cmd_set *cmd unused)
-{
-  if (lex_match_id ("MINIMUM"))
-    set_viewlength = 25;
-  else if (lex_match_id ("MEDIAN"))
-    set_viewlength = 43;       /* This is not correct for VGA displays. */
-  else if (lex_match_id ("MAXIMUM"))
-    set_viewlength = 43;
+static void
+show_format (void) 
+{
+  msg (MM, _("FORMAT is %s."), fmt_to_string (get_format ()));
+}
+
+static void
+show_length (void) 
+{
+  msg (MM, _("LENGTH is %d."), get_viewlength ());
+}
+
+static void
+show_mxerrs (void) 
+{
+  msg (MM, _("MXERRS is %d."), get_mxerrs ());
+}
+
+static void
+show_mxloops (void) 
+{
+  msg (MM, _("MXLOOPS is %d."), get_mxloops ());
+}
+
+static void
+show_mxwarns (void) 
+{
+  msg (MM, _("MXWARNS is %d."), get_mxwarns ());
+}
+
+static void
+show_scompression (void) 
+{
+  if (get_scompression ())
+    msg (MM, _("SCOMPRESSION is ON."));
   else
-    {
-      if (!lex_force_int ())
-       return 0;
-#if __MSDOS__
-      if (lex_integer () >= (43 + 25) / 2)
-       set_viewlength = 43;
-      else
-       set_viewlength = 25;
-#else /* not dos */
-      set_viewlength = lex_integer ();
-#endif /* not dos */
-      lex_get ();
-    }
+    msg (MM, _("SCOMPRESSION is OFF."));
+}
 
-#if __MSDOS__
-  msg (SW, _("VIEWLENGTH not implemented."));
-#endif /* dos */
-  return 1;
+static void
+show_undefined (void) 
+{
+  if (get_undefined ())
+    msg (MM, _("UNDEFINED is WARN."));
+  else
+    msg (MM, _("UNDEFINED is NOWARN."));
 }
 
-static int
-stc_custom_workdev (struct cmd_set *cmd unused)
+static void
+show_weight (void) 
+{
+  struct variable *var = dict_get_weight (default_dict);
+  if (var == NULL)
+    msg (MM, _("WEIGHT is off."));
+  else
+    msg (MM, _("WEIGHT is variable %s."), var->name);
+}
+
+static void
+show_width (void) 
+{
+  msg (MM, _("WIDTH is %d."), get_viewwidth ());
+}
+
+struct show_sbc 
+  {
+    const char *name;
+    void (*function) (void);
+  };
+
+struct show_sbc show_table[] = 
+  {
+    {"BLANKS", show_blanks},
+    {"CCA", show_cca},
+    {"CCB", show_ccb},
+    {"CCC", show_ccc},
+    {"CCD", show_ccd},
+    {"CCE", show_cce},
+    {"DECIMALS", show_decimals},
+    {"ENDCMD", show_endcmd},
+    {"FORMAT", show_format},
+    {"LENGTH", show_length},
+    {"MXERRS", show_mxerrs},
+    {"MXLOOPS", show_mxloops},
+    {"MXWARNS", show_mxwarns},
+    {"SCOMPRESSION", show_scompression},
+    {"UNDEFINED", show_undefined},
+    {"WEIGHT", show_weight},
+    {"WIDTH", show_width},
+  };
+
+static void
+show_all (void) 
 {
-  char c[2];
+  size_t i;
+  
+  for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
+    show_table[i].function ();
+}
 
-  msg (SW, _("%s is obsolete."),"WORKDEV");
+static void
+show_all_cc (void) 
+{
+  int i;
+
+  for (i = 0; i < 5; i++)
+    show_cc (i);
+}
 
-  c[1] = 0;
-  for (*c = 'A'; *c <= 'Z'; (*c)++)
-    if (token == T_ID && lex_id_match (c, tokid))
-      {
-       lex_get ();
-       return 1;
-      }
-  msg (SE, _("Drive letter expected in WORKDEV subcommand."));
-  return 0;
+static void
+show_warranty (void) 
+{
+  msg (MM, lack_of_warranty);
+}
+
+static void
+show_copying (void) 
+{
+  msg (MM, copyleft);
+}
+
+int
+cmd_show (void) 
+{
+  if (token == '.') 
+    {
+      show_all ();
+      return CMD_SUCCESS;
+    }
+
+  do 
+    {
+      if (lex_match (T_ALL))
+        show_all ();
+      else if (lex_match_id ("CC")) 
+        show_all_cc ();
+      else if (lex_match_id ("WARRANTY"))
+        show_warranty ();
+      else if (lex_match_id ("COPYING"))
+        show_copying ();
+      else if (token == T_ID)
+        {
+          int i;
+
+          for (i = 0; i < sizeof show_table / sizeof *show_table; i++)
+            if (lex_match_id (show_table[i].name)) 
+              {
+                show_table[i].function ();
+                goto found;
+              }
+          lex_error (NULL);
+          return CMD_PART_SUCCESS_MAYBE;
+
+        found: ;
+        }
+      else 
+        {
+          lex_error (NULL);
+          return CMD_PART_SUCCESS_MAYBE;
+        }
+
+      lex_match ('/');
+    }
+  while (token != '.');
+
+  return CMD_SUCCESS;
 }
 
 /*