/* Copyright (c) 1993
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 * Copyright (c) 1987 Oliver Laumann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ****************************************************************
 */

#include "rcs.h"
RCS_ID("$Id: process.c,v 1.1.1.1 1993/08/15 18:32:57 nate Exp $ FAU")

#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#if !defined(sun) && !defined(B43) && !defined(ISC) && !defined(pyr) && !defined(_CX_UX)
# include <time.h>
#endif
#include <sys/time.h>
#ifndef sun
#include <sys/ioctl.h>
#endif


#include "config.h"
#include "screen.h"
#include "extern.h"

#if defined(sun) && defined(SVR4)
# include <sys/stropts.h>
#endif

extern struct comm comms[];
extern char *rc_name;
extern char *RcFileName, *home, *extra_incap, *extra_outcap;
extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[];
extern char *hardcopydir, *screenlogdir;
extern char *VisualBellString;
extern int VBellWait, MsgWait, MsgMinWait, SilenceWait;
extern char SockPath[], *SockNamePtr;
extern int TtyMode, auto_detach;
extern int iflag;
extern int default_wrap;
extern int use_hardstatus, visual_bell, default_monitor;
extern int default_startup;
extern int slowpaste, defobuflimit;
extern int ZombieKey;
#ifdef AUTO_NUKE
extern int defautonuke;
#endif
extern int intrc, origintrc; /* display? */
extern struct NewWindow nwin_default, nwin_undef;
#ifdef COPY_PASTE
extern int join_with_cr;
extern char mark_key_tab[];
extern char *BufferFile;
#endif
#ifdef POW_DETACH
extern char *BufferFile, *PowDetachString;
#endif
extern time_t Now;

static int  CheckArgNum __P((int, char **));
static void FreeKey __P((int));
static int  NextWindow __P((void));
static int  PreviousWindow __P((void));
static int  MoreWindows __P((void));
static void LogToggle __P((int));
static void ShowTime __P((void));
static void ShowInfo __P((void));
static void SwitchWindow __P((int));
static char **SaveArgs __P((char **));
static struct win *WindowByName __P((char *));
static int  WindowByNumber __P((char *));
static void DoAction  __P((struct action *, int));
static int  ParseSwitch __P((struct action *, int *));
static int  ParseOnOff __P((struct action *, int *));
static int  ParseSaveStr __P((struct action *act, char **));
static int  ParseNum __P((struct action *act, int *));
static int  ParseWinNum __P((struct action *act, int *));
static int  ParseOct __P((struct action *act, int *));
static char *ParseChar __P((char *, char *));
static int  IsNum __P((char *, int));
static int  IsNumColon __P((char *, int, char *, int));
static void InputColon __P((void));
static void Colonfin __P((char *, int));
static void InputSelect __P((void));
static void InputSetenv __P((char *));
static void InputAKA __P((void));
static void AKAfin __P((char *, int));
#ifdef COPY_PASTE
static void copy_reg_fn __P((char *, int));
static void ins_reg_fn __P((char *, int));
#endif
static void process_fn __P((char *, int));
#ifdef PASSWORD
static void pass1 __P((char *, int));
static void pass2 __P((char *, int));
#endif
#ifdef POW_DETACH
static void pow_detach_fn __P((char *, int));
#endif



extern struct display *display, *displays;
extern struct win *fore, *console_window, *windows;
extern struct user *users;

extern char screenterm[], HostName[], version[];
extern struct NewWindow nwin_undef, nwin_default;
extern struct LayFuncs WinLf;
extern struct layer BlankLayer;

extern int Z0width, Z1width;
extern int real_uid, real_gid;

#ifdef NETHACK
extern int nethackflag;
#endif


struct win *wtab[MAXWIN];	/* window table */
struct action ktab[256];	/* command key translation table */


#ifdef MULTIUSER
extern char *multi;
#endif
#ifdef PASSWORD
int CheckPassword;
char Password[20];
#endif

struct plop plop_tab[MAX_PLOP_DEFS];

#ifdef PTYMODE
int TtyMode = PTYMODE;
#else
int TtyMode = 0622;
#endif
int hardcopy_append = 0;
int all_norefresh = 0;


char *noargs[1];

void
InitKeytab()
{
  register unsigned int i;

  for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++)
    {
      ktab[i].nr = RC_ILLEGAL;
      ktab[i].args = noargs;
    }

  ktab['h'].nr = RC_HARDCOPY;
#ifdef BSDJOBS
  ktab['z'].nr = ktab[Ctrl('z')].nr = RC_SUSPEND;
#endif
  ktab['c'].nr = ktab[Ctrl('c')].nr = RC_SCREEN;
  ktab[' '].nr = ktab[Ctrl(' ')].nr =
    ktab['n'].nr = ktab[Ctrl('n')].nr = RC_NEXT;
  ktab['N'].nr = RC_NUMBER;
  ktab[Ctrl('h')].nr = ktab[0177].nr = ktab['p'].nr = ktab[Ctrl('p')].nr = RC_PREV;
  ktab['k'].nr = ktab[Ctrl('k')].nr = RC_KILL;
  ktab['l'].nr = ktab[Ctrl('l')].nr = RC_REDISPLAY;
  ktab['w'].nr = ktab[Ctrl('w')].nr = RC_WINDOWS;
  ktab['v'].nr = ktab[Ctrl('v')].nr = RC_VERSION;
  ktab['q'].nr = ktab[Ctrl('q')].nr = RC_XON;
  ktab['s'].nr = ktab[Ctrl('s')].nr = RC_XOFF;
  ktab['t'].nr = ktab[Ctrl('t')].nr = RC_TIME;
  ktab['i'].nr = ktab[Ctrl('i')].nr = RC_INFO;
  ktab['m'].nr = ktab[Ctrl('m')].nr = RC_LASTMSG;
  ktab['A'].nr = RC_TITLE;
#if defined(UTMPOK) && defined(LOGOUTOK)
  ktab['L'].nr = RC_LOGIN;
#endif
  ktab[','].nr = RC_LICENSE;
  ktab['W'].nr = RC_WIDTH;
  ktab['.'].nr = RC_DUMPTERMCAP;
  ktab[Ctrl('\\')].nr = RC_QUIT;
  ktab['d'].nr = ktab[Ctrl('d')].nr = RC_DETACH;
  ktab['r'].nr = ktab[Ctrl('r')].nr = RC_WRAP;
  ktab['f'].nr = ktab[Ctrl('f')].nr = RC_FLOW;
  ktab['C'].nr = RC_CLEAR;
  ktab['Z'].nr = RC_RESET;
  ktab['H'].nr = RC_LOG;
  ktab[(int)(unsigned char)DefaultEsc].nr = RC_OTHER;
  ktab[(int)(unsigned char)DefaultMetaEsc].nr = RC_META;
  ktab['M'].nr = RC_MONITOR;
  ktab['?'].nr = RC_HELP;
  for (i = 0; i < ((MAXWIN < 10) ? MAXWIN : 10); i++)
    {
      char *args[2], arg1[10];
      args[0] = arg1;
      args[1] = 0;
      sprintf(arg1, "%d", i);
      ktab['0' + i].nr = RC_SELECT;
      ktab['0' + i].args = SaveArgs(args);
    }
  ktab[Ctrl('G')].nr = RC_VBELL;
  ktab[':'].nr = RC_COLON;
#ifdef COPY_PASTE
  ktab['['].nr = ktab[Ctrl('[')].nr = RC_COPY;
  ktab[']'].nr = ktab[Ctrl(']')].nr = RC_PASTE;
  ktab['{'].nr = RC_HISTORY;
  ktab['}'].nr = RC_HISTORY;
  ktab['>'].nr = RC_WRITEBUF;
  ktab['<'].nr = RC_READBUF;
  ktab['='].nr = RC_REMOVEBUF;
  ktab['\''].nr = ktab['"'].nr = RC_SELECT; /* calling a window by name */
#endif
#ifdef POW_DETACH
  ktab['D'].nr = RC_POW_DETACH;
#endif
#ifdef LOCK
  ktab['x'].nr = ktab[Ctrl('x')].nr = RC_LOCKSCREEN;
#endif
  ktab['b'].nr = ktab[Ctrl('b')].nr = RC_BREAK;
  ktab['B'].nr = RC_POW_BREAK;
  ktab['_'].nr = RC_SILENCE;
}

static void
FreeKey(key)
int key;
{
  char **p;

  struct action *act = &ktab[key];
  if (act->nr == RC_ILLEGAL)
    return;
  act->nr = RC_ILLEGAL;
  if (act->args == noargs)
    return;
  for (p = act->args; *p; p++)
    free(*p);
  free(act->args);
  act->args = noargs;
}

void
ProcessInput(ibuf, ilen)
char *ibuf;
int ilen;
{
  char *s;
  int slen;

  while (display)
    {
      fore = d_fore;
      slen = ilen;
      s = ibuf;
      while (ilen > 0)
	{
	  if (*s++ == d_user->u_Esc)
	    break;
	  ilen--;
	}
      slen -= ilen;
      while (slen)
	Process(&ibuf, &slen);
      if (--ilen == 0)
	d_ESCseen = 1;
      if (ilen <= 0)
	return;
      DoAction(&ktab[(int)(unsigned char)*s], (int)(unsigned char)*s);
      ibuf = s + 1;
      ilen--;
    }
}

int
FindCommnr(str)
char *str;
{
  int x, m, l = 0, r = RC_LAST;
  while (l <= r)
    {
      m = (l + r) / 2;
      x = strcmp(str, comms[m].name);
      if (x > 0)
	l = m + 1;
      else if (x < 0)
	r = m - 1;
      else
	return m;
    }
  return RC_ILLEGAL;
}

