/* Copyright (c) 1991
 *      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 1, 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.
 *
 * Noteworthy contributors to screen's design and implementation:
 *	Wayne Davison (davison@borland.com)
 *	Patrick Wolfe (pat@kai.com, kailand!pat)
 *	Bart Schaefer (schaefer@cse.ogi.edu)
 *	Nathan Glasser (nathan@brokaw.lcs.mit.edu)
 *	Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu)
 *	Howard Chu (hyc@hanauma.jpl.nasa.gov)
 *	Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
 *	Markku Jarvinen (mta@{cc,cs,ee}.tut.fi)
 *	Marc Boucher (marc@CAM.ORG)
 *
 ****************************************************************
 */

#ifndef lint
  static char rcs_id[] = "$Id: fileio.c,v 1.3 92/02/04 21:20:45 jnweiger Exp $ FAU";
#endif

#if defined(pyr) || defined(MIPS) || defined(GOULD_NP1) || defined(B43)
extern int errno;
#endif
#include <sys/types.h>
#ifndef sgi
# include <sys/file.h>
#endif /* sgi */
#include <sys/stat.h>
#include <fcntl.h>

#ifdef BSDI
# include <sys/signal.h>
#endif /* BSDI */

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

#ifdef _SEQUENT_
# define UTHOST		/* _SEQUENT_ has got ut_find_host() */
#endif

#ifndef GETUTENT
# ifdef GETTTYENT
#  include <ttyent.h>
# else
struct ttyent
{
  char *ty_name;
};
static char *tt, *ttnext;
static char ttys[] = "/etc/ttys";
# endif
#endif

#if defined(UTMPOK) && defined(GETUTENT) && !defined(SVR4)
# if defined(hpux) /* cruel hpux release 8.0 */
#  define pututline _pututline
# endif /* hpux */
# if !defined(linux)
extern struct utmp *getutline(), *pututline();
#endif
# if defined(_SEQUENT_)
extern struct utmp *ut_add_user(), *ut_delete_user();
extern char *ut_find_host();
# endif
#endif
#ifdef NETHACK
extern nethackflag;
#endif
int hardcopy_append = 0;
int all_norefresh = 0;

extern char *RcFileName, *home, *extra_incap, *extra_outcap;
extern char *BellString, *ActivityString, *ShellProg, *ShellArgs[];
extern char *BufferFile, *PowDetachString, *VisualBellString;
extern int VBellWait, MsgWait, MsgMinWait;
extern struct key ktab[];
extern char Esc, MetaEsc;
extern char *shellaka, SockPath[], *SockNamePtr, *LoginName;
extern int loginflag, allflag, TtyMode, auto_detach;
extern int iflag, rflag, dflag;
extern int default_flow, wrap;
extern HS, termcapHS, use_hardstatus, visual_bell, default_monitor;
extern int default_histheight;
extern int default_startup;
extern int slowpaste;
extern DeadlyMsg, HasWindow;
extern ForeNum, screenwidth, screenheight;
extern char display_tty[];
extern struct win *fore;
extern char screenterm[];
extern int join_with_cr;
extern struct mode OldMode, NewMode;
extern int HasWindow;
extern char mark_key_tab[];
extern int real_uid, eff_uid;
extern int real_gid, eff_gid;

#ifdef PASSWORD
int CheckPassword;
char Password[20];
#endif

#ifdef COPY_PASTE
extern char *copybuffer;
extern copylen;
#endif

static char *CatExtra __P((char *, char *));
static char **SaveArgs __P((int, char **));
static int Parse __P((char *, char *[]));
static char *ParseChar __P((char *, char *));
static void ParseNum __P((int, char *[], int*));
static void ParseOnOff __P((int, char *[], int*));
static void ParseSaveStr __P((int, char *[], char **, char *));
static int IsNum __P((char *, int));
static int IsNumColon __P((char *, int, char *, int));
static slot_t TtyNameSlot __P((char *));

#if !defined(GETTTYENT) && !defined(GETUTENT)
static void setttyent __P((void));
static struct ttyent *getttyent __P((void));
#endif

/*
 * XXX: system
 */
extern time_t time __P((time_t *));
#if !defined(BSDI) && !defined(SVR4) && !defined(linux)
extern char *getpass __P((char *));
#endif /* !BSDI && !SVR4 */

char *KeyNames[] = 
{
  "screen",
  "select0", "select1", "select2", "select3", "select4",
  "select5", "select6", "select7", "select8", "select9",
  "aka", "clear", "colon", "copy", "detach", "flow",
  "hardcopy", "help", "histnext", "history", "info", "kill", "lastmsg",
  "license",
  "lockscreen", "log", "login", "monitor", "next", "other", "paste",
  "pow_detach", "prev", "quit", "readbuf", "redisplay", "removebuf",
  "reset", "set", "shell", "suspend", "termcap", "time", "vbell",
  "version", "width", "windows", "wrap", "writebuf", "xoff", "xon",
  0,
};


/* Must be in alpha order !!! */

char *RCNames[] =
{
  "activity", "all", "autodetach", "bell", "bind", "bufferfile", "chdir",
  "crlf", "echo", "escape", "flow", "hardcopy_append", "hardstatus", "login", 
  "markkeys", "mode", "monitor", "msgminwait", "msgwait", "nethack", "password",
  "pow_detach_msg", "redraw", "refresh", "screen", "scrollback", "shell", 
  "shellaka", "sleep", "slowpaste", "startup_message", "term", "termcap",
  "terminfo", "vbell", "vbell_msg", "vbellwait", "visualbell",
  "visualbell_msg", "wrap",
};

enum RCcases
{
  RC_ACTIVITY,
  RC_ALL,
  RC_AUTODETACH,
  RC_BELL,
  RC_BIND,
  RC_BUFFERFILE,
  RC_CHDIR,
  RC_CRLF,
  RC_ECHO,
  RC_ESCAPE,
  RC_FLOW,
  RC_HARDCOPY_APP,
  RC_HARDSTATUS,
  RC_LOGIN,
  RC_MARKKEYS,
  RC_MODE,
  RC_MONITOR,
  RC_MSGMINWAIT,
  RC_MSGWAIT,
  RC_NETHACK,
  RC_PASSWORD,
  RC_POW_DETACH_MSG,
  RC_REDRAW,
  RC_REFRESH,
  RC_SCREEN,
  RC_SCROLLBACK,
  RC_SHELL,
  RC_SHELLAKA,
  RC_SLEEP,
  RC_SLOWPASTE,
  RC_STARTUP_MESSAGE,
  RC_TERM,
  RC_TERMCAP,
  RC_TERMINFO,
  RC_VBELL,
  RC_VBELL_MSG,
  RC_VBELLWAIT,
  RC_VISUALBELL,
  RC_VISUALBELL_MSG,
  RC_WRAP,
  RC_RCEND
};

#ifdef UTMPOK
static utmp, utmpf;
static char UtmpName[] = UTMPFILE;
# ifdef MIPS
  static utmpfappend;
# endif
#endif

static FILE *fp = NULL;
static char *rc_name;

char *SaveStr(str)
register char *str;
{
  register char *cp;

  if ((cp = malloc(strlen(str) + 1)) == NULL)
    Msg_nomem;
  else
    strcpy(cp, str);
  return cp;
}

static char *CatExtra(str1, str2)
register char *str1, *str2;
{
  register char *cp;
  register int len1, len2, add_colon;

  len1 = strlen(str1);
  if (len1 == 0)
    return(str2);
  add_colon = (str1[len1 - 1] != ':');
  if (str2)
    {
      len2 = strlen(str2);
      if ((cp = realloc(str2, (unsigned) len1 + len2 + add_colon + 1)) == NULL)
	Msg_nomem;
      bcopy(cp, cp + len1 + add_colon, len2 + 1);
    }
  else
    {
      if (len1 == 0)
	return 0;
      if ((cp = malloc((unsigned) len1 + add_colon + 1)) == NULL)
	Msg_nomem;
      cp[len1 + add_colon] = '\0'; 
    }
  bcopy(str1, cp, len1);
  if (add_colon)
    cp[len1] = ':';

  return cp;
}