static int
CheckArgNum(nr, args)
int nr;
char **args;
{
  int i, n;
  static char *argss[] = {"no", "one", "two", "three"};

  n = comms[nr].flags & ARGS_MASK;
  for (i = 0; args[i]; i++)
    ;
  if (comms[nr].flags & ARGS_ORMORE)
    {
      if (i < n)
	{
	  Msg(0, "%s: %s: at least %s argument%s required", rc_name, comms[nr].name, argss[n], n != 1 ? "s" : "");
	  return -1;
	}
    }
  else if ((comms[nr].flags & ARGS_PLUSONE) && (comms[nr].flags & ARGS_PLUSTWO))
    {
      if (i != n && i != n + 1 && i != n + 2)
        {
	  Msg(0, "%s: %s: %s, %s or %s argument%s required", rc_name,
	      comms[nr].name, argss[n], argss[n + 1], argss[n + 2], 
	      n != 0 ? "s" : "");
	  return -1;
	}
    }
  else if (comms[nr].flags & ARGS_PLUSONE)
    {
      if (i != n && i != n + 1)
	{
	  Msg(0, "%s: %s: %s or %s argument%s required", rc_name, comms[nr].name, argss[n], argss[n + 1], n != 0 ? "s" : "");
          return -1;
	}
    }
  else if (comms[nr].flags & ARGS_PLUSTWO)
    {
      if (i != n && i != n + 2)
        {
	  Msg(0, "%s: %s: %s or %s argument%s required", rc_name, 
	      comms[nr].name, argss[n], argss[n + 2], n != 0 ? "s" : "");
	  return -1;
	}
    }
  else if (i != n)
    {
      Msg(0, "%s: %s: %s argument%s required", rc_name, comms[nr].name, argss[n], n != 1 ? "s" : "");
      return -1;
    }
  return 0;
}

/*ARGSUSED*/
static void
DoAction(act, key)
struct action *act;
int key;
{
  int nr = act->nr;
  char **args = act->args;
  struct win *p;
  int i, n, msgok;
  char *s;
  char ch;

  if (nr == RC_ILLEGAL)
    {
      debug1("key '%c': No action\n", key);
      return;
    }
  n = comms[nr].flags;
  if ((n & NEED_DISPLAY) && display == 0)
    {
      Msg(0, "%s: %s: display required", rc_name, comms[nr].name);
      return;
    }
  if ((n & NEED_FORE) && fore == 0)
    {
      Msg(0, "%s: %s: window required", rc_name, comms[nr].name);
      return;
    }
  if (CheckArgNum(nr, args))
    return;
#ifdef MULTIUSER
  if (multi && display)
    {
      if (AclCheckPermCmd(d_user, ACL_EXEC, &comms[nr]))
	return;
    }
#endif /* MULTIUSER */
  msgok = display && !*rc_name;
  switch(nr)
    {
    case RC_SELECT:
      if (!*args)
        InputSelect();
      else if (ParseWinNum(act, &n) == 0)
        SwitchWindow(n);
      break;
#ifdef AUTO_NUKE
    case RC_DEFAUTONUKE:
      if (ParseOnOff(act, &defautonuke) == 0 && msgok)
	Msg(0, "Default autonuke turned %s", defautonuke ? "on" : "off");
      if (display && *rc_name)
	d_auto_nuke = defautonuke;
      break;
    case RC_AUTONUKE:
      if (ParseOnOff(act, &d_auto_nuke) == 0 && msgok)
	Msg(0, "Autonuke turned %s", d_auto_nuke ? "on" : "off");
      break;
#endif
    case RC_DUPLICATE:
      if (!*args)
	{
	  if (fore->w_dupto >= 0)
	    Msg(0, "Duplicating output to window %d", fore->w_dupto);
	  else
	    Msg(0, "No duplicate from here\n");
	  break;
	}
      if (!strcmp(*args, "off"))
        {
	  fore->w_dupto = -1;
	  break;
	}
      while (*args)
        {
	  n = WindowByNoN(*args++);
          if (n < 0)
	    {
	      Msg(0, "Invalid window description");
	      continue;
	    }
	  if ((p = wtab[n]) == 0)
	    {
	      Msg(0, "Window %d does not exist", n);
	      continue;
	    }
	  for (nr = fore->w_number; wtab[nr] && wtab[nr]->w_dupto >= 0;nr = wtab[nr]->w_dupto)
	    {
	      if (wtab[nr]->w_dupto == n)
		{
		  Msg(0, "Cyclic dup detected\n");
		  return;
		}
	    }
	  wtab[n]->w_dupto = fore->w_number;
	}
      break;
    case RC_DEFOBUFLIMIT:
      if (ParseNum(act, &defobuflimit) == 0 && msgok)
	Msg(0, "Default limit set to %d", defobuflimit);
      if (display && *rc_name)
	d_obufmax = defobuflimit;
      break;
    case RC_OBUFLIMIT:
      if (*args == 0)
	Msg(0, "Limit is %d, current buffer size is %d", d_obufmax, d_obuflen);
      else if (ParseNum(act, &d_obufmax) == 0 && msgok)
	Msg(0, "Limit set to %d", d_obufmax);
      break;
    case RC_DUMPTERMCAP:
      WriteFile(DUMP_TERMCAP);
      break;
    case RC_HARDCOPY:
      WriteFile(DUMP_HARDCOPY);
      break;
    case RC_LOG:
      n = fore->w_logfp ? 1 : 0;
      ParseSwitch(act, &n);
      LogToggle(n);
      break;
#ifdef BSDJOBS
    case RC_SUSPEND:
      Detach(D_STOP);
      break;
#endif
    case RC_NEXT:
      if (MoreWindows())
	SwitchWindow(NextWindow());
      break;
    case RC_PREV:
      if (MoreWindows())
	SwitchWindow(PreviousWindow());
      break;
    case RC_KILL:
      {
	char *name;

	n = fore->w_number;
#ifdef PSEUDOS
	if (fore->w_pwin)
	  {
	    FreePseudowin(fore);
#ifdef NETHACK
	    if (nethackflag)
	      Msg(0, "You have a sad feeling for a moment...");
	    else
#endif
	    Msg(0, "Filter removed.");
	    break;
	  }
#endif
	name = SaveStr(fore->w_title);
	KillWindow(fore);
#ifdef NETHACK
	if (nethackflag)
	  Msg(0, "You destroy poor window %d (%s).", n, name);
	else
#endif
	Msg(0, "Window %d (%s) killed.", n, name);
	if (name)
	  free(name);
	break;
      }
    case RC_QUIT:
      Finit(0);
      /* NOTREACHED */
    case RC_DETACH:
      Detach(D_DETACH);
      break;
#ifdef POW_DETACH
    case RC_POW_DETACH:
      if (key >= 0)
	{
	  static char buf[2];

	  buf[0] = key;
	  Input(buf, 1, pow_detach_fn, INP_RAW);
	}
      else
        Detach(D_POWER); /* detach and kill Attacher's parent */
      break;
#endif
    case RC_DEBUG:
#ifdef DEBUG
      if (!*args)
        {
	  if (dfp)
	    Msg(0, "debugging info is written to %s/", DEBUGDIR);
	  else
	    Msg(0, "debugging is currently off. Use 'debug on' to enable.");
	  break;
	}
      if (dfp)
        {
	  debug("debug: closing debug file.\n");
	  fflush(dfp);
	  fclose(dfp);
	  dfp = NULL;
	}
      if (strcmp("off", *args))
        {
	  char buf[255];

	  sprintf(buf, "%s/SCREEN.%d", DEBUGDIR, getpid());
	  if ((dfp = fopen(buf, "a")) == NULL)
	    dfp = stderr;
	  debug("debug: opening debug file.\n");
	}
#else
      Msg(0, "Sorry, screen was compiled without -DDEBUG option.");
#endif
      break;
    case RC_ZOMBIE:
      if (!(s = *args))
        {
	  ZombieKey = 0;
	  break;
	}
      if (!(s = ParseChar(s, &ch)) || *s)
        {
	  Msg(0, "%s:zombie: one character expected.", rc_name);
	  break;
	}
      ZombieKey = ch;
      break;
    case RC_WALL:
      for (n = 0, s = *args; args[n]; n++)
        {
	  /* glue the vector together again. Brute force method. */
	  while (*s)
	    s++;
	  while (s < args[n+1])
	    *s++ = ' ';
	}
#ifdef MULTIUSER
      s = d_user->u_name;
#else
      s = d_usertty;
#endif
      display = NULL;	/* a message without display will cause a broadcast */
      Msg(0, "%s: %s", s, *args);
      break;
    case RC_AT:
#ifdef MULTIUSER
      s = SaveStr(d_user->u_name);
#else
      s = SaveStr(d_usertty);
#endif
      n = strlen(args[0]);
      if (n) n--;
      /*
       * the windows/displays loops are quite dangerous here, take extra
       * care not to trigger landmines. Things may appear/disappear while
       * we are walking along.
       */
      switch (args[0][n])
        {
	case '*':
	  {
	    struct display *nd;
	    struct user *u;

	    args[0][n] = '\0';
	    if (!*args[0])
	      u = d_user;
	    else
	      for (u = users; u; u = u->u_next)
	        if (!strncmp(s, u->u_name, n))
		  break;
	    debug1("at all displays of user %s\n", u->u_name);
	    for (display = displays; display; display = nd)
	      {
		nd = display->_d_next;
		fore = d_fore;
	        if (d_user != u)
		  continue;
		debug1("AT display %s\n", d_usertty);
		DoCommand(args + 1);
		if (display)
		  Msg(0, "command from %s: %s %s", 
		      s, args[1], args[2] ? args[2] : "");
		display = NULL;
		fore = NULL;
	      }
	    free(s);
	    return;
	  }
	case '%':
	  {
	    struct display *nd;

	    args[0][n] = '\0';
	    debug1("at display matching '%s'\n", args[0]);
	    for (display = displays; display; display = nd)
	      {
	        nd = display->_d_next;
		fore = d_fore;
	        if (strncmp(args[0], d_usertty, n) && 
		    (strncmp("/dev/", d_usertty, 5) || 
		     strncmp(args[0], d_usertty + 5, n)) &&
		    (strncmp("/dev/tty", d_usertty, 8) ||
		     strncmp(args[0], d_usertty + 8, n)))
		  continue;
		debug1("AT display %s\n", d_usertty);
		DoCommand(args + 1);
		if (display)
		  Msg(0, "command from %s: %s %s", 
		      s, args[1], args[2] ? args[2] : "");
		display = NULL;
		fore = NULL;
	      }
	    free(s);
	    return;
	  }
	case '#':
	  args[0][n--] = '\0';
	  /* FALLTHROUGH */
	default:
	  {
	    struct win *nw;

	    n++; 
	    if (!*args[0] || (i = WindowByNumber(args[0])) < 0)
	      {
	        /* try looping over titles */
		for (fore = windows; fore; fore = nw)
		  {
		    nw = fore->w_next;
		    if (strncmp(args[0], fore->w_title, n))
		      continue;
		    debug2("AT window %d(%s)\n", fore->w_number, fore->w_title);
		    i++;
		    DoCommand(args + 1);
		    if ((display = fore->w_display))
		      Msg(0, "command from %s: %s %s", 
			  s, args[1], args[2] ? args[2] : "");
		  }
		display = NULL;
		fore = NULL;
		if (i < 0)
		  Msg(0, "%s: at '%s': no such window.\n", rc_name, args[0]);
		free(s);
		return;
	      }
	    else if (i < MAXWIN && (fore = wtab[i]))
	      {
	        debug2("AT window %d (%s)\n", fore->w_number, fore->w_title);
		DoCommand(args + 1);
		if ((display = fore->w_display))
		  Msg(0, "command from %s: %s %s", 
		      s, args[1], args[2] ? args[2] : "");
		display = NULL;
		fore = NULL;
		free(s);
		return;
	      }
	  }
	}
      Msg(0, "%s: at [identifier][%%|*|#] command [args]", rc_name);
      free(s);
      break;
#ifdef COPY_PASTE
    case RC_COPY_REG:
      if ((s = *args) == NULL)
	{
	  Input("Copy to register:", 1, copy_reg_fn, INP_RAW);
	  break;
	}
      if ((s = ParseChar(s, &ch)) == NULL || *s)
	{
	  Msg(0, "%s: copy_reg: character, ^x, or (octal) \\032 expected.",
	      rc_name);
	  break;
	}
      copy_reg_fn(&ch, 0);
      break;
    case RC_INS_REG:
      if ((s = *args) == NULL)
	{
	  Input("Insert from register:", 1, ins_reg_fn, INP_RAW);
	  break;
	}
      if ((s = ParseChar(s, &ch)) == NULL || *s)
	{
	  Msg(0, "%s: ins_reg: character, ^x, or (octal) \\032 expected.",
	      rc_name);
	  break;
	}
      ins_reg_fn(&ch, 0);
      break;
#endif
    case RC_REGISTER:
      if ((s = ParseChar(*args, &ch)) == NULL || *s)
	Msg(0, "%s: register: character, ^x, or (octal) \\032 expected.",
	    rc_name);
      else
	{
	  struct plop *plp = plop_tab + (int)(unsigned char)ch;

	  if (plp->buf)
	    free(plp->buf);
	  plp->buf = SaveStr(expand_vars(args[1]));
	  plp->len = strlen(plp->buf);
	}
      break;
    case RC_PROCESS:
      if ((s = *args) == NULL)
	{
	  Input("Process register:", 1, process_fn, INP_RAW);
	  break;
	}
      if ((s = ParseChar(s, &ch)) == NULL || *s)
	{
	  Msg(0, "%s: process: character, ^x, or (octal) \\032 expected.",
	      rc_name);
	  break;
	}
      process_fn(&ch, 0);
      break;
    case RC_REDISPLAY:
      Activate(-1);
      break;
    case RC_WINDOWS:
      ShowWindows();
      break;
    case RC_VERSION:
      Msg(0, "screen %s", version);
      break;
    case RC_TIME:
      ShowTime();
      break;
    case RC_INFO:
      ShowInfo();
      break;
    case RC_OTHER:
      if (MoreWindows())
	SwitchWindow(d_other ? d_other->w_number : NextWindow());
      break;
    case RC_META:
      ch = d_user->u_Esc;
      s = &ch;
      n = 1;
      Process(&s, &n);
      break;
    case RC_XON:
      ch = Ctrl('q');
      s = &ch;
      n = 1;
      Process(&s, &n);
      break;
    case RC_XOFF:
      ch = Ctrl('s');
      s = &ch;
      n = 1;
      Process(&s, &n);
      break;
    case RC_POW_BREAK:
    case RC_BREAK:
      n = 0;
      if (*args && ParseNum(act, &n))
	break;
      SendBreak(fore, n, nr == RC_POW_BREAK);
      break;
#ifdef LOCK
    case RC_LOCKSCREEN:
      Detach(D_LOCK);
      break;
#endif
    case RC_WIDTH:
      if (*args)
	{
	  if (ParseNum(act, &n))
	    break;
	}
      else
	{
	  if (d_width == Z0width)
	    n = Z1width;
	  else if (d_width == Z1width)
	    n = Z0width;
	  else if (d_width > (Z0width + Z1width) / 2)
	    n = Z0width;
	  else
	    n = Z1width;
	}
      if (n <= 0)
        {
	  Msg(0, "Illegal width");
	  break;
	}
      if (n == d_width)
	break;
      if (ResizeDisplay(n, d_height) == 0)
	{
	  DoResize(d_width, d_height);
	  Activate(d_fore ? d_fore->w_norefresh : 0);
	}
      else
	Msg(0, "Your termcap does not specify how to change the terminal's width to %d.", n);
      break;
    case RC_HEIGHT:
      if (*args)
	{
	  if (ParseNum(act, &n))
	    break;
	}
      else
	{
#define H0height 42
#define H1height 24
	  if (d_height == H0height)
	    n = H1height;
	  else if (d_height == H1height)
	    n = H0height;
	  else if (d_height > (H0height + H1height) / 2)
	    n = H0height;
	  else
	    n = H1height;
	}
      if (n <= 0)
        {
	  Msg(0, "Illegal height");
	  break;
	}
      if (n == d_height)
	break;
      if (ResizeDisplay(d_width, n) == 0)
	{
	  DoResize(d_width, d_height);
	  Activate(d_fore ? d_fore->w_norefresh : 0);
	}
      else
	Msg(0, "Your termcap does not specify how to change the terminal's height to %d.", n);
      break;
    case RC_AKA:
    case RC_TITLE:
      if (*args == 0)
	InputAKA();
      else
	ChangeAKA(fore, *args, 20);
      break;
    case RC_COLON:
      InputColon();
      break;
    case RC_LASTMSG:
      if (d_status_lastmsg)
	Msg(0, "%s", d_status_lastmsg);
      break;
    case RC_SCREEN:
      DoScreen("key", args);
      break;
    case RC_WRAP:
      if (ParseSwitch(act, &fore->w_wrap) == 0 && msgok)
        Msg(0, "%cwrap", fore->w_wrap ? '+' : '-');
      break;
    case RC_FLOW:
      if (*args)
	{
	  if (args[0][0] == 'a')
	    {
	      fore->w_flow = (fore->w_flow & FLOW_AUTO) ? FLOW_AUTOFLAG |FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
	    }
	  else
	    {
	      if (ParseOnOff(act, &n))
		break;
	      fore->w_flow = (fore->w_flow & FLOW_AUTO) | n;
	    }
	}
      else
	{
	  if (fore->w_flow & FLOW_AUTOFLAG)
	    fore->w_flow = (fore->w_flow & FLOW_AUTO) | FLOW_NOW;
	  else if (fore->w_flow & FLOW_NOW)
	    fore->w_flow &= ~FLOW_NOW;
	  else
	    fore->w_flow = fore->w_flow ? FLOW_AUTOFLAG|FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
	}
      SetFlow(fore->w_flow & FLOW_NOW);
      if (msgok)
	Msg(0, "%cflow%s", (fore->w_flow & FLOW_NOW) ? '+' : '-',
	    (fore->w_flow & FLOW_AUTOFLAG) ? "(auto)" : "");
      break;
    case RC_WRITELOCK:
      if (*args)
	{
	  if (args[0][0] == 'a')
	    {
	      fore->w_wlock = WLOCK_AUTO;
	    }
	  else
	    {
	      if (ParseOnOff(act, &n))
		break;
	      fore->w_wlock = n ? WLOCK_ON : WLOCK_OFF;
	    }
	}
      fore->w_wlockuser = d_user;
      Msg(0, "writelock %s", (fore->w_wlock == WLOCK_AUTO) ? "auto" :
	  ((fore->w_wlock == WLOCK_OFF) ? "off" : "on"));
      break;
    case RC_CLEAR:
      if (fore->w_state == LIT)
	WriteString(fore, "\033[H\033[J", 6);
      break;
    case RC_RESET:
      if (fore->w_state == LIT)
	WriteString(fore, "\033c", 2);
      break;
    case RC_MONITOR:
      n = fore->w_monitor == MON_ON;
      if (ParseSwitch(act, &n))
	break;
      if (n)
	{
	  fore->w_monitor = MON_ON;
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "You feel like someone is watching you...");
	  else
#endif
	    Msg(0, "Window %d (%s) is now being monitored for all activity.", 
		fore->w_number, fore->w_title);
	}
      else
	{
	  fore->w_monitor = MON_OFF;
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "You no longer sense the watcher's presence.");
	  else
#endif
	    Msg(0, "Window %d (%s) is no longer being monitored for activity.", 
		fore->w_number, fore->w_title);
	}
      break;
    case RC_DISPLAYS:
      display_displays();
      break;
    case RC_HELP:
      display_help();
      break;
    case RC_LICENSE:
      display_copyright();
      break;