static char *findrcfile(rcfile)
char *rcfile;
{
  static char buf[256];
  char *rc, *p;

  if (rcfile)
    {
      rc = SaveStr(rcfile);
      debug1("findrcfile: you specified '%s'\n", rcfile);
    }
  else
    {
      debug("findrcfile: you specified nothing...\n");
      if ((p = getenv("ISCREENRC")) != NULL && *p != '\0')
	{
	  debug1("  ... but $ISCREENRC has: '%s'\n", p);
	  rc = SaveStr(p);
	}
      else if ((p = getenv("SCREENRC")) != NULL && *p != '\0')
	{
	  debug1("  ... but $SCREENRC has: '%s'\n", p);
	  rc = SaveStr(p);
	}
      else
	{
	  debug("  ...nothing in $SCREENRC, defaulting $HOME/.screenrc\n");
	  if (strlen(home) > 244)
	    Msg(0, "Rc: home too large");
	  sprintf(buf, "%s/.iscreenrc", home);
          if (access(buf, R_OK))
	    sprintf(buf, "%s/.screenrc", home);
	  rc = SaveStr(buf);
	}
    }
  return rc;
}

/*
 * this will be called twice:
 * 1) rcfilename = "/etc/screenrc"
 * 2) rcfilename = RcFileName
 */
void
StartRc(rcfilename)
char *rcfilename;
{
  register int argc, len;
  register char *p, *cp;
  char buf[256];
  char *args[MAXARGS], *t;

  rc_name = findrcfile(rcfilename);

  if ((fp = secfopen(rc_name, "r")) == NULL)
    {
      if (RcFileName && strcmp(RcFileName, rc_name) == 0)
	{
          /*
           * User explicitly gave us that name,
           * this is the only case, where we get angry, if we can't read
           * the file.
           */
	  debug3("StartRc: '%s','%s', '%s'\n", RcFileName, rc_name, rcfilename);
          Msg(0, "Unable to open \"%s\".", rc_name);
	  /* NOTREACHED */
	}
      debug1("StartRc: '%s' no good. ignored\n", rc_name);
      Free(rc_name);
      rc_name = "";
      return;
    }
  if ((t = getenv("TERM")) == NULL)
    Msg(0, "No TERM in environment.");
  debug1("startrc got termcp:%s\n", t);
  while (fgets(buf, sizeof buf, fp) != NULL)
    {
      if ((p = rindex(buf, '\n')) != NULL)
	*p = '\0';
      if ((argc = Parse(buf, args)) == 0)
	continue;
      if (strcmp(args[0], "echo") == 0)
	{
	  if (argc < 2 || (argc == 3 && strcmp(args[1], "-n")) || argc > 3)
	    {
	      DeadlyMsg = 0;
	      Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
	    }
	  else
	    {
	      printf((argc == 3) ? "%s" : "%s\r\n", args[argc - 1]);
	    }
	}
      else if (strcmp(args[0], "sleep") == 0)
	{
	  if (argc != 2)
	    {
	      DeadlyMsg = 0;
	      Msg(0, "%s: sleep: one numeric argument expected.", rc_name);
	    }
	  else
	    sleep(atoi(args[1]));
	}
#ifdef TERMINFO
      else if (strcmp(args[0], "terminfo") == 0)
#else
      else if (strcmp(args[0], "termcap") == 0)
#endif
	{
	  if (argc < 3 || argc > 4)
	    Msg(0, "%s: %s: incorrect number of arguments.", rc_name, args[0]);
	  for (p = args[1]; p && *p; p = cp)
	    {
	      if ((cp = index(p, '|')) != 0)
		*cp++ = '\0';
	      len = strlen(p);
	      if (p[len - 1] == '*')
		{
		  if (!(len - 1) || !strncmp(p, t, len - 1))
		    break;
		}
	      else if (!strcmp(p, t))
		break;
	    }
	  if (!(p && *p))
	    continue;
	  extra_incap = CatExtra(args[2], extra_incap);
	  if (argc == 4)
	    extra_outcap = CatExtra(args[3], extra_outcap);
	}
    }
  fclose(fp);
  Free(rc_name);
  rc_name = "";
}

static char *
ParseChar(p, cp)
char *p, *cp;
{
  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;
}

/*
 * 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;
    }
  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;
}

static char **SaveArgs(argc, argv)
register int argc;
register char **argv;
{
  register char **ap, **pp;

  if ((pp = ap = (char **) malloc((unsigned) (argc + 1) * sizeof(char **))) == 0)
    Msg_nomem;
#ifdef notdef
  debug("saveargs:\n"); 
#endif
  while (argc--)
    {
      debug1(" '%s'", *argv);
      *pp++ = SaveStr(*argv++);
    }
  debug("\n");
  *pp = 0;
  return ap;
}

void
FinishRc(rcfilename)
char *rcfilename;
{
  /* in FinishRc screen is not yet open, thus Msg() is deadly here.
   */
  char buf[256];

  rc_name = findrcfile(rcfilename);

  if ((fp = secfopen(rc_name, "r")) == NULL)
    {
      if (RcFileName && strcmp(RcFileName, rc_name) == 0)
	{
    	  /*
 	   * User explicitly gave us that name, 
	   * this is the only case, where we get angry, if we can't read
	   * the file.
	   */
  	  debug3("FinishRc:'%s','%s','%s'\n", RcFileName, rc_name, rcfilename);
          Msg(0, "Unable to open \"%s\".", rc_name);
	  /* NOTREACHED */
	}
      debug1("FinishRc: '%s' no good. ignored\n", rc_name);
      Free(rc_name);
      rc_name = "";
      return;
    }

  debug("finishrc is going...\n");
  while (fgets(buf, sizeof buf, fp) != NULL)
    {
      RcLine(buf);
    }
  (void) fclose(fp);
  Free(rc_name);
  rc_name = "";
}

/*
 * this is a KEY_SET pressed
 */
void
DoSet(argv)
char **argv;
{
  char *p;
  static char buf[256];

  p = buf;
  debug("DoSet\n");
  if (!argv || !*argv || !**argv)
    {
      debug("empty DoSet\n");
      sprintf(buf, "set ");
      RcLine(buf);
      return;
    }
  sprintf(p, "set"); p+=3;
  while(*argv && (strlen(buf) + strlen(*argv) < 255))
    {
      sprintf(p, " %s", *argv++);
      p += strlen(p);
    }
  RcLine(buf);
}

/*
 *	"$HOST blafoo"   	-> "localhost blafoo"
 *	"${HOST}blafoo"	  	-> "localhostblafoo"
 *	"\$HOST blafoo" 	-> "$HOST blafoo"
 *	"\\$HOST blafoo"	-> "\localhost blafoo"
 *	"'$HOST ${HOST}'"	-> "'$HOST ${HOST}'" 
 *	"'\$HOST'"       	-> "'\$HOST'"
 *	"\'$HOST' $HOST"   	-> "'localhost' $HOST"
 */
static char *expand_env_vars(ss)
char *ss;
{
  static char ebuf[2048];
  register int esize = 2047, quofl = 0;
  register char *e = ebuf;
  register char *s = ss;
  register char *v;

  while (*s && *s != '\n' && esize > 0)
    {
      if (*s == '\'')
	quofl ^= 1;
      if (*s == '$' && !quofl)
	{
	  char *p, c;

	  p = ++s;
	  if (*s == '{')
	    {
	      p = ++s;
	      while (*p != '}')
	        if (*p++ == '\0')
	          return ss;
	    }
	  else
	    {
	      while (*p != ' ' && *p != '\0' && *p != '\n')
		p++;
	    }
	  c = *p;
	  debug1("exp: c='%c'\n", c);
	  *p = '\0';
	  if (v = getenv(s)) 
	    {
	      debug2("exp: $'%s'='%s'\n", s, v);
	      while (*v && esize-- > 0)
	        *e++ = *v++;
	    }
	  else 
	    debug1("exp: '%s' not env\n", s);
	  if ((*p = c) == '}')
	    p++;
	  s = p;
	}
      else
	{
	  if (s[0] == '\\' && !quofl)
	    if (s[1] == '$' || (s[1] == '\\' && s[2] == '$') ||
	        s[1] == '\'' || (s[1] == '\\' && s[2] == '\''))
	      s++;
	  *e++ = *s++;
	  esize--;
	}
    }
  if (esize <= 0)
    Msg(0, "expand_env_vars: buffer overflow\n");
  *e = '\0';
  return ebuf;
}