#ifdef COPY_PASTE
    case RC_COPY:
      if (d_layfn != &WinLf)
	{
	  Msg(0, "Must be on a window layer");
	  break;
	}
      MarkRoutine();
      break;
    case RC_HISTORY:
      if (d_layfn != &WinLf)
	{
	  Msg(0, "Must be on a window layer");
	  break;
	}
      if (GetHistory() == 0)
        break;
      if (d_user->u_copybuffer == NULL)
        break;
      /*FALLTHROUGH*/
    case RC_PASTE:
      {
        char *ss;
        int l = 0;

        if ((s = *args) == 0)
          s = ".";
        for (ss = s; (ch = *ss); ss++)
          {
	    if (ch == '.')
              l += d_user->u_copylen;
	    else
              l += plop_tab[(int)(unsigned char)ch].len;
          }
        if (l == 0)
	  {
#ifdef NETHACK
	    if (nethackflag)
	      Msg(0, "Nothing happens.");
	    else
#endif
	    Msg(0, "empty buffer");
	    break;
	  }
        fore->w_pasteptr = 0;
        fore->w_pastelen = 0;
        if (fore->w_pastebuf)
          free(fore->w_pastebuf);
        fore->w_pastebuf = 0;
        if (s[1] == 0)
          {
            if (*s == '.')
	      fore->w_pasteptr = d_user->u_copybuffer;
            else
	      fore->w_pasteptr = plop_tab[(int)(unsigned char)ch].buf;
	    fore->w_pastelen = l;
	    break;
          }
        if ((fore->w_pastebuf = (char *)malloc(l)) == 0)
          {
	    Msg(0, strnomem);
	    break;
          }
        l = 0;
        for (ss = s; (ch = *ss); ss++)
          {
	    if (ch == '.')
	      {
		bcopy(d_user->u_copybuffer, fore->w_pastebuf + l, d_user->u_copylen);
                l += d_user->u_copylen;
              }
	    else
	      {
		bcopy(plop_tab[(int)(unsigned char)ch].buf, fore->w_pastebuf + l, plop_tab[(int)(unsigned char)ch].len);
                l += plop_tab[(int)(unsigned char)ch].len;
              }
          }
        fore->w_pasteptr = fore->w_pastebuf;
        fore->w_pastelen = l;
        break;
      }
    case RC_WRITEBUF:
      if (d_user->u_copybuffer == NULL)
	{
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "Nothing happens.");
	  else
#endif
	  Msg(0, "empty buffer");
	  break;
	}
      WriteFile(DUMP_EXCHANGE);
      break;
    case RC_READBUF:
      ReadFile();
      break;
    case RC_REMOVEBUF:
      KillBuffers();
      break;
#endif				/* COPY_PASTE */
    case RC_ESCAPE:
      FreeKey((int)(unsigned char)d_user->u_Esc);
      FreeKey((int)(unsigned char)d_user->u_MetaEsc);
      if (ParseEscape(d_user, *args))
	{
	  Msg(0, "%s: two characters required after escape.", rc_name);
	  break;
	}
      FreeKey((int)(unsigned char)d_user->u_Esc);
      FreeKey((int)(unsigned char)d_user->u_MetaEsc);
      ktab[(int)(unsigned char)d_user->u_Esc].nr = RC_OTHER;
      ktab[(int)(unsigned char)d_user->u_MetaEsc].nr = RC_META;
      break;
    case RC_CHDIR:
      s = *args ? *args : home;
      if (chdir(s) == -1)
	Msg(errno, "%s", s);
      break;
    case RC_SHELL:
      if (ParseSaveStr(act, &ShellProg) == 0)
        ShellArgs[0] = ShellProg;
      break;
    case RC_HARDCOPYDIR:
      (void)ParseSaveStr(act, &hardcopydir);
      break;
    case RC_LOGDIR:
      (void)ParseSaveStr(act, &screenlogdir);
      break;
    case RC_SHELLTITLE:
    case RC_SHELLAKA:
      (void)ParseSaveStr(act, &nwin_default.aka);
      break;
    case RC_SLEEP:
    case RC_TERMCAP:
    case RC_TERMINFO:
      break;			/* Already handled */
    case RC_TERM:
      s = NULL;
      if (ParseSaveStr(act, &s))
	break;
      if (strlen(s) >= 20)
	{
	  Msg(0,"%s: term: argument too long ( < 20)", rc_name);
	  free(s);
	  break;
	}
      strcpy(screenterm, s);
      free(s);
      debug1("screenterm set to %s\n", screenterm);
      MakeTermcap(display == 0);
      debug("new termcap made\n");
      break;
    case RC_ECHO:
      if (msgok)
	{
	  /*
	   * d_user typed ^A:echo... well, echo isn't FinishRc's job,
	   * but as he wanted to test us, we show good will
	   */
	  if (*args && (args[1] == 0 || (strcmp(args[1], "-n") == 0 && args[2] == 0)))
	    Msg(0, "%s", args[1] ? args[1] : *args);
	  else
 	    Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
	}
      break;
    case RC_BELL:
      (void)ParseSaveStr(act, &BellString);
      break;
#ifdef COPY_PASTE
    case RC_BUFFERFILE:
      if (*args == 0)
	BufferFile = SaveStr(DEFAULT_BUFFERFILE);
      else if (ParseSaveStr(act, &BufferFile))
        break;
      if (msgok)
        Msg(0, "Bufferfile is now '%s'\n", BufferFile);
      break;
#endif
    case RC_ACTIVITY:
      (void)ParseSaveStr(act, &ActivityString);
      break;
#ifdef POW_DETACH
    case RC_POW_DETACH_MSG:
      (void)ParseSaveStr(act, &PowDetachString);
      break;
#endif
#if defined(UTMPOK) && defined(LOGOUTOK)
    case RC_DEFLOGIN:
      (void)ParseOnOff(act, &nwin_default.lflag);
      break;
    case RC_LOGIN:
      n = fore->w_slot != (slot_t)-1;
      if (ParseSwitch(act, &n) == 0)
        SlotToggle(n);
      break;
#endif
    case RC_DEFFLOW:
      if (args[0] && args[1] && args[1][0] == 'i')
	{
	  iflag = 1;
	  if ((intrc == VDISABLE) && (origintrc != VDISABLE))
	    {
#if defined(TERMIO) || defined(POSIX)
	      intrc = d_NewMode.tio.c_cc[VINTR] = origintrc;
	      d_NewMode.tio.c_lflag |= ISIG;
#else /* TERMIO || POSIX */
	      intrc = d_NewMode.m_tchars.t_intrc = origintrc;
#endif /* TERMIO || POSIX */

	      if (display)
		SetTTY(d_userfd, &d_NewMode);
	    }
	}
      if (args[0] && args[0][0] == 'a')
	nwin_default.flowflag = FLOW_AUTOFLAG;
      else
	(void)ParseOnOff(act, &nwin_default.flowflag);
      break;
    case RC_DEFWRAP:
      (void)ParseOnOff(act, &default_wrap);
      break;
    case RC_HARDSTATUS:
      RemoveStatus();
      (void)ParseSwitch(act, &use_hardstatus);
      break;
    case RC_DEFMONITOR:
      if (ParseOnOff(act, &n) == 0)
        default_monitor = (n == 0) ? MON_OFF : MON_ON;
      break;
    case RC_CONSOLE:
      n = (console_window != 0);
      if (ParseSwitch(act, &n))
        break;
      if (TtyGrabConsole(fore->w_ptyfd, n, rc_name))
	break;
      if (n == 0)
	  Msg(0, "%s: releasing console %s", rc_name, HostName);
      else if (console_window)
	  Msg(0, "%s: stealing console %s from window %d (%s)", rc_name, 
	      HostName, console_window->w_number, console_window->w_title);
      else
	  Msg(0, "%s: grabbing console %s", rc_name, HostName);
      console_window = n ? fore : 0;
      break;
    case RC_ALLPARTIAL:
      if (ParseOnOff(act, &all_norefresh))
	break;
      if (!all_norefresh && fore)
	Activate(-1);
      if (msgok)
        Msg(0, all_norefresh ? "No refresh on window change!\n" :
			       "Window specific refresh\n");
      break;
    case RC_PARTIAL:
      (void)ParseSwitch(act, &n);
      fore->w_norefresh = n;
      break;
    case RC_VBELL:
      if (ParseSwitch(act, &visual_bell) || !msgok)
        break;
      if (visual_bell == 0)
	{
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "Suddenly you can't see your bell!");
	  else
#endif
	  Msg(0, "switched to audible bell.");
	}
      else
	{
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "Your bell is no longer invisible.");
	  else
#endif
	  Msg(0, "switched to visual bell.");
	}
      break;
    case RC_VBELLWAIT:
      if (ParseNum(act, &VBellWait) == 0 && msgok)
        Msg(0, "vbellwait set to %d seconds", VBellWait);
      break;
    case RC_MSGWAIT:
      if (ParseNum(act, &MsgWait) == 0 && msgok)
        Msg(0, "msgwait set to %d seconds", MsgWait);
      break;
    case RC_MSGMINWAIT:
      if (ParseNum(act, &MsgMinWait) == 0 && msgok)
        Msg(0, "msgminwait set to %d seconds", MsgMinWait);
      break;
    case RC_SILENCEWAIT:
      if ((ParseNum(act, &SilenceWait) == 0) && msgok)
        {
	  if (SilenceWait < 1)
	    SilenceWait = 1;
	  for (p = windows; p; p = p->w_next)
	    if (p->w_tstamp.seconds)
	      p->w_tstamp.seconds = SilenceWait;
	  Msg(0, "silencewait set to %d seconds", SilenceWait);
	}
      break;
    case RC_NUMBER:
      if (*args == 0)
        Msg(0, "This is window %d (%s).\n", fore->w_number, fore->w_title);
      else
        {
	  int old = fore->w_number;

	  if (ParseNum(act, &n) || n >= MAXWIN)
	    break;
	  p = wtab[n];
	  wtab[n] = fore;
	  fore->w_number = n;
	  wtab[old] = p;
	  if (p)
	    p->w_number = old;
#ifdef MULTIUSER
	  AclWinSwap(old, n);
#endif
	}
      break;
    case RC_SILENCE:
      n = fore->w_tstamp.seconds != 0;
      i = SilenceWait;
      if (args[0] && 
          (args[0][0] == '-' || (args[0][0] >= '0' && args[0][0] <= '9')))
        {
	  if (ParseNum(act, &i))
	    break;
	  n = i;
	}
      else if (ParseSwitch(act, &n))
        break;
      if (n)
        {
	  fore->w_tstamp.lastio = time(0);
	  fore->w_tstamp.seconds = i;
	  if (!msgok)
	    break;
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "You feel like someone is waiting for %d sec. silence...",
	        fore->w_tstamp.seconds);
	  else
#endif
	    Msg(0, "Window %d (%s) is now being monitored for %d sec. silence.",
	    	fore->w_number, fore->w_title, fore->w_tstamp.seconds);
	}
      else
        {
	  fore->w_tstamp.lastio = (time_t)0;
	  fore->w_tstamp.seconds = 0;
	  if (!msgok)
	    break;
#ifdef NETHACK
	  if (nethackflag)
	    Msg(0, "You no longer sense the watcher's silence.");
	  else
#endif
	    Msg(0, "Window %d (%s) is no longer being monitored for silence.", 
		fore->w_number, fore->w_title);
	}
      break;
#ifdef COPY_PASTE
    case RC_DEFSCROLLBACK:
      (void)ParseNum(act, &nwin_default.histheight);
      break;
    case RC_SCROLLBACK:
      (void)ParseNum(act, &n);
      ChangeScrollback(fore, n, d_width);
      if (msgok)
	Msg(0, "scrollback set to %d", fore->w_histheight);
      break;
#endif
    case RC_SESSIONNAME:
      if (*args == 0)
	Msg(0, "This session is named '%s'\n", SockNamePtr);
      else
	{
	  char buf[MAXPATHLEN];

	  s = NULL;
	  if (ParseSaveStr(act, &s))
	    break;
	  if (!*s || strlen(s) > MAXPATHLEN - 13)
	    {
	      Msg(0, "%s: bad session name '%s'\n", rc_name, s);
	      free(s);
	      break;
	    }
	  sprintf(buf, "%s", SockPath);
	  sprintf(buf + (SockNamePtr - SockPath), "%d.%s", getpid(), s); 
	  free(s);
	  if ((access(buf, F_OK) == 0) || (errno != ENOENT))
	    {
	      Msg(0, "%s: inappropriate path: '%s'.", rc_name, buf);
	      break;
	    }
	  if (rename(SockPath, buf))
	    {
	      Msg(errno, "%s: failed to rename(%s, %s)", rc_name, SockPath, buf);
	      break;
	    }
	  debug2("rename(%s, %s) done\n", SockPath, buf);
	  sprintf(SockPath, "%s", buf);
	  MakeNewEnv();
	}
      break;
    case RC_SETENV:
      if (!args[0] || !args[1])
        {
	  debug1("RC_SETENV arguments missing: %s\n", args[0] ? args[0] : "");
          InputSetenv(args[0]);
	}
      else
#ifndef USESETENV
	{
	  char *buf;
	  int l;

	  if ((buf = (char *)malloc((l = strlen(args[0])) + 
				     strlen(args[1]) + 2)) == NULL)
	    {
	      Msg(0, strnomem);
	      break;
	    }
	  strcpy(buf, args[0]);
	  buf[l] = '=';
	  strcpy(buf + l + 1, args[1]);
	  putenv(buf);
# ifdef NEEDPUTENV
	  /*
	   * we use our own putenv(), knowing that it does a malloc()
	   * the string space, we can free our buf now. 
	   */
	  free(buf);
# else /* NEEDSETENV */
	  /*
	   * For all sysv-ish systems that link a standard putenv() 
	   * the string-space buf is added to the environment and must not
	   * be freed, or modified.
	   * We are sorry to say that memory is lost here, when setting 
	   * the same variable again and again.
	   */
# endif /* NEEDSETENV */
	}
#else /* USESETENV */
# if defined(linux) || defined(__386BSD__) || defined(BSDI)
      setenv(args[0], args[1], 0);
# else
      setenv(args[0], args[1]);
# endif /* linux || __386BSD__ || BSDI */
#endif /* USESETENV */
      MakeNewEnv();
      break;
    case RC_UNSETENV:
      unsetenv(*args);
      MakeNewEnv();
      break;
    case RC_SLOWPASTE:
      if (ParseNum(act, &slowpaste) == 0 && msgok)
	Msg(0, "slowpaste set to %d milliseconds", slowpaste);
      break;
#ifdef COPY_PASTE
    case RC_MARKKEYS:
      s = NULL;
      if (ParseSaveStr(act, &s))
        break;
      if (CompileKeys(s, mark_key_tab))
	{
	  Msg(0, "%s: markkeys: syntax error.", rc_name);
	  free(s);
	  break;
	}
      debug1("markkeys %s\n", *args);
      free(s);
      break;
#endif
#ifdef NETHACK
    case RC_NETHACK:
      (void)ParseOnOff(act, &nethackflag);
      break;
#endif
    case RC_HARDCOPY_APPEND:
      (void)ParseOnOff(act, &hardcopy_append);
      break;
    case RC_VBELL_MSG:
      (void)ParseSaveStr(act, &VisualBellString);
      debug1(" new vbellstr '%s'\n", VisualBellString);
      break;
    case RC_DEFMODE:
      if (ParseOct(act, &n))
        break;
      if (n < 0 || n > 0777)
	{
	  Msg(0, "%s: mode: Invalid tty mode %o", rc_name, n);
          break;
	}
      TtyMode = n;
      if (msgok)
	Msg(0, "Ttymode set to %03o", TtyMode);
      break;
#ifdef COPY_PASTE
    case RC_CRLF:
      (void)ParseOnOff(act, &join_with_cr);
      break;
#endif
    case RC_AUTODETACH:
      (void)ParseOnOff(act, &auto_detach);
      break;
    case RC_STARTUP_MESSAGE:
      (void)ParseOnOff(act, &default_startup);
      break;
#ifdef PASSWORD
    case RC_PASSWORD:
      CheckPassword = 1;
      if (*args)
	{
	  strncpy(Password, *args, sizeof(Password) - 1);
	  if (!strcmp(Password, "none"))
	    CheckPassword = 0;
	}
      else
	{
	  if (display == 0)
	    {
	      debug("prompting for password on no display???\n");
	      break;
	    }
	  Input("New screen password:", sizeof(Password) - 1, pass1, 
		INP_NOECHO);
	}
      break;
#endif				/* PASSWORD */
    case RC_BIND:
      if ((s = ParseChar(*args, &ch)) == NULL || *s)
	{
	  Msg(0, "%s: bind: character, ^x, or (octal) \\032 expected.",
	      rc_name);
	  break;
	}
      n = (unsigned char)ch;
      FreeKey(n);
      if (args[1])
	{
	  if ((i = FindCommnr(args[1])) == RC_ILLEGAL)
	    {
	      Msg(0, "%s: bind: unknown command '%s'", rc_name, args[1]);
	      break;
	    }
	  if (CheckArgNum(i, args + 2))
	    break;
	  ktab[n].nr = i;
	  if (args[2])
	    ktab[n].args = SaveArgs(args + 2);
	}
      break;
#ifdef MULTIUSER
    case RC_ACLCHG:
    case RC_ACLADD:
	{
	  struct user **u;
	  
	  u = FindUserPtr(args[0]);
	  UserAdd(args[0], NULL, u);
	  if (args[1] && args[2])
	    AclSetPerm(*u, args[1], args[2]);
	  else
	    AclSetPerm(*u, "+rwx", "#?"); 
	  break;
	}
    case RC_ACLDEL:
        {
	  if (UserDel(args[0], NULL))
	    break;
	  if (msgok)
	    Msg(0, "%s removed from acl database", args[0]);
	  break;
        }
    case RC_ACLGRP:
        {
	  break;
	}
    case RC_MULTIUSER:
      if (ParseOnOff(act, &n))
	break;
      multi = n ? "" : 0;
      chsock();
      if (msgok)
	Msg(0, "Multiuser mode %s", multi ? "enabled" : "disabled");
      break;