void
RcLine(ubuf)
char *ubuf;
{
  char *args[MAXARGS];
  register char *buf, *p, **pp, **ap;
  register int argc, setflag;
  int q, qq;
  char key;
  int low, high, mid, x;

  buf = expand_env_vars(ubuf); 

  ap = args;

  if ((p = rindex(buf, '\n')) != NULL)
    *p = '\0';
  if (strncmp("set ", buf, 4) == 0)
    {
      buf += 4;
      setflag = 1;
      debug1("RcLine: '%s' is a set command\n", buf);
    }
  else if (strncmp("se ", buf, 3) == 0)
    {
      buf += 3;
      setflag = 1;
      debug1("RcLine: '%s' is a se command\n", buf);
    }
  else
    {
      setflag = 0;
      debug1("RcLine: '%s'\n", buf);
    }
  if ((argc = Parse(buf, ap)) == 0)
    {
      if (setflag)
	{
	  DeadlyMsg = 0;
	  Msg(0, "%s: set what?\n", rc_name);
	}
      return;
    }

  low = 0;
  high = (int)RC_RCEND - 1;
  while (low <= high)
    {
      mid = (low + high) / 2;
      x = strcmp(ap[0], RCNames[mid]);
      if (x < 0)
        high = mid - 1;
      else if (x > 0)
        low = mid + 1;
      else
        break;
    }
  if (low > high)
    mid = (int)RC_RCEND;
  switch ((enum RCcases) mid)
    {
    case RC_ESCAPE:
      if (argc != 2 || !ParseEscape(ap[1]))
	{
	  DeadlyMsg = 0; 
	  Msg(0, "%s: two characters required after escape.", rc_name);
	  return;
	}
      if (Esc != MetaEsc)
	ktab[Esc].type = KEY_OTHER;
      else
	ktab[Esc].type = KEY_IGNORE;
      return;
    case RC_CHDIR:
      if (setflag)
	break;
      p = argc < 2 ? home : ap[1];
      if (chdir(p) == -1)
	{
	  DeadlyMsg = 0; 
	  Msg(errno, "%s", p);
	}
      return;
    case RC_SHELL:
      ParseSaveStr(argc, ap, &ShellProg, "shell");
      ShellArgs[0] = ShellProg;
      return;
    case RC_SHELLAKA:
      ParseSaveStr(argc, ap, &shellaka, "shellaka");
      return;
    case RC_SCREEN:
      if (setflag)
	break;
      DoScreen(rc_name, ap + 1);
      return;
    case RC_SLEEP:
    case RC_TERMCAP:
    case RC_TERMINFO:
      return;			/* Already handled */
    case RC_TERM:
      {
        char *tmp = NULL;

        ParseSaveStr(argc, ap, &tmp, "term");
        if (!tmp)
          return;
	if (strlen(tmp) >= 20)
	  {
	    DeadlyMsg = 0;
            Msg(0,"%s: term: argument too long ( < 20)", rc_name);
            Free(tmp);
	    return;
          }
        strcpy(screenterm, args[1]);
	Free(tmp);
        debug1("screenterm set to %s\n", screenterm);
        MakeTermcap(0);
        return;	
      }
    case RC_ECHO:
      if (HasWindow && *rc_name == '\0')
	{
	  /*
	   * user typed ^A:echo... well, echo isn't FinishRc's job,
	   * but as he wanted to test us, we show good will
	   */
	  DeadlyMsg = 0;
	  if (argc == 2 || (argc == 3 && !strcmp(ap[1], "-n")))
	    Msg(0, "%s", ap[argc - 1]);
	  else
 	    Msg(0, "%s: 'echo [-n] \"string\"' expected.", rc_name);
	}
      return;
    case RC_BELL:
      ParseSaveStr(argc, ap, &BellString, "bell");
      return;
    case RC_BUFFERFILE:
      ParseSaveStr(argc, ap, &BufferFile, "bufferfile");
      return;
    case RC_ACTIVITY:
      ParseSaveStr(argc, ap, &ActivityString, "activity");
      return;
    case RC_POW_DETACH_MSG:
      ParseSaveStr(argc, ap, &PowDetachString, "pow_detach");
      return;
    case RC_LOGIN:
#ifdef UTMPOK
      q = loginflag;
      ParseOnOff(argc, ap, &loginflag);
      if (fore && setflag)
	{
	  SlotToggle(loginflag?(1):(-1));
	  loginflag = q;
	}
#endif
      return;
    case RC_FLOW:
      if (argc == 3 && ap[2][0] == 'i')
	{
	  iflag = 1;
	  argc--;
	}
      if (argc == 2 && ap[1][0] == 'a')
	default_flow = FLOW_AUTOFLAG;
      else
	ParseOnOff(argc, ap, &default_flow);
      return;
    case RC_WRAP:
      ParseOnOff(argc, ap, &wrap);
      return;
    case RC_HARDSTATUS:
      ParseOnOff(argc, ap, &use_hardstatus);
      if (use_hardstatus)
	HS = termcapHS;
      else
	HS = 0;
      return;
    case RC_MONITOR:
	{
	  int f; 

	  ParseOnOff(argc, ap, &f);
	  if (fore && setflag)
	    fore->monitor = (f == 0) ? MON_OFF : MON_ON;
	  else
	    default_monitor = (f == 0) ? MON_OFF : MON_ON;
	}
      return;
    case RC_REDRAW:
    case RC_REFRESH:
	{
	  int r;

	  ParseOnOff(argc, ap, &r);
	  if (fore && setflag)
	    fore->norefresh = (r) ? 0 : 1;
	  else
	    {
	      all_norefresh = (r) ? 0 : 1;
	      if (all_norefresh)
	        Msg(0, "No refresh on window change!\n");
	      else
	        Msg(0, "Window specific refresh\n");
	    }
	}
      return;
    case RC_VBELL:
    case RC_VISUALBELL:
      ParseOnOff(argc, ap, &visual_bell);
      return;
    case RC_VBELLWAIT:
      ParseNum(argc, ap, &VBellWait);
      if (fore && rc_name[0] == '\0')
        Msg(0, "vbellwait set to %d seconds", VBellWait);
      return;
    case RC_MSGWAIT:
      ParseNum(argc, ap, &MsgWait);
      if (fore && rc_name[0] == '\0')
        Msg(0, "msgwait set to %d seconds", MsgWait);
      return;
    case RC_MSGMINWAIT:
      ParseNum(argc, ap, &MsgMinWait);
      if (fore && rc_name[0] == '\0')
        Msg(0, "msgminwait set to %d seconds", MsgMinWait);
      return;
    case RC_SCROLLBACK:
      if (fore && setflag)
	{
	  int i;

	  ParseNum(argc, ap, &i);
	  ChangeScrollback(fore, i, fore->width);
	  if (fore && rc_name[0] == '\0')
	    Msg(0, "scrollback set to %d", fore->histheight);
	}
      else
	ParseNum(argc, ap, &default_histheight);
      return;
    case RC_SLOWPASTE:
      ParseNum(argc, ap, &slowpaste);
      if (fore && rc_name[0] == '\0')
	Msg(0, "slowpaste set to %d milliseconds", slowpaste);
      return;
    case RC_MARKKEYS:
      {
        char *tmp = NULL;

        ParseSaveStr(argc, ap, &tmp, "markkeys");
        if (CompileKeys(ap[1], mark_key_tab))
	  {
	    DeadlyMsg = 0;
	    Msg(0, "%s: markkeys: syntax error.", rc_name);
	    Free(tmp);
	    return;
	  }
        debug1("markkeys %s\n", ap[1]);
        Free(tmp);
        return;
      }
#ifdef NETHACK
    case RC_NETHACK:
      ParseOnOff(argc, ap, &nethackflag);
      return;
#endif
    case RC_HARDCOPY_APP:
      ParseOnOff(argc, ap, &hardcopy_append);
      return;
    case RC_VBELL_MSG:
    case RC_VISUALBELL_MSG:
      ParseSaveStr(argc, ap, &VisualBellString, "vbell_msg");
      debug1(" new vbellstr '%s'\n", VisualBellString);
      return;
    case RC_MODE:
      if (argc != 2)
	{
	  DeadlyMsg = 0; 
	  Msg(0, "%s: mode: one argument required.", rc_name);
	  return;
	}
      if (!IsNum(ap[1], 7))
	{
	  DeadlyMsg = 0; 
	  Msg(0, "%s: mode: octal number expected.", rc_name);
	  return;
	}
      (void) sscanf(ap[1], "%o", &TtyMode);
      return;
    case RC_CRLF:
      ParseOnOff(argc, ap, &join_with_cr);
      return;
    case RC_AUTODETACH:
      ParseOnOff(argc, ap, &auto_detach);
      return;
    case RC_STARTUP_MESSAGE:
      ParseOnOff(argc, ap, &default_startup);
      return;
#ifdef PASSWORD
    case RC_PASSWORD:
      CheckPassword = 1;
      if (argc >= 2)
	{
	  strncpy(Password, ap[1], sizeof Password);
	  if (!strcmp(Password, "none"))
	    CheckPassword = 0;
	}
      else
	{
	  char *mstr = 0;
	  int msleep = 0, st;
          char salt[2];

#ifdef POSIX
	  if (HasWindow)
	    {
	      Msg(0, "Cannot ask for password on POSIX systems");
	      return;
	    }
#endif
	  /* there is a clear screen sequence in the buffer. */
	  fflush(stdout);
	  if (HasWindow)
	    {
              ClearDisplay();
	      SetTTY(0, &OldMode);
	    }
	  strncpy(Password, getpass("New screen password:"),
		  sizeof(Password));
	  if (strcmp(Password, getpass("Retype new password:")))
	    {
#ifdef NETHACK
              if (nethackflag)
	        mstr = "[ Passwords don't match - your armor crumbles away ]";
	      else
#endif
	      mstr = "[ Passwords don't match - checking turned off ]";
	      msleep = 1;
	      CheckPassword = 0;
	    }
	  if (Password[0] == '\0')
	    {
	      CheckPassword = 0;
	      mstr = "[ No password - no secure ]";
	      msleep = 1;
	    }
	  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 (copybuffer)

		Free(copybuffer);
	      copylen = strlen(Password);
	      if ((copybuffer = (char *) malloc(copylen+1)) == NULL)
		{
		  Msg_nomem;
		  return;
		}
	      strcpy(copybuffer, Password);
	      mstr = "[ Password moved into copybuffer ]";
	      msleep = 1;
#else				/* COPY_PASTE */
	      mstr = "[ Crypted password is \"%s\" ]";
	      msleep = 5;
#endif				/* COPY_PASTE */
	    }
          if (HasWindow)
	    {
	      SetTTY(0, &NewMode);
	      Activate(0); /* Redraw */
	      if (mstr)
	        {
	          Msg(0, mstr, Password);
	        }
	    }
          else
	    {
	      if (mstr)
	        {
	          printf(mstr, Password);
	          putchar('\n');
	          sleep(msleep);
	        }
              ClearDisplay();
	    }
	}
      debug1("finishrc: our password is: --%s%-- \n", Password);
      return;
#endif				/* PASSWORD */
    case RC_ALL:
      if (!setflag || !HasWindow || *rc_name)
        break;
      display_help();
      return;
    case RC_BIND:
      if (setflag)
	break;
      p = ap[1];
      if (argc < 2 || *p == '\0')
	{
	  DeadlyMsg = 0; 
	  Msg(0, "%s: key expected after bind.", rc_name);
	  return;
	}
      if ((p = ParseChar(p, &key)) == NULL || *p)
	{
	  DeadlyMsg = 0; 
	  Msg(0, "%s: bind: character, ^x, or (octal) \\032 expected.",
	      rc_name);
	  return;
	}
      if (ktab[key].type != KEY_IGNORE)
	{
	  ktab[key].type = KEY_IGNORE;
	  if ((pp = ktab[key].args) != NULL)
	    {
	      for (; *pp; pp++)
		Free(*pp);
	      Free(ktab[key].args);
	    }
	}
      if (argc > 2)
	{
	  for (pp = KeyNames; *pp; ++pp)
	    if (strcmp(ap[2], *pp) == 0)
	      break;
	  if (*pp)
	    {
	      ktab[key].type = (enum keytype) (pp - KeyNames + 1);
	      if (argc > 3)
		{
		  ktab[key].args = SaveArgs(argc - 3, ap + 3);
		}
	      else
		ktab[key].args = NULL;
	    }
	  else
	    {
	      ktab[key].type = KEY_CREATE;
	      ktab[key].args = SaveArgs(argc - 2, ap + 2);
	    }
	}
      return;
    case RC_RCEND:
    default:
	{
	  char ibuf[3];
	  /*
	   * now we are user-friendly: 
	   * if anyone typed a key name like "help" or "next" ...
	   * we did not match anything above. so look in the KeyNames table.
	   */
	  debug1("--ap[0] %s\n", ap[0]);
	  for (pp = KeyNames; *pp; ++pp)
	    if (strcmp(ap[0], *pp) == 0)
		break;
	  if (*pp == 0)
	    break;

	  ibuf[0] = Esc;
	  ibuf[1] = pp - KeyNames +1;
	  debug1("RcLine: it was a keyname: '%s'\n", *pp);
	  q = 2; qq = 0;
	  if (HasWindow)
	    ProcessInput(ibuf, &q, (char *)0, &qq, 0);
	  else
	    {
	      DeadlyMsg = 0; 
	      Msg(0, "%s: Key '%s' has no effect while no window open...\n",
	          rc_name, ap[0]);
	    }
	}
      return;
    }
  DeadlyMsg = 0; 
  Msg(0, "%s: unknown %skeyword \"%s\"", rc_name, 
      setflag?"'set' ":"", ap[0]);
}