#endif /* MULTIUSER */
#ifdef PSEUDOS
    case RC_EXEC:
      winexec(args);
      break;
#endif
#ifdef MULTI
    case RC_CLONE:
      execclone(args);
      break;
#endif
    default:
      break;
    }
}

void
DoCommand(argv) 
char **argv;
{
  struct action act;

  if ((act.nr = FindCommnr(*argv)) == RC_ILLEGAL)  
    {
      Msg(0, "%s: unknown command '%s'", rc_name, *argv);
      return;
    }
  act.args = argv + 1;
  DoAction(&act, -1);
}

static char **
SaveArgs(args)
char **args;
{
  register char **ap, **pp;
  register int argc = 0;

  while (args[argc])
    argc++;
  if ((pp = ap = (char **) malloc((unsigned) (argc + 1) * sizeof(char **))) == 0)
    Panic(0, strnomem);
  while (argc--)
    {
      *pp++ = SaveStr(*args++);
    }
  *pp = 0;
  return ap;
}

int 
Parse(buf, args)
char *buf, **args;
{
  register char *p = buf, **ap = args;
  register int delim, argc;

  argc = 0;
  for (;;)
    {
      while (*p && (*p == ' ' || *p == '\t'))
	++p;
      if (argc == 0)
	{
	  /* 
	   * Expand hardcoded shortcuts.
	   * This should not be done here, cause multiple commands per
	   * line or prefixed commands won't be recognized.
	   * But as spaces between shortcut character and arguments
	   * can be ommited this expansion affects tokenisation and
	   * should be done here. Hmmm. jw.
	   */
	  switch (*p)
	    {
	    case '@':
	      *ap++ = "at";
	      while (*(++p) == ' ' || *p == '\t')
	        ;
	      argc++;
	      break;
#ifdef PSEUDOS
	    case '!':
	    case '|':
	      *ap++ = "exec";
	      if (*p == '!')
		p++;
	      while (*p == ' ' || *p == '\t')
		p++;
	      argc++;
	      break;
#endif
	    }
        }
      if (*p == '\0' || *p == '#')
	{
	  *p = '\0';
	  args[argc] = 0;
	  return argc;
	}
      if (++argc >= MAXARGS)
	{
	  Msg(0, "%s: too many tokens.", rc_name);
	  return 0;
	}
      delim = 0;
      if (*p == '"' || *p == '\'')
	delim = *p++;
      *ap++ = p;
      while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
	++p;
      if (*p == '\0')
	{
	  if (delim)
	    {
	      Msg(0, "%s: Missing quote.", rc_name);
	      return 0;
	    }
	}
      else
        *p++ = '\0';
    }
}

int 
ParseEscape(u, p)
struct user *u;
char *p;
{
  if ((p = ParseChar(p, u ? &u->u_Esc : &DefaultEsc)) == NULL ||
      (p = ParseChar(p, u ? &u->u_MetaEsc : &DefaultMetaEsc)) == NULL || *p)
    return -1;
  return 0;
}

static int
ParseSwitch(act, var)
struct action *act;
int *var;
{
  if (*act->args == 0)
    {
      *var ^= 1;
      return 0;
    }
  return ParseOnOff(act, var);
}

static int
ParseOnOff(act, var)
struct action *act;
int *var;
{
  register int num = -1;
  char **args = act->args;

  if (args[1] == 0)
    {
      if (strcmp(args[0], "on") == 0)
	num = 1;
      else if (strcmp(args[0], "off") == 0)
	num = 0;
    }
  if (num < 0)
    {
      Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, comms[act->nr].name);
      return -1;
    }
  *var = num;
  return 0;
}

static int
ParseSaveStr(act, var)
struct action *act;
char **var;
{
  char **args = act->args;
  if (*args == 0 || args[1])
    {
      Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name);
      return -1;
    }
  if (*var)
    free(*var);
  *var = SaveStr(*args);
  return 0;
}

static int
ParseNum(act, var)
struct action *act;
int *var;
{
  int i;
  char *p, **args = act->args;

  p = *args;
  if (p == 0 || *p == 0 || args[1])
    {
      Msg(0, "%s: %s: invalid argument. Give one argument.",
          rc_name, comms[act->nr].name);
      return -1;
    }
  i = 0; 
  while (*p)
    {
      if (*p >= '0' && *p <= '9')
	i = 10 * i + (*p - '0');
      else
	{
	  Msg(0, "%s: %s: invalid argument. Give numeric argument.",
	      rc_name, comms[act->nr].name);
	  return -1;
	}    
      p++;
    }
  debug1("ParseNum got %d\n", i);
  *var = i;
  return 0;
}

static struct win *
WindowByName(s)
char *s;
{
  struct win *p;

  for (p = windows; p; p = p->w_next)
    if (!strncmp(p->w_title, s, strlen(s)))
      return p;
  return NULL;
}

static int
WindowByNumber(str)
char *str;
{
  int i;
  char *s;

  for (i = 0, s = str; *s; s++)
    {
      if (*s < '0' || *s > '9')
        break;
      i = i * 10 + (*s - '0');
    }
  return *s ? -1 : i;
}

/* 
 * Get window number from Name or Number string.
 * Numbers are tried first, then names, a prefix match suffices.
 * Be careful when assigning numeric strings as WindowTitles.
 */
int
WindowByNoN(str)
char *str;
{
  int i;
  struct win *p;
  
  if ((i = WindowByNumber(str)) < 0 || i >= MAXWIN)
    {
      if ((p = WindowByName(str)))
	return p->w_number;
      return -1;
    }
  return i;
}

static int
ParseWinNum(act, var)
struct action *act;
int *var;
{
  char **args = act->args;
  int i = 0;

  if (*args == 0 || args[1])
    {
      Msg(0, "%s: %s: one argument required.", rc_name, comms[act->nr].name);
      return -1;
    }
  
  i = WindowByNoN(*args);
  if (i < 0)
    {
      Msg(0, "%s: %s: invalid argument. Give window number or name.",
          rc_name, comms[act->nr].name);
      return -1;
    }
  debug1("ParseWinNum got %d\n", i);
  *var = i;
  return 0;
}

static int
ParseOct(act, var)
struct action *act;
int *var;
{
  char *p, **args = act->args;
  int i = 0; 

  p = *args;
  if (p == 0 || *p == 0 || args[1])
    {
      Msg(0, "%s: %s: invalid argument. Give one octal argument.",
          rc_name, comms[act->nr].name);
      return -1;
    }
  while (*p)
    {
      if (*p >= '0' && *p <= '7')
	i = 8 * i + (*p - '0');
      else
	{
	  Msg(0, "%s: %s: invalid argument. Give octal argument.",
	      rc_name, comms[act->nr].name);
	  return -1;
	}    
      p++;
    }
  debug1("ParseOct got %d\n", i);
  *var = i;
  return 0;
}

static char *
ParseChar(p, cp)
char *p, *cp;
{
  if (*p == 0)
    return 0;
  if (*p == '^')
    {
      if (*++p == '?')
        *cp = '\177';
      else if (*p >= '@')
        *cp = Ctrl(*p);
      else
        return 0;
      ++p;
    }
  else if (*p == '\\' && *++p <= '7' && *p >= '0')
    {
      *cp = 0;
      do
        *cp = *cp * 8 + *p - '0';
      while (*++p <= '7' && *p >= '0');
    }
  else
    *cp = *p++;
  return p;
}


static
int IsNum(s, base)
register char *s;
register int base;
{
  for (base += '0'; *s; ++s)
    if (*s < '0' || *s > base)
      return 0;
  return 1;
}

static int
IsNumColon(s, base, p, psize)
int base, psize;
char *s, *p;
{
  char *q;
  if ((q = rindex(s, ':')) != NULL)
    {
      strncpy(p, q + 1, psize - 1);
      p[psize - 1] = '\0';
      *q = '\0';
    }
  else
    *p = '\0';
  return IsNum(s, base);
}

static void
SwitchWindow(n)
int n;
{
  struct win *p;

  debug1("SwitchWindow %d\n", n);
  if (display == 0)
    return;
  if (n < 0 || n >= MAXWIN || (p = wtab[n]) == 0)
    {
      ShowWindows();
      return;
    }
  if (p == d_fore)
    {
      Msg(0, "This IS window %d (%s).", n, p->w_title);
      return;
    }
  if (p->w_display)
    {
      Msg(0, "Window %d (%s) is on another display (%s@%s).", n, p->w_title,
          p->w_display->_d_user->u_name, p->w_display->_d_usertty);
      return;
    }
  SetForeWindow(p);
  Activate(fore->w_norefresh);  
}  

/* 
 * returns 0, if the lock really has been released 
 */
int
ReleaseAutoWritelock(dis, w)
struct display *dis;
struct win *w;
{
  /* release auto writelock when user has no other display here */
  if (w->w_wlock == WLOCK_AUTO && w->w_wlockuser == d_user)
    {
      struct display *d;

      for (d = displays; d; d = d->_d_next)
	if (( d != display) && (d->_d_fore == w))
	  break;
      debug3("%s %s autolock on win %d\n", 
	     d_user->u_name, d?"keeps":"releases", w->w_number);
      if (!d)
        {
	  w->w_wlockuser = NULL;
	  return 0;
	}
    }
  return 1;
}