static 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 (*p == '\0' || *p == '#')
	{
	  *p = '\0';
	  return argc;
	}
      if (argc > MAXARGS - 1)
	Msg(0, "%s: too many tokens.", rc_name);
      delim = 0;
      if (*p == '"' || *p == '\'')
	delim = *p++;
      argc++;
      *ap = p;
      *++ap = 0;
      while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
	++p;
      if (*p == '\0')
	{
	  if (delim)
	    {
	      DeadlyMsg = 0;
	      Msg(0, "%s: Missing quote.", rc_name);
	      return 0;
	}
	  return argc;
	}
      *p++ = '\0';
    }
}

int 
ParseEscape(p)
char *p;
{
  if ((p = ParseChar(p, &Esc)) == NULL ||
      (p = ParseChar(p, &MetaEsc)) == NULL || *p)
    return 0;
  return 1;
}

static void
ParseNum(argc, ap, var)
int argc;
char *ap[];
int *var;
{
  int i;
  char *p;

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

static void
ParseSaveStr(argc, ap, var, title)
int argc;
char *ap[];
char **var;
char *title;
{
  if (argc != 2)
    {
      DeadlyMsg = 0;
      Msg(0, "%s: %s: one argument required.", rc_name, title);
      return;
    }
  if (*var)
    Free(*var);
  *var = SaveStr(ap[1]);
  return;
}
 
static void
ParseOnOff(argc, ap, var)
int argc;
char *ap[];
int *var;
{
  register int num = -1;

  if (argc == 2 && ap[1][0] == 'o')
    {
      if (ap[1][1] == 'f')
	num = 0;
      else if (ap[1][1] == 'n')
	num = 1;
    }
  if (num < 0)
    {
      DeadlyMsg = 0;
      Msg(0, "%s: %s: invalid argument. Give 'on' or 'off'", rc_name, ap[0]);
      return;
    }
  *var = num;
}


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);
}

void
SlotToggle(how)
int how;
/*
 * how = 0	real toggle mode
 * how > 0	do try to set a utmp slot.
 * how < 0	try to withdraw a utmp slot
 *
 * slot = -1    window not logged in.
 * slot = 0     window not logged in, but should be logged in. 
 *              (unable to write utmp, or detached).
 */
{
  debug1("SlotToggle %d\n", how);
  if (how == 0)
    how = (fore->slot == (slot_t) -1)?(1):(-1);
    /* 
     * slot 0 or active -> we try to log out.
     * slot -1          -> we try to log in.
     */
#ifdef UTMPOK
  if (how > 0)
    {
      debug(" try to log in\n");
      if ((fore->slot == (slot_t) -1) || (fore->slot == (slot_t) 0))
	{
#ifdef USRLIMIT
          if (CountUsers() >= USRLIMIT)
            Msg(0, "User limit reached.");
          else
#endif
            {
              if (SetUtmp(fore, ForeNum) == 0)
                Msg(0, "This window is now logged in.");
              else
                Msg(0, "This window should now be logged in.");
            }
	}
      else
	Msg(0, "This window is already logged in.");
    }
  else if (how < 0)
    {
      debug(" try to log out\n");
      if (fore->slot == (slot_t) -1)
	Msg(0, "This window is already logged out\n");
      else if (fore->slot == (slot_t) 0)
	{
	  debug("What a relief! In fact, it was not logged in\n");
	  Msg(0, "This window is not logged in.");
	  fore->slot = (slot_t) -1;
	}
      else
	{
	  RemoveUtmp(fore);
	  if (fore->slot != (slot_t) -1)
	    Msg(0, "What? Cannot remove Utmp slot?");
	  else
	    Msg(0, "This window is no longer logged in.");
	}
    }
#else	/* !UTMPOK */
  Msg(0, "Unable to modify %s.\n", UTMPFILE);
#endif
}

void
DoScreen(fn, av)
char *fn, **av;
{
  register int flowflag, num, lflag = loginflag, aflag = 0;
  register char *aka = NULL;
  register int histheight = default_histheight;
  char buf[20];
  char termbuf[25];
  char *termp;
  char *args[2];

  flowflag = default_flow;
  termbuf[0] = '\0';
  termp = NULL;
  while (av && *av && av[0][0] == '-')
    {
      switch (av[0][1])
	{
	case 'f':
	  switch (av[0][2])
	    {
	    case 'n':
	    case '0':
	      flowflag = FLOW_NOW * 0;
	      break;
	    case 'y':
	    case '1':
	    case '\0':
	      flowflag = FLOW_NOW * 1;
	      break;
	    case 'a':
	      flowflag = FLOW_AUTOFLAG;
	      break;
	    default:
	      break;
	    }
	  break;
	case 'k':
	case 't':
	  if (av[0][2])
	    aka = &av[0][2];
	  else if (*++av)
	    aka = *av;
	  else
	    --av;
	  break;
	case 'T':
	  if (av[0][2])
	    termp = &av[0][2];
	  else if (*++av)
	    termp = *av;
	  else
	    --av;
	  break;
	case 'h':
	  if (av[0][2])
	    histheight = atoi(av[0] + 2);
	  else if (*++av)
	    histheight = atoi(*av);
	  else 
	    --av;
	  break;
	case 'l':
	  switch (av[0][2])
	    {
	    case 'n':
	    case '0':
	      lflag = 0;
	      break;
	    case 'y':
	    case '1':
	    case '\0':
	      lflag = 1;
	      break;
	    default:
	      break;
	    }
	  break;
	case 'a':
	  aflag = 1;
	  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')
	aka = buf;
      num = atoi(*av);
      if (num < 0 || num > MAXWIN - 1)
	{
	  Msg(0, "%s: illegal screen number %d.", fn, num);
	  num = 0;
	}
      ++av;
    }
  if (!av || !*av)
    {
      av = args;
      av[0] = ShellProg;
      av[1] = NULL;
      if (!aka)
	aka = shellaka;
    }
  MakeWindow(aka, av, aflag, flowflag, num, (char *) 0, lflag, histheight, termp);
}

void
WriteFile(dump)
int dump;
{
  /* dump==0:	create .termcap,
   * dump==1:	hardcopy,
   * #ifdef COPY_PASTE
   * dump==2:	BUFFERFILE
   * #endif COPY_PASTE 
   */
  register int i, j, k;
  register char *p;
  register FILE *f;
  char fn[1024];
  char *mode = "w";

  switch (dump)
    {
    case DUMP_TERMCAP:
      i = SockNamePtr - SockPath;
      strncpy(fn, SockPath, i);
      strcpy(fn + i, ".termcap");
      break;
    case DUMP_HARDCOPY:
      sprintf(fn, "hardcopy.%d", ForeNum);
      if (hardcopy_append && !access(fn, W_OK))
	mode = "a";
      break;
    case DUMP_EXCHANGE:
      sprintf(fn, "%s", BufferFile);
      umask(0);
      break;
    }

  debug2("WriteFile(%d) %s\n", dump, fn);
  if (UserContext() > 0)
    {
      debug("Writefile: usercontext\n");
      if ((f = fopen(fn, mode)) == NULL)
	{
	  debug2("WriteFile: fopen(%s,\"%s\") failed\n", fn, mode);
	  UserReturn(0);
	}
      else
	{
	  switch (dump)
	    {
	    case DUMP_HARDCOPY:
	      if (*mode == 'a')
		{
		  putc('>', f);
		  for (j = screenwidth - 2; j > 0; j--)
		    putc('=', f);
		  fputs("<\n", f);
		}
	      for (i = 0; i < screenheight; ++i)
		{
		  p = fore->image[i];
		  for (k = screenwidth - 1; k >= 0 && p[k] == ' '; --k)
		    ;
		  for (j = 0; j <= k; ++j)
		    putc(p[j], f);
		  putc('\n', f);
		}
	      break;
	    case DUMP_TERMCAP:
	      if ((p = index(MakeTermcap(fore->aflag), '=')) != NULL)
		{
		  fputs(++p, f);
		  putc('\n', f);
		}
	      break;
#ifdef COPY_PASTE
	    case DUMP_EXCHANGE:
	      p = copybuffer;
	      for (i = 0; i < copylen; i++)
		putc(*p++, f);
	      break;
#endif
	    }
	  (void) fclose(f);
	  UserReturn(1);
	}
    }
  if (UserStatus() <= 0)
    Msg(0, "Cannot open \"%s\"", fn);
  else
    {
      switch (dump)
	{
	case DUMP_TERMCAP:
	  Msg(0, "Termcap entry written to \"%s\".", fn);
	  break;
	case DUMP_HARDCOPY:
	  Msg(0, "Screen image %s to \"%s\".",
	      (*mode == 'a') ? "appended" : "written", fn);
	  break;
#ifdef COPY_PASTE
	case DUMP_EXCHANGE:
	  Msg(0, "Copybuffer written to \"%s\".", fn);
#endif
	}
    }
}

#ifdef COPY_PASTE

void
ReadFile()
{
  int i, l, size;
  char fn[1024], c;
  struct stat stb;

  sprintf(fn, "%s", BufferFile);
  debug1("ReadFile(%s)\n", fn);
  if ((i = secopen(fn, O_RDONLY, 0)) < 0)
    {
      Msg(errno, "no %s -- no slurp", fn);
      return;
    }
  if (fstat(i, &stb))
    {
      Msg(errno, "no good %s -- no slurp", fn);
      close(i);
      return;
    }
  size = stb.st_size;
  if (copybuffer)
    Free(copybuffer);
  copylen = 0;
  if ((copybuffer = malloc(size)) == NULL)
    {
      close(i);
      Msg_nomem;
      return;
    }
  errno = 0;
  if ((l = read(i, copybuffer, size)) != size)
    {
      copylen = (l > 0) ? l : 0;
#ifdef NETHACK
      if (nethackflag)
        Msg(errno, "You choke on your food: %d bytes", copylen);
      else
#endif
      Msg(errno, "Got only %d bytes from %s", copylen, fn);
      close(i);
      return;
    }
  copylen = l;
  if (read(i, &c, 1) > 0)
    Msg(0, "Slurped only %d characters into buffer - try again", copylen);
  else
    Msg(0, "Slurped %d characters into buffer", copylen);
  close(i);
  return;
}

void
KillBuffers()
{
  char fn[1024];
  sprintf(fn, "%s", BufferFile);
  errno = 0;
  if (access(fn, W_OK) == -1)
    {
      Msg(errno, "%s not removed", fn);
      return;
    }
  else
    {
      unlink(fn);
      Msg(errno, "%s removed", fn);
    }
}
#endif	/* COPY_PASTE */

#ifdef USRLIMIT
CountUsers()
{
#ifdef GETUTENT
  struct utmp *ut, *getutent();
#else
  struct utmp utmpbuf;
#endif
  int UserCount;

  debug1("CountUsers() - utmp=%d\n",utmp);
  if (!utmp)
    return(0);
  UserCount = 0;
#ifdef GETUTENT
  setutent();
  while (ut = getutent())
    if (ut->ut_type == USER_PROCESS)
      UserCount++;
#else
  (void) lseek(utmpf, (off_t) 0, 0);
  while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0)
    {
      if (utmpbuf.ut_name[0] != '\0')
       UserCount++;
    }
#endif
  return(UserCount);
}
#endif