void
SetForeWindow(wi)
struct win *wi;
{
  struct win *p, **pp;
  struct layer *l;
  /*
   * If we come from another window, make it inactive.
   */
  if (display)
    {
      fore = d_fore;
      if (fore)
	{
	  ReleaseAutoWritelock(display, fore);
	  /* deactivate old window. */
	  if (fore->w_tstamp.seconds)
	    fore->w_tstamp.lastio = Now;
	  d_other = fore;
	  fore->w_active = 0;
	  fore->w_display = 0;
	}
      else
	{
	  /* put all the display layers on the window. */
	  for (l = d_lay; l; l = l->l_next)
	    if (l->l_next == &BlankLayer)
	      {
		l->l_next = wi->w_lay;
		wi->w_lay = d_lay;
		for (l = d_lay; l != wi->w_lay; l = l->l_next)
		  l->l_block |= wi->w_lay->l_block;
		break;
	      }
	}
      d_fore = wi;
      if (d_other == wi)
	d_other = 0;
      d_lay = wi->w_lay;
      d_layfn = d_lay->l_layfn;
      if ((wi->w_wlock == WLOCK_AUTO) && !wi->w_wlockuser)
        {
	  debug2("%s obtained auto writelock for window %d\n",
	  	 d_user->u_name, wi->w_number);
          wi->w_wlockuser = d_user;
        }
    }
  fore = wi;
  fore->w_display = display;
  if (!fore->w_lay)
    fore->w_active = 1;
  /*
   * Place the window at the head of the most-recently-used list.
   */
  for (pp = &windows; (p = *pp); pp = &p->w_next)
    if (p == wi)
      break;
  ASSERT(p);
  *pp = p->w_next;
  p->w_next = windows;
  windows = p;
}

static int
NextWindow()
{
  register struct win **pp;
  int n = fore ? fore->w_number : 0;

  for (pp = wtab + n + 1; pp != wtab + n; pp++)
    {
      if (pp == wtab + MAXWIN)
	pp = wtab;
      if (*pp)
	break;
    }
  return pp - wtab;
}

static int
PreviousWindow()
{
  register struct win **pp;
  int n = fore ? fore->w_number : MAXWIN - 1;

  for (pp = wtab + n - 1; pp != wtab + n; pp--)
    {
      if (pp < wtab)
	pp = wtab + MAXWIN - 1;
      if (*pp)
	break;
    }
  return pp - wtab;
}

static int
MoreWindows()
{
  if (windows && windows->w_next)
    return 1;
  if (fore == 0)
    {
      Msg(0, "No window available");
      return 0;
    }
#ifdef NETHACK
  if (nethackflag)
    Msg(0, "You cannot escape from window %d!", fore->w_number);
  else
#endif
  Msg(0, "No other window.");
  return 0;
}

void
KillWindow(wi)
struct win *wi;
{
  struct win **pp, *p;

  display = wi->w_display;
  if (display)
    {
      if (wi == d_fore)
	{
	  RemoveStatus();
	  if (d_lay != &wi->w_winlay)
	    ExitOverlayPage();
	  d_fore = 0;
	  d_lay = &BlankLayer;
	  d_layfn = BlankLayer.l_layfn;
	}
    }

  for (pp = &windows; (p = *pp); pp = &p->w_next)
    if (p == wi)
      break;
  ASSERT(p);
  *pp = p->w_next;
  /*
   * Remove window from linked list.
   */
  wi->w_inlen = 0;
  wtab[wi->w_number] = 0;
  FreeWindow(wi);
  /*
   * If the foreground window disappeared check the head of the linked list
   * of windows for the most recently used window. If no window is alive at
   * all, exit.
   */
  if (display && d_fore)
    return;
  if (windows == 0)
    Finit(0);
  SwitchWindow(windows->w_number);
}

static void
LogToggle(on)
int on;
{
  char buf[1024];

  if ((fore->w_logfp != 0) == on)
    {
      if (display && !*rc_name)
	Msg(0, "You are %s logging.", on ? "already" : "not");
      return;
    }
  if (screenlogdir)
    sprintf(buf, "%s/screenlog.%d", screenlogdir, fore->w_number);
  else
    sprintf(buf, "screenlog.%d", fore->w_number);
  if (fore->w_logfp != NULL)
    {
#ifdef NETHACK
      if (nethackflag)
	Msg(0, "You put away your scroll of logging named \"%s\".", buf);
      else
#endif
      Msg(0, "Logfile \"%s\" closed.", buf);
      fclose(fore->w_logfp);
      fore->w_logfp = NULL;
      return;
    }
  if ((fore->w_logfp = secfopen(buf, "a")) == NULL)
    {
#ifdef NETHACK
      if (nethackflag)
	Msg(0, "You don't seem to have a scroll of logging named \"%s\".", buf);
      else
#endif
      Msg(errno, "Error opening logfile \"%s\"", buf);
      return;
    }
#ifdef NETHACK
  if (nethackflag)
    Msg(0, "You %s your scroll of logging named \"%s\".",
	ftell(fore->w_logfp) ? "add to" : "start writing on", buf);
  else
#endif
  Msg(0, "%s logfile \"%s\"", ftell(fore->w_logfp) ? "Appending to" : "Creating", buf);
}

void
ShowWindows()
{
  char buf[1024];
  register char *s, *ss;
  register struct win **pp, *p;
  register char *cmd;

  ASSERT(display);
  s = ss = buf;
  for (pp = wtab; pp < wtab + MAXWIN; pp++)
    {
      if ((p = *pp) == 0)
	continue;

      cmd = p->w_title;
      if (s - buf + strlen(cmd) > sizeof(buf) - 6)
	break;
      if (s > buf)
	{
	  *s++ = ' ';
	  *s++ = ' ';
	}
      sprintf(s, "%d", p->w_number);
      s += strlen(s);
      if (p == fore)
	{
	  ss = s;
	  *s++ = '*';
	}
      else if (p == d_other)
	*s++ = '-';
      if (p->w_display && p->w_display != display)
	*s++ = '&';
      if (p->w_monitor == MON_DONE || p->w_monitor == MON_MSG)
	*s++ = '@';
      if (p->w_bell == BELL_DONE || p->w_bell == BELL_MSG)
	*s++ = '!';
#ifdef UTMPOK
      if (p->w_slot != (slot_t) 0 && p->w_slot != (slot_t) -1)
	*s++ = '$';
#endif
      if (p->w_logfp != NULL)
	{
	  strcpy(s, "(L)");
	  s += 3;
	}
      if (p->w_ptyfd < 0)
        *s++ = 'Z';
      *s++ = ' ';
      strcpy(s, cmd);
      s += strlen(s);
      if (p == fore)
	{
	  /* 
	   * this is usually done by Activate(), but when looking
	   * on your current window, you may get annoyed, as there is still
	   * that temporal '!' and '@' displayed.
	   * So we remove that after displaying it once.
	   */
	  p->w_bell = BELL_OFF;
	  if (p->w_monitor != MON_OFF)
	    p->w_monitor = MON_ON;
	}
    }
  *s++ = ' ';
  *s = '\0';
  if (ss - buf > d_width / 2)
    {
      ss -= d_width / 2;
      if (s - ss < d_width)
	{
	  ss = s - d_width;
	  if (ss < buf)
	    ss = buf;
	}
    }
  else
    ss = buf;
  Msg(0, "%s", ss);
}


static void
ShowTime()
{
  char buf[512];
  struct tm *tp;
  time_t now;

  (void) time(&now);
  tp = localtime(&now);
  sprintf(buf, "%2d:%02d:%02d %s", tp->tm_hour, tp->tm_min, tp->tm_sec,
	  HostName);
#ifdef LOADAV
  AddLoadav(buf + strlen(buf));
#endif /* LOADAV */
  Msg(0, "%s", buf);
}