#ifdef UTMPOK

static slot_t loginslot;
static struct utmp utmp_logintty;
#ifdef _SEQUENT_
static char loginhost[100+1];
#endif

void
InitUtmp()
{
  debug("InitUtmp testing...\n");
  if ((utmpf = open(UtmpName, O_RDWR)) == -1)
    {
      if (errno != EACCES)
	Msg(errno, UtmpName);
      debug("InitUtmp failed.\n");
      utmp = 0;
      return;
    }
#ifdef GETUTENT
  close(utmpf);
  utmpf= -1;
#endif
#ifdef MIPS
  if ((utmpfappend = open(UtmpName, O_APPEND)) == -1) 
    {
      if (errno != EACCES)
	Msg(errno, UtmpName);
      return;
    }
#endif
  utmp = 1;
#ifndef apollo
  ReInitUtmp();
#endif
}

void
ReInitUtmp()
{
#ifndef apollo
  if (!utmp)
    {
      debug("Reinitutmp: utmp == 0\n");
      return;
    }
#endif
  debug("(Re)InitUtmp: removing your logintty\n");
  loginslot = TtyNameSlot(display_tty);
  if (loginslot!=(slot_t)0 && loginslot!=(slot_t)-1)
    {
#ifdef _SEQUENT_
      if (p=ut_find_host(loginslot))
        strncpy(loginhost, p, 100);
#endif
      RemoveLoginSlot(loginslot, &utmp_logintty);
    }
  debug1(" slot %d zapped\n", loginslot);
}

void
RestoreLoginSlot()
{
  debug("RestoreLoginSlot()\n");
#ifdef apollo
  InitUtmp();
#endif
  if (utmp && loginslot!=(slot_t)0 && loginslot!=(slot_t)-1)
    {
#ifdef GETUTENT
# ifdef _SEQUENT_
      int fail;
      debug1(" logging you in again (slot %s)\n", loginslot);
/*
 * We have problems if we add the console and use ut_add_user()
 * because the id will be 'scon' instead of 'co'. So we
 * restore it with pututline(). The reason why we don't use
 * pututline all the time is that we want to set the host field.
 * Unfortunatelly this can only be done with ut_add_user().
 */
      if (*loginhost)
        {
          fail = (ut_add_user(LoginName, loginslot, utmp_logintty.ut_pid,
                              *loginhost?loginhost:(char *)0) == 0);
        }
      else
        {
          setutent();
          fail = (pututline(&utmp_logintty) == 0);
        }
      if (fail)
# else	/* _SEQUENT_ */
      debug1(" logging you in again (slot %s)\n", loginslot);
      setutent();
#ifdef linux
      pututline(&utmp_logintty);
      if (0)
#else
      if (pututline(&utmp_logintty)==0)
#endif /* linux */
# endif	/* _SEQUENT */
#else	/* GETUTENT */
      debug1(" logging you in again (slot %d)\n", loginslot);
# ifdef sequent
      /* call sequent undocumented routine to count logins and add utmp entry if possible */
      if (add_utmp(loginslot, &utmp_logintty) == -1)
# else
      (void) lseek(utmpf, (off_t) (loginslot * sizeof(struct utmp)), 0);
      if (write(utmpf, (char *) &utmp_logintty, sizeof(struct utmp))
	  != sizeof(struct utmp))
# endif /* sequent */
#endif	/* GETUTENT */
        {
#ifdef NETHACK
          if (nethackflag)
            Msg(errno, "%s is too hard to dig in.", UTMPFILE);
	  else
#endif
          Msg(errno,"Could not write %s.", UTMPFILE);
        }
    }
#ifdef apollo
  close(utmpf);
#endif
  loginslot = (slot_t) 0;
}

void
RemoveLoginSlot(slot, up)
slot_t slot;
struct utmp *up;
{
#ifdef GETUTENT
  struct utmp *uu;
#endif
  struct utmp u;
#ifdef apollo
  struct utmp *uq;
#endif

#ifdef GETUTENT
  debug2("RemoveLoginSlot(%s, %08x)\n", (slot == (slot_t) 0 ||
         slot == (slot_t) -1 ) ? "no slot" : slot, up);
#else
  debug2("RemoveLoginSlot(%d, %08x)\n", slot, up);
#endif
#ifdef apollo
  InitUtmp();
  bzero((char *)up, sizeof(struct utmp));
  uq = (struct utmp *)malloc(sizeof(struct utmp));
  bzero((char *)uq, sizeof(struct utmp));
#endif /* apollo */
  if (!utmp)
    return;
  if (slot != (slot_t) 0 && slot != (slot_t) -1)
    {
      bzero((char *) &u, sizeof u);
#ifdef GETUTENT
      setutent();
      strncpy(u.ut_line, slot, sizeof(u.ut_line));
      if ((uu = getutline(&u)) == 0)
        {
	  DeadlyMsg = 0;
          Msg(0, "Utmp slot not found -> not removed");
          return;
        }
      *up= *uu;
# ifdef _SEQUENT_
      if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0)
# else
      uu->ut_type = DEAD_PROCESS;
#ifndef linux
      uu->ut_exit.e_termination = 0;
      uu->ut_exit.e_exit= 0;
      if (pututline(uu) == 0)
#else
      pututline(uu);
      if (0)
#endif
# endif
#else
      (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
      if (read(utmpf, (char *) up, sizeof u) != sizeof u)
	{
	  DeadlyMsg = 0;
	  Msg(errno, "cannot read %s ???", UTMPFILE);
	  sleep(1);
	}
      (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
# ifdef apollo
      bcopy((char *)up, (char *)uq, sizeof(struct utmp));
      bzero(uq->ut_name, sizeof(uq->ut_name));
      bzero(uq->ut_host, sizeof(uq->ut_host));
      if (write(utmpf, (char *)uq, sizeof(struct utmp)) != sizeof(struct utmp))
# else
      if (write(utmpf, (char *) &u, sizeof u) != sizeof u)
# endif /* apollo */
#endif
        {
#ifdef NETHACK
          if (nethackflag)
	    {
	      DeadlyMsg = 0;
              Msg(errno, "%s is too hard to dig in.", UTMPFILE); 
	    }
          else
#endif
	    {
	      DeadlyMsg = 0;
              Msg(errno, "Could not write %s.", UTMPFILE);
	    }
        }
    }
  else 
    {
      debug1("There is no utmp-slot to be removed(%d)\n", slot);
    }
#ifdef apollo
  close(utmpf);
  free(uq);
#endif
}

static slot_t TtyNameSlot(nam)
char *nam;
{
  char *name;
  register slot_t slot;
#ifndef GETUTENT
  register struct ttyent *tp;
#endif
#ifdef apollo
  struct utmp *up;
#endif

  debug1("TtyNameSlot(%s)\n", nam);
#ifdef apollo
  InitUtmp();
#endif
  if (!utmp || nam == NULL)
    return (slot_t)0;
  name = stripdev(nam);
#ifdef GETUTENT
  slot = name;
#else
# ifdef apollo
  slot = 0;
  up = (struct utmp *)malloc(sizeof(struct utmp));
  while (1)
    {
      if ((read(utmpf, (char *)up, sizeof(struct utmp)) ==
	   sizeof(struct utmp)) && (strcmp(up->ut_line, name)))
	slot++;
      else
	break;
    }
  close(utmpf);
  free(up);
# else /* !apollo */
  slot = 1;
  setttyent();
  while ((tp = getttyent()) != NULL && strcmp(name, tp->ty_name) != 0)
    {
      debug2("'%s' %d, ", tp->ty_name, slot);
      ++slot;
    }
  debug("\n");
#  ifdef MIPS
  if (tp == NULL)
    {
      slot = CreateUtmp(name);
    }
#  endif /* MIPS */
# endif /* apollo */
#endif /* GETUTENT */
  return slot;
}

int
SetUtmp(wi, displaynumber)
struct win *wi;
int displaynumber;
{
  register char *p;
  register slot_t slot;
  char *line;
  struct utmp u;
#ifdef UTHOST
# ifdef _SEQUENT_
  char host[100+5];
# else
  char host[sizeof(utmp_logintty.ut_host)+5];
# endif
#endif

  wi->slot = (slot_t) 0;
  if (!utmp)
    return -1;
  if ((slot = TtyNameSlot(wi->tty)) == (slot_t) NULL)
    {
      debug1("SetUtmp failed (tty %s).\n",wi->tty);
      return -1;
    }
  debug2("SetUtmp %d will get slot %d...\n", displaynumber, (int)slot);
#ifdef apollo
  InitUtmp();
#endif

#ifdef UTHOST
  host[sizeof(host)-5] = '\0';
# ifdef _SEQUENT_
  strncpy(host, loginhost, sizeof(host) - 5);
# else
  strncpy(host, utmp_logintty.ut_host, sizeof(host) - 5);
# endif
  if (loginslot != (slot_t)0 && loginslot != (slot_t)-1 && host[0] != '\0')
    {
      /*
       * we want to set our ut_host field to something like
       * ":ttyhf:s.0" or
       * "faui45:s.0" or
       * "132.199.81.4:s.0" (even this may hurt..), but not
       * "faui45.informati"......:s.0
       */
      for (p = host; *p; p++)
	{
	  if ((*p < '0' || *p > '9') && (*p != '.'))
	    break;
	}
      if (*p)
	{
	  for (p = host; *p; p++)
	    {
	      if (*p == '.')
		{
		  *p = '\0';
		  break;
		}
	    }
	}
    }
  else
    {
      strncpy(host + 1, stripdev(display_tty), sizeof(host) - 6);
      host[0] = ':';
    }
  debug1("rlogin hostname: '%s'\n", host);
  sprintf(host + strlen(host), ":S.%c", '0' + displaynumber);
  debug1("rlogin hostname: '%s'\n", host);
#endif /* UTHOST */

  line = stripdev(wi->tty);
  bzero((char *) &u, sizeof u);

#ifdef GETUTENT
# ifdef _SEQUENT_
  if (ut_add_user(LoginName, slot, wi->wpid, host)==0)
# else
  strncpy(u.ut_user, LoginName, sizeof(u.ut_user));
  strncpy(u.ut_id, line + strlen(line) - 2, sizeof(u.ut_id));
  strncpy(u.ut_line, line, sizeof(u.ut_line));
  u.ut_pid = wi->wpid;
  u.ut_type = USER_PROCESS;
#  ifdef SVR4
    (void) time(&u.ut_tv.tv_sec);
    u.ut_tv.tv_usec=0;
#  else
    (void) time(&u.ut_time);
#  endif /* SVR4 */
#  ifdef UTHOST
  strncpy(u.ut_host, host, sizeof(u.ut_host));
#  endif /* UTHOST */
#ifndef linux
  if (pututline(&u) == 0)
#else
  pututline(&u);
  if (0)
#endif
# endif /* _SEQUENT_ */
#else	/* GETUTENT */
  strncpy(u.ut_line, line, sizeof(u.ut_line));
  strncpy(u.ut_name, LoginName, sizeof(u.ut_name));
# ifdef UTHOST
  strncpy(u.ut_host, host, sizeof(u.ut_host));
# endif	/* UTHOST */
# ifdef MIPS
  u.ut_type = 7; /* USER_PROCESS */
  strncpy(u.ut_id, line + 3, 4);
# endif /* MIPS */
  (void) time(&u.ut_time);
# ifdef sequent
/* call sequent undocumented routine to count logins and add utmp entry if possible */
  if (add_utmp(slot, &u) == -1)
# else
  (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
  if (write(utmpf, (char *) &u, sizeof u) != sizeof u)
# endif /* sequent */
#endif	/* GETUTENT */

    {
#ifdef NETHACK
      if (nethackflag)
        Msg(errno, "%s is too hard to dig in.", UTMPFILE);
      else
#endif
      Msg(errno,"Could not write %s.", UTMPFILE);
#ifdef apollo
      close(utmpf);
#endif
      return -1;
    }
  debug("SetUtmp successful\n");
  wi->slot = slot;
#ifdef apollo
  close(utmpf);
#endif
  return 0;
}

#ifdef MIPS

#define GETTTYENT
static int ttyfd = 0;

static void setttyent()
{
  if (ttyfd)
    close(ttyfd);
  ttyfd = open(UtmpName, O_RDONLY);
}

static struct ttyent *getttyent()
{
  static struct utmp u;
  static struct ttyent t;

  if (!ttyfd)
    return NULL;
  
  if (read(ttyfd, &u, sizeof u)) 
    {
      t.ty_name = u.ut_line;
      return &t;
    }
  return NULL;
}

CreateUtmp(name)
char *name;
{
  int slot;
  struct utmp u;

  strncpy(u.ut_line, name, 8);
  strncpy(u.ut_name, LoginName, 8);
  u.ut_type = 7; /* USER_PROCESS */
  strncpy(u.ut_id, name+3, 4);
  (void) time(&u.ut_time);
  slot = (lseek(utmpfappend, 0, 2) + 1) / sizeof u;
  (void) write(utmpfappend, (char *)&u, sizeof u);
  close(utmpfappend);
  if ((utmpfappend = open(UtmpName, O_APPEND)) == -1) 
    {
      if (errno != EACCES)
        Msg(errno, UtmpName);
      return;
    }
  return slot;
}
#endif /* MIPS */

/*
 * if slot could be removed or was 0,  wi->slot = -1;
 * else not changed.
 */
int
RemoveUtmp(wi)
struct win *wi;
{
#ifdef GETUTENT
  struct utmp *uu;
#endif
#ifdef apollo
  struct utmp *up;
#endif
  struct utmp u;
  slot_t slot;

  slot = wi->slot;
#ifdef GETUTENT
  debug1("RemoveUtmp(%s)\n", (slot == (slot_t) 0) ?
         "no slot (0)":((slot == (slot_t) -1) ? "no slot (-1)" : slot));
#else
  debug1("RemoveUtmp(wi.slot: %d)\n", slot);
#endif
#ifdef apollo
  InitUtmp();
  up = (struct utmp *)malloc(sizeof(struct utmp));
  bzero((char *)up, sizeof(struct utmp));
#endif /* apollo */
  if (!utmp)
    return -1;
  if (slot == (slot_t) 0 || slot == (slot_t) -1)
    {
      debug1("There is no utmp-slot to be removed(%d)\n", slot);
      wi->slot = (slot_t) -1;
      return 0;
    }
  bzero((char *) &u, sizeof u);
#ifdef GETUTENT
  setutent();
  strncpy(u.ut_line, slot, sizeof(u.ut_line));
  if ((uu = getutline(&u)) == 0)
    {
      Msg(0, "Utmp slot not found -> not removed");
      return -1;
    }
# ifdef _SEQUENT_
  if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0)
# else
  uu->ut_type = DEAD_PROCESS;
#ifndef linux
  uu->ut_exit.e_termination = 0;
  uu->ut_exit.e_exit= 0;
  if (pututline(uu) == 0)
#else
  pututline(uu);
  if (0)
#endif
# endif
#else	/* GETUTENT */
  (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
# ifdef apollo
  if (read(utmpf, (char *) up, sizeof u) != sizeof u)
    {
      DeadlyMsg = 0;
      Msg(errno, "cannot read %s?", UTMPFILE);
      sleep(1);
    }
  (void) lseek(utmpf, (off_t) (slot * sizeof u), 0);
  bzero(up->ut_name, sizeof(u.ut_name));
  bzero(up->ut_host, sizeof(u.ut_host));
  if (write(utmpf, (char *)up, sizeof u) != sizeof u)
# else
  if (write(utmpf, (char *) &u, sizeof u) != sizeof u)
# endif /* apollo */
#endif
    {
#ifdef NETHACK
      if (nethackflag)
        Msg(errno, "%s is too hard to dig in.", UTMPFILE);
      else
#endif
      Msg(errno,"Could not write %s.", UTMPFILE);
#ifdef apollo
      close(utmpf);
      free(up);
#endif
      return -1;
    }
  debug("RemoveUtmp successfull\n");
  wi->slot = (slot_t) -1;
#ifdef apollo
  close(utmpf);
  free(up);
#endif
  return 0;
}

#endif	/* UTMPOK */

char *
stripdev(nam)
char *nam;
{
#ifdef apollo
  char *p;

  if (nam == NULL)
    return NULL;
  if (p = strstr(nam,"/dev/"))
    return p + 5;
#else
  if (nam == NULL)
    return NULL;
  if (strncmp(nam, "/dev/", 5) == 0)
    return nam + 5;
#endif
  return nam;
}

#if !defined(GETTTYENT) && !defined(GETUTENT)

static void setttyent()
{
  struct stat s;
  register int f;
  register char *p, *ep;

  if (ttnext)
    {
      ttnext = tt;
      return;
    }
  if ((f = open(ttys, O_RDONLY)) == -1 || fstat(f, &s) == -1)
    Msg(errno, ttys);
  if ((tt = malloc((unsigned) s.st_size + 1)) == 0)
    Msg_nomem;
  if (read(f, tt, s.st_size) != s.st_size)
    Msg(errno, ttys);
  close(f);
  for (p = tt, ep = p + s.st_size; p < ep; ++p)
    if (*p == '\n')
      *p = '\0';
  *p = '\0';
  ttnext = tt;
}

static struct ttyent *getttyent()
{
  static struct ttyent t;

  if (*ttnext == '\0')
    return NULL;
  t.ty_name = ttnext + 2;
  ttnext += strlen(ttnext) + 1;
  return &t;
}

#endif	/* GETTTYENT */

/*
 * (Almost) secure open and fopen... mlschroe.
 */

FILE *
secfopen(name, mode)
char *name;
char *mode;
{
  FILE *fi;
#ifdef NOREUID
  int flags, fd;
#endif

  debug2("secfopen(%s, %s)\n", name, mode);
  if (eff_uid == real_uid)
    return(fopen(name, mode));
#ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
  fi = fopen(name, mode);
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
#else
  if (mode[0] && mode[1] == '+')
    flags = O_RDWR;
  else
    flags = (mode[0] == 'r') ? O_RDONLY : O_WRONLY;
  if (mode[0] == 'w')
    flags |= O_CREAT | O_TRUNC;
  else if (mode[0] == 'a')
    flags |= O_CREAT | O_APPEND;
  else if (mode[0] != 'r')
    {
      errno = EINVAL;
      return(0);
    }
  if ((fd = secopen(name, flags, 0666)) < 0)
    return(0);
  if ((fi = fdopen(fd, mode)) == 0)
    {
      close(fd);
      return(0);
    }
#endif
  return(fi);
}


int
secopen(name, flags, mode)
char *name;
int flags;
int mode;
{
  int fd;
#ifdef NOREUID
  int q;
  struct stat stb;
#endif

  debug3("secopen(%s, 0x%x, 0%03o)\n", name, flags, mode);
  if (eff_uid == real_uid)
    return(open(name, flags, mode));
#ifndef NOREUID
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
  fd = open(name, flags, mode);
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
#else
  /* Truncation/creation is done in UserContext */
  if ((flags & O_TRUNC) || ((flags & O_CREAT) && access(name, F_OK)))
    {
      if (UserContext() > 0)
	{
          if ((fd = open(name, flags, mode)) >= 0)
	    {
	      close(fd);
	      UserReturn(0);
            }
	  if (errno == 0)
	    errno = EACCES;
	  UserReturn(errno);
	}
      if (q = UserStatus())
	{
	  if (q > 0)
	    errno = q;
          return(-1);
	}
    }
  if (access(name, F_OK))
    return(-1);
  if ((fd = open(name, flags & ~(O_TRUNC | O_CREAT), 0)) < 0)
    return(-1);
  debug("open successful\n");
  if (fstat(fd, &stb))
    {
      close(fd);
      return(-1);
    }
  debug("fstat successful\n");
  if (stb.st_uid != real_uid)
    {
      switch (flags & (O_RDONLY | O_WRONLY | O_RDWR))
        {
	case O_RDONLY:
	  q = 0004;
	  break;
	case O_WRONLY:
	  q = 0002;
	  break;
	default:
	  q = 0006;
	  break;
        }
      if ((stb.st_mode & q) != q)
	{
          debug("secopen: permission denied\n");
	  close(fd);
	  errno = EACCES;
	  return(-1);
	}
    }
#endif
  debug1("secopen ok - returning %d\n", fd);
  return(fd);
}

#if defined(BUGGYGETLOGIN) && defined(UTMPOK)
char *
getlogin()
{
  char *tty;
#ifdef utmp
# undef utmp
#endif
  struct utmp u;
  static char retbuf[sizeof(u.ut_user)+1];
  int fd;

  for (fd = 0; fd <= 2 && (tty = ttyname(fd)) == NULL; fd++)
    ;
  if ((tty == NULL) || ((fd = open(UTMP_FILE, O_RDONLY)) < 0))
    return NULL;
  tty = stripdev(tty);
  retbuf[0] = '\0';
  while (read(fd, &u, sizeof(struct utmp)) == sizeof(struct utmp))
    {
      if (!strncmp(tty, u.ut_line, sizeof(u.ut_line)))
	{
	  strncpy(retbuf, u.ut_user, sizeof(u.ut_user));
	  retbuf[sizeof(u.ut_user)] = '\0';
	  if (u.ut_type == USER_PROCESS)
	    break;
	}
    }
  close(fd);

  return *retbuf ? retbuf : NULL;
}
#endif