static void
ShowInfo()
{
  char buf[512], *p;
  register struct win *wp = fore;
  register int i;

  if (wp == 0)
    {
      Msg(0, "(%d,%d)/(%d,%d) no window", d_x + 1, d_y + 1, d_width, d_height);
      return;
    }
#ifdef COPY_PASTE
  sprintf(buf, "(%d,%d)/(%d,%d)+%d %c%sflow %cins %corg %cwrap %capp %clog %cmon %cr",
#else
  sprintf(buf, "(%d,%d)/(%d,%d) %c%sflow %cins %corg %cwrap %capp %clog %cmon %cr",
#endif
	  wp->w_x + 1, wp->w_y + 1, wp->w_width, wp->w_height,
#ifdef COPY_PASTE
	  wp->w_histheight,
#endif
	  (wp->w_flow & FLOW_NOW) ? '+' : '-',
	  (wp->w_flow & FLOW_AUTOFLAG) ? "" : ((wp->w_flow & FLOW_AUTO) ? "(+)" : "(-)"),
	  wp->w_insert ? '+' : '-', wp->w_origin ? '+' : '-',
	  wp->w_wrap ? '+' : '-', wp->w_keypad ? '+' : '-',
	  (wp->w_logfp != NULL) ? '+' : '-',
	  (wp->w_monitor != MON_OFF) ? '+' : '-',
	  wp->w_norefresh ? '-' : '+');
  if (CG0)
    {
      p = buf + strlen(buf);
      sprintf(p, " G%1d [", wp->w_Charset);
      for (i = 0; i < 4; i++)
	p[i + 5] = wp->w_charsets[i] ? wp->w_charsets[i] : 'B';
      p[9] = ']';
      p[10] = '\0';
    }
  Msg(0, "%s", buf);
}


static void
AKAfin(buf, len)
char *buf;
int len;
{
  ASSERT(display);
  if (len && fore)
    ChangeAKA(fore, buf, 20);
}

static void
InputAKA()
{
  Input("Set window's title to: ", 20, AKAfin, INP_COOKED);
}

static void
Colonfin(buf, len)
char *buf;
int len;
{
  if (len)
    RcLine(buf);
}

static void
InputColon()
{
  Input(":", 100, Colonfin, INP_COOKED);
}

static void
SelectFin(buf, len)
char *buf;
int len;
{
  int n;

  if (!len || !display)
    return;
  if ((n = WindowByNoN(buf)) < 0)
    return;
  SwitchWindow(n);
}
    
static void
InputSelect()
{
  Input("Switch to window: ", 20, SelectFin, INP_COOKED);
}

static char setenv_var[31];


static void
SetenvFin1(buf, len)
char *buf;
int len;
{
  if (!len || !display)
    return;
  InputSetenv(buf);
}
  
static void
SetenvFin2(buf, len)
char *buf;
int len;
{
  struct action act;
  char *args[3];

  if (!len || !display)
    return;
  act.nr = RC_SETENV;
  args[0] = setenv_var;
  args[1] = buf;
  args[2] = NULL;
  act.args = args;
  debug2("SetenvFin2: setenv '%s' '%s'\n", setenv_var, buf);
  DoAction(&act, -1);
}

static void
InputSetenv(arg)
char *arg;
{
  static char setenv_buf[80];	/* need to be static here, cannot be freed */

  if (arg)
    {
      strncpy(setenv_var, arg, 30);
      sprintf(setenv_buf, "Enter value for %s: ", arg);
      Input(setenv_buf, 30, SetenvFin2, INP_COOKED);
    }
  else
    Input("Setenv: Enter variable name: ", 30, SetenvFin1, INP_COOKED);
}

void
DoScreen(fn, av)
char *fn, **av;
{
  struct NewWindow nwin;
  register int num;
  char buf[20];
  char termbuf[25];

  nwin = nwin_undef;
  termbuf[0] = '\0';
  while (av && *av && av[0][0] == '-')
    {
      switch (av[0][1])
	{
	case 'f':
	  switch (av[0][2])
	    {
	    case 'n':
	    case '0':
	      nwin.flowflag = FLOW_NOW * 0;
	      break;
	    case 'y':
	    case '1':
	    case '\0':
	      nwin.flowflag = FLOW_NOW * 1;
	      break;
	    case 'a':
	      nwin.flowflag = FLOW_AUTOFLAG;
	      break;
	    default:
	      break;
	    }
	  break;
	case 'k':
	case 't':
	  if (av[0][2])
	    nwin.aka = &av[0][2];
	  else if (*++av)
	    nwin.aka = *av;
	  else
	    --av;
	  break;
	case 'T':
	  if (av[0][2])
	    nwin.term = &av[0][2];
	  else if (*++av)
	    nwin.term = *av;
	  else
	    --av;
	  break;
	case 'h':
	  if (av[0][2])
	    nwin.histheight = atoi(av[0] + 2);
	  else if (*++av)
	    nwin.histheight = atoi(*av);
	  else 
	    --av;
	  break;
#ifdef LOGOUTOK
	case 'l':
	  switch (av[0][2])
	    {
	    case 'n':
	    case '0':
	      nwin.lflag = 0;
	      break;
	    case 'y':
	    case '1':
	    case '\0':
	      nwin.lflag = 1;
	      break;
	    default:
	      break;
	    }
	  break;
#endif
	case 'a':
	  nwin.aflag = 1;
	  break;
	case 'M':
	  nwin.monitor = MON_ON;
debug("nwin.monitor = MON_ON;\n");
	  break;
	default:
	  Msg(0, "%s: screen: invalid option -%c.", fn, av[0][1]);
	  break;
	}
      ++av;
    }
  num = 0;
  if (av && *av && IsNumColon(*av, 10, buf, sizeof(buf)))
    {
      if (*buf != '\0')
	nwin.aka = buf;
      num = atoi(*av);
      if (num < 0 || num > MAXWIN - 1)
	{
	  Msg(0, "%s: illegal screen number %d.", fn, num);
	  num = 0;
	}
      nwin.StartAt = num;
      ++av;
    }
  if (av && *av)
    {
      nwin.args = av;
      if (!nwin.aka)
        nwin.aka = Filename(*av);
    }
  MakeWindow(&nwin);
}

#ifdef COPY_PASTE
/*
 * CompileKeys must be called before Markroutine is first used.
 * to initialise the keys with defaults, call CompileKeys(NULL, mark_key_tab);
 *
 * s is an ascii string in a termcap-like syntax. It looks like
 *   "j=u:k=d:l=r:h=l: =.:" and so on...
 * this example rebinds the cursormovement to the keys u (up), d (down),
 * l (left), r (right). placing a mark will now be done with ".".
 */
int
CompileKeys(s, array)
char *s, *array;
{
  int i;
  unsigned char key, value;

  if (!s || !*s)
    {
      for (i = 0; i < 256; i++)
        array[i] = i;
      return 0;
    }
  debug1("CompileKeys: '%s'\n", s);
  while (*s)
    {
      s = ParseChar(s, (char *) &key);
      if (*s != '=')
	return -1;
      do 
	{
          s = ParseChar(++s, (char *) &value);
	  array[value] = key;
	}
      while (*s == '=');
      if (!*s) 
	break;
      if (*s++ != ':')
	return -1;
    }
  return 0;
}
#endif /* COPY_PASTE */

/*
 *  Asynchronous input functions
 */

#ifdef POW_DETACH
static void
pow_detach_fn(buf, len)
char *buf;
int len;
{
  if (len)
    {
      *buf = 0;
      return;
    }
  if (ktab[(int)(unsigned char)*buf].nr != RC_POW_DETACH)
    {
      if (display)
        write(d_userfd, "\007", 1);
#ifdef NETHACK
      if (nethackflag)
	 Msg(0, "The blast of disintegration whizzes by you!");
#endif
    }
  else
    Detach(D_POWER);
}
#endif /* POW_DETACH */

#ifdef COPY_PASTE
static void
copy_reg_fn(buf, len)
char *buf;
int len;
{
  struct plop *pp = plop_tab + (int)(unsigned char)*buf;

  if (len)
    {
      *buf = 0;
      return;
    }
  if (pp->buf)
    free(pp->buf);
  if ((pp->buf = (char *)malloc(d_user->u_copylen)) == NULL)
    {
      Msg(0, strnomem);
      return;
    }
  bcopy(d_user->u_copybuffer, pp->buf, d_user->u_copylen);
  pp->len = d_user->u_copylen;
  Msg(0, "Copied %d characters into register %c", d_user->u_copylen, *buf);
}

static void
ins_reg_fn(buf, len)
char *buf;
int len;
{
  struct plop *pp = plop_tab + (int)(unsigned char)*buf;

  if (len)
    {
      *buf = 0;
      return;
    }
  if (pp->buf)
    {
      if (fore->w_pastebuf)
        free(fore->w_pastebuf);
      fore->w_pastebuf = 0;
      fore->w_pasteptr = pp->buf;
      fore->w_pastelen = pp->len;
      return;
    }
#ifdef NETHACK
  if (nethackflag)
    Msg(0, "Nothing happens.");
  else
#endif
  Msg(0, "Empty register.");
}
#endif /* COPY_PASTE */

static void
process_fn(buf, len)
char *buf;
int len;
{
  struct plop *pp = plop_tab + (int)(unsigned char)*buf;

  if (len)
    {
      *buf = 0;
      return;
    }
  if (pp->buf)
    {
      ProcessInput(pp->buf, pp->len);
      return;
    }
#ifdef NETHACK
  if (nethackflag)
    Msg(0, "Nothing happens.");
  else
#endif
  Msg(0, "Empty register.");
}


#ifdef PASSWORD

/* ARGSUSED */
static void
pass1(buf, len)
char *buf;
int len;
{
  strncpy(Password, buf, sizeof(Password) - 1);
  Input("Retype new password:", sizeof(Password) - 1, pass2, 1);
}

/* ARGSUSED */
static void
pass2(buf, len)
char *buf;
int len;
{
  int st;
  char salt[2];

  if (buf == 0 || strcmp(Password, buf))
    {
#ifdef NETHACK
      if (nethackflag)
	Msg(0, "[ Passwords don't match - your armor crumbles away ]");
      else
#endif /* NETHACK */
        Msg(0, "[ Passwords don't match - checking turned off ]");
      CheckPassword = 0;
    }
  if (Password[0] == '\0')
    {
      Msg(0, "[ No password - no secure ]");
      CheckPassword = 0;
    }
  for (st = 0; st < 2; st++)
    salt[st] = 'A' + (int)((time(0) >> 6 * st) % 26);
  strncpy(Password, crypt(Password, salt), sizeof(Password));
  if (CheckPassword)
    {
#ifdef COPY_PASTE
      if (d_user->u_copybuffer)
	UserFreeCopyBuffer(d_user);
      d_user->u_copylen = strlen(Password);
      if ((d_user->u_copybuffer = (char *) malloc(d_user->u_copylen + 1)) == NULL)
	{
	  Msg(0, strnomem);
          d_user->u_copylen = 0;
	}
      else
	{
	  strcpy(d_user->u_copybuffer, Password);
	  Msg(0, "[ Password moved into copybuffer ]");
	}
#else				/* COPY_PASTE */
      Msg(0, "[ Crypted password is \"%s\" ]", Password);
#endif				/* COPY_PASTE */
    }
  if (buf)
    bzero(buf, strlen(buf));
}
#endif /* PASSWORD */

