[net.sources] Phone program source

fidder@isucs1.UUCP (11/08/84)

Here is the source to an interuser phone program written for 4.2BSD.
I consider it better then talk on a single host, but only you can 
be the judge of that.
If you have any comments or susgestions please let me know. One last thing
if you have not fixed some of the the socket bugs in 4.2 you should be
carefull about running this program (esp. the bug when closing sockets
while someone has connected and you have not accepted yet.)


					Ted Fidder
					USENET: isucs1!fidder
					CSNET: fidder@iowa-state

/* Begin PARANOIA */

#----------CUT HERE ----------------------------------------------
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile cparse.c cparse.h getuser.c phone.1 phone.c phone.h phoned.c phonemain.c phoneparse.c slotmanager.c ttycmio.c ttydumbio.c ttyio.c

echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
#---------------------------------------------------------
#	Programmer name: Kevin Buettner
#	Project:         4.2BSD Phone
#
#	Module:          Makefile
#	Abstract:	 
#			 
#	Required files:  phone stuff
#	Date written:    
#
#-=-=-=-=-=-Revision: 11/6/84
#	Name:	Ted Fidder
#	Reason:	Redo Makefile
#		
#=======================================================*/

INSTDIR= /usr/local
MANDIR= /usr/man/man1
INCLUDE= phone.h cparse.h
SRCS= phone.c getuser.c slotmanager.c phonemain.c ttyio.c ttycmio.c \
	ttydumbio.c phoneparse.c cparse.c phoned.c
BIN= root
BINGRP=wheel

CFLAGS= -O

all: phone phoned

phone : phone.h phone.o getuser.o slotmanager.o phonemain.o ttyio.o ttycmio.o \
	 ttydumbio.o phoneparse.o cparse.o
	cc ${CFLAGS} phone.o getuser.o slotmanager.o phonemain.o \
	ttyio.o ttycmio.o ttydumbio.o phoneparse.o cparse.o -ltermlib -o phone

phoned : phoned.o
	cc ${CFLAGS} phoned.o -o phoned

clean: 
	rm *.o phone phoned

xclean: 
	rm *.o phone phoned ${INSTDIR}/phone \
	   ${INSTDIR}/phoned ${MANDIR}/phone.1


install: phone phoned
	 cp phone 	${INSTDIR}/phone
	 chown ${BIN} 	${INSTDIR}/phone
	 chgrp ${BINGRP} 	${INSTDIR}/phone
	 chmod 751 	${INSTDIR}/phone
	 cp phoned 	${INSTDIR}/phoned
	 chown ${BIN} 	${INSTDIR}/phoned
	 chgrp ${BINGRP} 	${INSTDIR}/phoned
	 chmod 751 	${INSTDIR}/phoned
	 cp phone.1	${MANDIR}/phone.1
//E*O*F Makefile//

echo x - cparse.c
cat > "cparse.c" << '//E*O*F cparse.c//'
#include "cparse.h"

defaultputerrormessage();
initctab();

char *cp_ctab = (char *) 0;	/* pointer to current character table */
int (*cp_puterrormessage)() = defaultputerrormessage;
char cp_badtoken[50];	/* used to store copy of token that caused error */
char *cp_errormessage;	/* error message to put out */


int cparse(sstate,line)
   statee *sstate;	/* starting state */
   char   *line;	/* line to parse  */
{
   char *argvec[100];	/* argument vector */
   int argcnt;		/* argument count  */
   char *tokvec[100];	/* token vector    */
   int retval;		/* resultant value from call to transitions */

   tokenize(line,tokvec);	/* break the line up into words */

   argcnt = 0;			/* start the argument count out at zero */
   cp_badtoken[0] = '\0';	/* null out the bad token copy	*/
   cp_errormessage = "Unrecognized token.";	/* default message */
   if ((retval = transitions(sstate,tokvec,argvec,&argcnt)) < 0)
      cp_puterrormessage(retval);
}

#define lcase(c) (('A'<=(c) && (c)<='Z')?((c)+' '):(c))

tokenize(l,tv)
   register char *l;	/* line to tokenize  */
   char **tv;		/* vector to put the broken up line into */
{
   char quotec;
   static char tokenbuf[1024];	/* here is where we buffer up the tokens */
   register char *tp = tokenbuf;

   if (cp_ctab == (char *) 0)
      initctab();		/* initialize the character table if need be */

   while (*l) {
      while (*l && cp_ctab[(*l)] == CP_CSKIP) l++;	/* skip white space */

      if (cp_ctab[(*l)] == CP_CQUOTE) {
	 *tv++ = tp;
	 quotec = *l++;
	 while (*l && *l != quotec) 
	    *tp++ = *l++;
	 *tp++ = '\0';	/* kill the final quote */
	 l++;
      }
      else if (cp_ctab[(*l)] == CP_CCTOKEN) {
	 *tv++ = tp;
	 *tp++ = *l++;
	 *tp++ = '\0';
      }
      else if (*l) {
	 *tv++ = tp;
	 while (*l && cp_ctab[(*l)] == CP_CSTOKEN) {
	    *tp++ = lcase(*l);
	    l++;	/* this can't be done in the above line!!! */
	 }
	 *tp++ = '\0';
      }
   }
   *tv = (char *) 0;		/* null terminate the token vector */
}


static defaultputerrormessage(ev)
   int ev;	/* errorval */
{
   switch (ev) {
      case CP_EEXTRAINPUT :
	 printf("Error: extra input encountered. (%s)\n",cp_badtoken);
	 break;
      case CP_EUNRECOGNIZEDTOKEN :
	 printf("Error: %s (%s)\n",cp_errormessage,cp_badtoken);
	 break;
      default :
	 printf("Error: Unknown error.\n");
	 break;
   }
}

int transitions (s, tv, av, acp)
   statee  *s;	/* current state	*/
   char  **tv;	/* token vector		*/
   char *av[];	/* argument vector	*/
   int	 *acp;	/* pointer to the argument count	*/
{
   statee *se;	/* state entry		*/
   int retval;	/* return value		*/
   statee * findentry();

   if (s == CP_NULLSTATE && *tv == (char *) 0)
      return(0);
   else if (s == CP_NULLSTATE) {
      strncpy(cp_badtoken,*tv,sizeof(cp_badtoken)-1);
      cp_badtoken[sizeof(cp_badtoken)-1] = '\0';
      return(CP_EEXTRAINPUT);
   }
   
   if ((se = findentry(s,*tv,av,acp)) == CP_NULLSTATE) {
      strncpy(cp_badtoken,*tv,sizeof(cp_badtoken)-1);
      cp_badtoken[sizeof(cp_badtoken)-1] = '\0';
      return(CP_EUNRECOGNIZEDTOKEN);
   }
   
   retval = 0;
   /* Use DeMorgan's Rule on the next line to figure out what it means */
   if (se->token != CP_TKNULLSTRING || **tv != '\0') 
      if ((retval = transitions(se->nextstate,
				(se->token == CP_TKNULLSTRING) ? tv : tv+1,
				av,
				acp))  <  0)
	 return(retval);
   
   if (se->func != CP_NULLFUNC) {
      switch (se->functype) {
	 case CP_FTVALUE  : (se->func)(retval+se->retval);
			    break;
	 case CP_FTPARAMS : (se->func)(*acp,av);
			    break;
	 case CP_FTVPARAMS: (se->func)(retval+se->retval,*acp,av);
			    break;
	 case CP_FTSHELLCMD :
			    {
			      int i;
			      char cmdline[300];
			      strcpy(cmdline,(char *) se->func);
			      for (i=0;i < (*acp);i++) {
				 strcat(cmdline," ");
				 strcat(cmdline,av[i]);
			      }
			      system(cmdline);
			    }
	 default          : /* ignore */
			    break;
      }
   }

   return(retval + se->retval);
}

statee *
findentry(s, t, av, acp)
   statee   *s;		/* current state	*/
   char	    *t;		/* current token	*/
   char  *av[];		/* argument vector	*/
   int    *acp;		/* pointer to argument vector	*/
{
   statee *rv = CP_NULLSTATE;

   if (t == (char *) 0) {
      while (s->token != CP_TKENDMARKER)
	 if (s->token == CP_TKNULLSTRING)
	    return(s);
	 else if (s->token == CP_TKERRORMSG) {
	    cp_errormessage = (char *) s->func;
	    s++;
	 }
	 else
	    s++;
      return(CP_NULLSTATE);	/* no match */
   }
   else {
      while (s->token != CP_TKENDMARKER)
	 if (s->token == CP_TKNULLSTRING)
	    rv = s++;		/* skip the null string case */
	 else if (s->token == CP_TKERRORMSG) {
	    cp_errormessage = (char *) s->func;
	    s++;
	 }
	 else if (s->token == CP_TKDATA) {
	    if (cp_ctab[(*t)] == CP_CQUOTE)
	       av[(*acp)] = t+1;
	    else
	       av[(*acp)] = t;
	    (*acp)++;
	    return(s);
	 }
	 else if (cmptok(s->token,t))
	    return(s);
	 else
	    s++;
   }

   return(rv);		/* return null string pointer maybe....otherwise
			   we'll return the null state pointer.		*/
}


static int cmptok(ss,cs)
   char *ss,*cs;
{
   while (*ss && *cs && *ss != '[' && *ss != ' ')
      if (*ss++ != *cs++) return(0);
   
   if (*ss == '[') ss++;
   else if (*ss == ' ' && *cs == '\0') return(1);
   else if (*ss == '\0' && *cs == '\0') return(1);
   else return(0);

   while (*ss && *cs)
      if (*ss == ']') ss++;
      else if (*ss++ != *cs++) return(0);
   
   if (*cs == '\0') return(1);
   else return(0);
}

static initctab()
{
   char *malloc();
   int i;
   cp_ctab = malloc(256);
   for (i=0;i<256;i++)
      cp_ctab[i]  = CP_CSTOKEN;
   
   cp_ctab['\t'] = CP_CSKIP;
   cp_ctab[' '] = CP_CSKIP;
   cp_ctab['"'] = CP_CQUOTE;
}

cp_setctab(c,v)
   char c;
   char v;
{
   if (cp_ctab == (char *) 0)
      initctab();
   cp_ctab[c] = v;
}
//E*O*F cparse.c//

echo x - cparse.h
cat > "cparse.h" << '//E*O*F cparse.h//'
/*
 * cparse.h		-- command parser include file
 */

/* Predefined token types */
#define CP_TKENDMARKER ((char *) 0)
#define CP_TKDATA ((char *) 1)
#define CP_TKNULLSTRING ((char *) 2)
#define CP_TKERRORMSG ((char *) 3)

/* Function Types */
#define CP_FTNONE	0	/* no associated function	*/
#define CP_FTPARAMS	1	/* call associated function with parameter
				   block		*/
#define CP_FTVALUE	2	/* call associated function with accumulated
				   return value				*/
#define CP_FTVPARAMS	3	/* call with both accumulated value and 
				   parameter block			*/
#define CP_FTERRORMSG	4	/* corresponding function is really a string
				   containing error message if anything goes
				   wrong				*/
#define CP_FTSHELLCMD	5	/* corresponding function is a string of a
				   shell function to execute.  The accumulated
				   parameters form the command parameters */

#define CP_NULLSTATE (statee *) 0
#define CP_NULLFUNC (int (*)()) 0

#define CP_EEXTRAINPUT -1
#define CP_EUNRECOGNIZEDTOKEN -2

	/* The following macros are used in the character table to tokenize
	   the input. (They can be ignored for many applications)	*/
#define CP_CSKIP 0		/* Characters that we Skip		*/
#define CP_CCTOKEN 1		/* Character alone defines a Token	*/
#define CP_CSTOKEN 2		/* character composes part of a Token
				   String				*/
#define CP_CQUOTE 3		/* Character begins Quoted string	*/

typedef struct statee_ {
		char *token;			/* pointer to token */
		struct statee_ *nextstate;	/* pointer to next state */
		int functype;			/* function type	*/
		int (*func)();			/* pointer to function	*/
		int retval;			/* return value		*/
	} statee;	/* command parser state entry */


/* Macros used for defining states.
 *	The following macros are used to define a state in the finite state
 *	machine the user must build to define his command language
 *
 *	defstate(statename)   is used to start the definition of a state
 *	endstate	      is used to terminate the definition of a state
 *
 *	Between these two macros, the user may enter something of the form
 *	  { "token-string", nextstate, functiontype, functionpointer, retval },
 *	where each of the parameters is of a type in statee.  Usually,
 *	however, only a few of these parameters are of interest at a time so
 *	there are macros to take care of these.
 */

#define DEFSTATE(s) statee s[] = {
#define ENDSTATE    ,{CP_TKENDMARKER,CP_NULLSTATE,CP_FTNONE,CP_NULLFUNC,0}};

#define NS(t,ns) {t,ns,CP_FTNONE,CP_NULLFUNC,0}

#define NULLSTR {CP_TKNULLSTRING,CP_NULLSTATE,CP_FTNONE,CP_NULLFUNC,0}
#define NULLSTR_NS(ns) {CP_TKNULLSTRING,ns,CP_FTNONE,CP_NULLFUNC,0}
#define NULLSTR_PFUNC(f) {CP_TKNULLSTRING,CP_NULLSTATE,CP_FTPARAMS,f,0}
#define NULLSTR_VFUNC(f) {CP_TKNULLSTRING,CP_NULLSTATE,CP_FTVALUE,f,0}
#define NULLSTR_PFUNC_NS(f,ns) {CP_TKNULLSTRING,ns,CP_FTPARAMS,f,0}
#define NULLSTR_VFUNC_NS(f,ns) {CP_TKNULLSTRING,ns,CP_FTVALUE,f,0}

#define DATA {CP_TKDATA,CP_NULLSTATE,CP_FTNONE,CP_NULLFUNC,0}
#define DATA_NS(ns) {CP_TKDATA,ns,CP_FTNONE,CP_NULLFUNC,0}
#define DATA_PFUNC(f) {CP_TKDATA,CP_NULLSTATE,CP_FTPARAMS,f,0}
#define DATA_VFUNC(f) {CP_TKDATA,CP_NULLSTATE,CP_FTVALUE,f,0}
#define DATA_PFUNC_NS(f,ns) {CP_TKDATA,ns,CP_FTPARAMS,f,0}
#define DATA_VFUNC_NS(f,ns) {CP_TKDATA,ns,CP_FTVALUE,f,0}

#define PFUNC(t,f) {t,CP_NULLSTATE,CP_FTPARAMS,f,0}
#define VFUNC(t,f) {t,CP_NULLSTATE,CP_FTVALUE,f,0}
#define VSUM(t,v) {t,CP_NULLSTATE,CP_FTNONE,CP_NULLFUNC,v}

#define PFUNC_NS(t,f,ns) {t,ns,CP_FTPARAMS,f,0}
#define VFUNC_NS(t,f,ns) {t,ns,CP_FTVALUE,f,0}
#define VSUM_NS(t,v,ns) {t,ns,CP_FTNONE,CP_NULLFUNC,v}

#define VPFUNC(t,f) {t,CP_NULLSTATE,CP_FTVPARAMS,f,0}
#define VPFUNC_NS(t,f,ns) {t,ns,CP_FTVPARAMS,f,0}
#define DATA_VPFUNC(f) {CP_TKDATA,CP_NULLSTATE,CP_FTVPARAMS,f,0}
#define DATA_VPFUNC_NS(f,ns) {CP_TKDATA,ns,CP_FTVPARAMS,f,0}

#define SHELLCMD(t,cmd) {t,CP_NULLSTATE,CP_FTSHELLCMD,(int (*)()) cmd,0}
#define SHELLCMD_NS(t,cmd,ns) {t,ns,CP_FTSHELLCMD,(int (*)()) cmd,0}
#define DATA_SHELLCMD(cmd) {CP_TKDATA,CP_NULLSTATE,CP_FTSHELLCMD,(int (*)()) cmd,0}
#define DATA_SHELLCMD_NS(cmd,ns) {CP_TKDATA,ns,CP_FTSHELLCMD,(int (*)()) cmd,0}
#define NULLSTR_SHELLCMD(cmd) {CP_TKNULLSTRING,CP_NULLSTATE,CP_FTSHELLCMD,\
			       (int (*)()) cmd,0}
#define NULLSTR_SHELLCMD_NS(cmd,ns) {CP_TKNULLSTRING,ns,CP_FTSHELLCMD,\
			       (int (*)()) cmd,0}

#define ERRORMSG(m) {CP_TKERRORMSG,CP_NULLSTATE,CP_FTERRORMSG,(int (*)()) m,0}

/* Useful external variables */
extern int (*cp_puterrormessage)();
extern char cp_badtoken[];
extern char *cp_errormessage;
extern char *cp_ctab;		/* pointer to current character table */
//E*O*F cparse.h//

echo x - getuser.c
cat > "getuser.c" << '//E*O*F getuser.c//'
/*
 * Version 2 Phone -- getuser.c
 *
 * This section gets information associated with a logged in user.  We pass
 * getuser the name and possibly a terminal and it returns the systems idea
 * of the user name, whether or not his terminal can be written to, his tty,
 * and his mother's birthday.  (Note: a pointer to a userinformation structure
 * is returned.  If zero is returned, this indicates that the user is not logged
 * in.  -1 indicates an error opening the utmp file or accessing it.
 *
 *
 * Program Author:  Kevin A. Buettner
 * Created: 7-7-84
 * Revisions:
 */

#include "phone.h"
#include <utmp.h>
#include <sys/file.h>

#define UTSIZE (sizeof (struct utmp))
#define UTLSIZE (sizeof urec.ut_line)
#define UTNSIZE (sizeof urec.ut_name)



userinformation *
getuser(username,ttyname)
   char *username,
	*ttyname;
{
   static userinformation retval;
   int utfd;				/* file descriptor to utmp */
   int fd;				/* file descriptor         */
   struct utmp urec;			/* utmp record		   */
   register done = 0;			/* done flag               */
   char tfname[50];			/* built up filename       */

   if ((utfd = open("/etc/utmp",O_RDONLY,0)) < 0)
      return((userinformation *) -1);
   
   done = 0;
   while (!done && UTSIZE == read(utfd,&urec,UTSIZE)) {
      if (strncmp(username,urec.ut_name,UTNSIZE) == 0)
	 if (ttyname[0] == '\0')
	    done = 1;
	 else if (strncmp(ttyname,urec.ut_line,UTLSIZE) == 0)
	    done = 1;
   }

   close(utfd);
   if (!done)
      return((userinformation *) 0);
   
   strncpy(retval.ttyname,urec.ut_line,UTLSIZE);
   retval.ttyname[UTLSIZE] = '\0';
   strncpy(retval.username,urec.ut_name,UTNSIZE);
   retval.username[UTNSIZE] = '\0';

   /* set the canwriteto field */
   strcpy(tfname,"/dev/");
   strcat(tfname,retval.ttyname);
   retval.canwriteto = (0 == access(tfname,W_OK));

   /* get the socket name to connect to (if possible)	*/
   strcpy(tfname,"/tmp/V2Ph");
   strcat(tfname,retval.ttyname);
   if ((fd=open(tfname,0,0)) < 0)
      retval.psockname[0] = '\0';
   else {
      read(fd,retval.psockname,40);
      close(fd);
      if (access(retval.psockname,F_OK) < 0)
	 retval.psockname[0] = '\0';
   }

   return(&retval);
}
//E*O*F getuser.c//

echo x - phone.1
cat > "phone.1" << '//E*O*F phone.1//'
.TH PHONE 1 "07 November 1984"
.UC 4
.SH NAME
phone \- talk to users interactively on the local host.
.SH SYNOPSIS
.B phone
person [ ttyname ]
.SH DESCRIPTION
.I Phone
is a visual communication program which
copies characters from your terminal to that of
one or more other user's terminals.
.PP 
To communicate with another user just use the person's 
login name. 
If you want to talk to a user who is logged in more than once,
the
.I ttyname
argument may be used to indicate the
appropriate terminal name.
.PP
When you 
.I phone
another user the following message is displayed on the receiving
users terminal:
.sp 2
	Phone: your_name on terminal your_terminal is calling you
.br
While attempting connection the callers terminal clears and displays 
the status of the call. The above message will appear on the callee's
terminal every few seconds until they either answer the call or you 
cancel the call. 
Once communication is established, the two parties may type 
simultaneously, with their output appearing in separate windows. 
.PP 
While conversing with one or more users there are several possible 
commands. These commands include:
.sp 2
<cntrl> L	-- Replot Screen
.br
<cntrl> N	-- Erase your portion of the screen.
.br
<cntrl> C/D	-- Terminate Conversation. "Hangup the phone"
.br
<esc>		-- Phone Command Processor escape
.br
.PP
The valid commands under the Phone Command Processor include:
.sp 2
help/?		-- Help Summary
.br
who		-- List Users on system
.br
exit		-- same as <cntrl> c
.br
call		-- Place a call to another user (conference calling)
.br
cancel		-- Cancel call to a user
.br
set/show minterval  -- Sets/Shows message interval (controls how fast messages
scroll on the botton line of the window.
.br
.PP
Permission to talk may be denied or granted by use of the
.I mesg
command.
.PP
Note: Phone also has a hardcopy mode for users on noncursor addressable 
terminals. This operates exactly as normal mode but is line oriented and
very messy.
.PP
.SH Files
/etc/utmp	to find the recipient's tty
.SH "SEE ALSO"
mesg(1), who(1), mail(1), write(1), talk(1)
.SH Bugs
Uses 4.2 sockets and can be very slow sometimes. Also on slow terminals
when users enter and leave a conversion the screen is redrawn to reflect
the new users window which is a pain on dialup terminals. If you have not
fixed the 4.2 socket bug which hangs the system at NETIPL it is not a good
idea to run this program.
//E*O*F phone.1//

echo x - phone.c
cat > "phone.c" << '//E*O*F phone.c//'
/*
 * Version 2 Phone -- phone.c
 *
 * This section of the version 2 phone utility contains the function main.
 *
 * Program Author:  Kevin A. Buettner
 * Creation Date:   7/7/84
 * Revisions:
 */

#include "phone.h"
#include <signal.h>
#include <pwd.h>
#include <stdio.h>

extern struct passwd *getpwuid();
extern userinformation * getuser();

main(argc,argv)
   int argc;
   char **argv;
{
   userinformation *u;
   char *mytty;
   char *ttyname();
   int  dpid;				/* daemon pid */
   char psockname[80];			/* phone socket name */
   struct passwd *pw;			/* pointer to password structure */
   int nodaemon(),daemonstarted();	/* predeclare for signal	*/

   /* Check the number of arguments */
   if (argc < 2) {
      printf("Usage: %s username [ttyname]\r\n",argv[0]);
      _exit(1);
   }

   /* See if the user we're trying to call is logged in, and if so
      get certain information about him				  */
   if ((u = getuser(argv[1],((argc > 2) ? argv[2] : ""))) <= 0) {
      if ((int) u < 0)
	 printf("Error accessing /etc/utmp.\r\n");
      else
	 printf("User not logged in.\r\n");
      _exit(1);
   }

   /* Get the current terminal name */
   if ((mytty = ttyname(0)) == 0) {
      printf("You must be connected to a terminal.\n");
      _exit(1);
   }

   /* Get my user name from the uid and the passwd file */
   pw = getpwuid(getuid());
   if (pw == 0) {
      printf("Can't get your login name.");
      _exit(1);
   }

   initslots();
   setslot(MYSLOT,pw->pw_name,&mytty[5],pw->pw_gecos);

   if (u->psockname[0] == '\0') {

      if (u->canwriteto == 0) {
	 printf("Permission denied (Can't ring terminal).");
	 exit(1);
      }

      /* Gotta create a daemon */
      signal(SIGIOT,nodaemon);		/* set up signal handlers */
      signal(SIGEMT,daemonstarted);
      if ((dpid=vfork()) == 0) {
	 /* daemon branch */
	 signal(SIGIOT,SIG_DFL);
	 signal(SIGEMT,SIG_DFL);
	 /* try executing the phone daemon in a number of places */
	 execl("/usr/local/phoned","phoned",pw->pw_name,&mytty[5],
		   u->username,u->ttyname,0);
	 execl("/usr/bin/phoned","phoned",pw->pw_name,&mytty[5],
		   u->username,u->ttyname,0);
	 execl("/usr/new/phoned","phoned",pw->pw_name,&mytty[5],
		   u->username,u->ttyname,0); 
	 execl("/test.bin/phoned","phoned",pw->pw_name,&mytty[5],
		   u->username,u->ttyname,0);

	 kill(getppid(),SIGIOT);	/* can't do it */
	 exit(1);
      }
      else {
	 /* normal branch */
	 sigpause(0);
	 signal(SIGIOT,SIG_DFL);
	 signal(SIGEMT,SIG_DFL);
	 sprintf(psockname,"/tmp/pD%d",dpid);
         phonemain(psockname,pw->pw_name,&mytty[5],pw->pw_gecos);
	 _exit(0);
      }
   }
   else {
      strcpy(psockname,u->psockname);
      phonemain(psockname,pw->pw_name,&mytty[5],pw->pw_gecos);
      _exit(0);
   }
}

nodaemon()
{
   printf("Error in starting daemon\r\n");
   fflush(stdout);
   _exit(1);
}

daemonstarted()
{
   /* null function */
}
//E*O*F phone.c//

echo x - phone.h
cat > "phone.h" << '//E*O*F phone.h//'
/*
 *  Version 2 Phone:  phone.h
 *
 *  This include file contains definitions and declarations common to the
 *  various parts of the version 2 phone program.
 *
 *  Author:  Kevin A. Buettner
 *  Creation: 7/7/84
 *  Revision:
 */

typedef struct {
		char ttyname[8];	/* terminal name */
		char username[30];	/* short user name */
		char realname[60];	/* his name out of the gcos field */
		int  canwriteto;	/* flag telling us if we can write to
					   his tty      */
		char psockname[80];	/* the socket name to connect to (null
					   if there isn't any yet)	*/
		char mothersbirthday[15];	/* string whose ascii
					   representation denotes the the date
					   of the user's mother's birth  */
	       } userinformation;

typedef struct {
		int  inuse;		/* indicates that the slot is in use */
		char loginname[40];	/* the login name		     */
		char ttyname[40];       /* name of the tty                   */
		char realname[80];      /* real life name                    */
		int  xpos;		/* current screen x-position         */
		int  ypos;              /* current screen y-position         */
		int  topline;           /* top-most line that this user gets */
		int  bottomline;        /* bottom-most line for this user    */
		int  lcount;		/* number of lines in the line queue */
		int  lqhead;		/* head of the line queue            */
		int  lqtail;		/* tail of the line queue            */
	       } slottype;

typedef struct {
		int  llen;		/* line length                       */
		char line[132];		/* actual data in the line           */
		int  nextl;		/* index of next line (-1 is null)   */
	       } linetype;

#define MAXSLOT 16			/* one bigger than in the phone daemon*/
#define MYSLOT  (MAXSLOT-1)

extern slottype slots[];
extern linetype lines[];
extern int      nslines,		/* see slotmanager.c                 */
	        nflines,
	        nslots,
	        maxlines,
		curslot;

extern (*announcearrival)();
extern (*announcedeparture)();
extern (*plotscr)();
extern (*puttoscr)();
extern (*putmessage)();
extern (*killcommandline)();
extern (*setupcommandline)();
extern (*putcommandcharacter)();

extern int oncommandline;
//E*O*F phone.h//

echo x - phoned.c
cat > "phoned.c" << '//E*O*F phoned.c//'
/*
 *  Version 2 Phone  -- phoned.c
 *
 *  This file contains the phone daemon.
 *
 *  Program Author:  Kevin A. Buettner
 *  Creation Date:   7/9/84
 *  Revisions:
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/file.h>
#include <errno.h>
#include <stdio.h>


#define MAXSLOT 15



struct {
		int	inuse;		/* slot use indicator		*/
		int	ringsleft;	/* number of rings left...-1 indicates
					   slot in conversation.  -2 indicates
					   an unsolicited caller waiting to be
					   accepted.        0 indicates
					   a wait for a user to join
					   conversation.		*/
		char	loginname[40];	/* the login name		*/
		char	ttyname[40];	/* tty name			*/
		char	realname[80];	/* real life name		*/
		int	callerid;	/* slot number of the caller	*/
		int	lastwriter;	/* slot id of last writer to 
					   this slot			*/
		int	sd;		/* socket descriptor		*/
	} slots[MAXSLOT];

char dsockname[20];		/* daemon socket name			*/
int  lsock;			/* socket that we're listening on	*/
struct itimerval ringinterval;	/* time between rings structure		*/
extern ringring();		/* function which rings a terminal	*/
extern int errno;


main(argc,argv)
   int argc;
   char **argv;
{
   int  cslot,ceslot;		/* caller and callee slots		*/
   struct sockaddr sname;	/* name of socket we're listening on	*/
   int  readmask,nfound;	/* select mask and number of descrs
				   found in select call			*/

   setpgrp(getpid(),getpid());	/* make ourselve's a process group	*/
   if (argc < 5) {
      printf("You should not run the phone daemon directly.\r\n");
      _exit(1);
   }
   initslots();			/* get ready for users			*/

   sprintf(dsockname,"/tmp/pD%d",getpid());

   if ((lsock = socket(AF_UNIX,SOCK_STREAM,PF_UNSPEC)) < 0) {
      perror("Error opening socket");
      kill(getppid(),SIGKILL);
      exit(1);
   }

   strcpy(sname.sa_data,dsockname);
   sname.sa_family = PF_UNSPEC;
   if (bind(lsock,&sname,sizeof(sname.sa_family) + strlen(sname.sa_data)) < 0) {
      perror("Error binding socket");
      kill(getppid(),SIGKILL);
      exit(1);
   }

   if (listen(lsock,5) < 0) {
      perror("Listen call failed");
      shutdown(lsock,2);
      close(lsock);
      unlink(dsockname);
      kill(getppid(),SIGKILL);
      exit(1);
   }

   /* socket should be set up and ready to go now */

   cslot = setslot( emptyslot(),
		    0,		/* no rings for the caller	*/
		    argv[1],	/* the login name		*/
		    argv[2],	/* ttyname			*/
		    "",		/* don't know real name yet	*/
		    -1,		/* no caller			*/
		    -1);	/* no socket descr yet		*/
   
   ceslot= setslot( emptyslot(),
		    20,		/* twenty rings			*/
		    argv[3],	/* the login name		*/
		    argv[4],	/* ttyname			*/
		    "",		/* don't know real name yet	*/
		    cslot,	/* the caller			*/
		    -1);	/* no socket descriptor yet	*/
   umask(0);	/* set mode mask for all permissions		*/

   if (makesocketnamefile(cslot) < 0) {
      perror("Couldn't make socket name file");
      shutdown(lsock,2);
      close(lsock);
      unlink(dsockname);
      kill(getppid(),SIGKILL);
   }

   kill(getppid(),SIGEMT);	/* tell parent daemon is started */

   timerclear(&ringinterval.it_interval);
   timerclear(&ringinterval.it_value);
   ringinterval.it_interval.tv_sec   = 20;
   ringinterval.it_value.tv_sec      = 20;
   sigsetmask(~0);
   signal(SIGPIPE,SIG_IGN);	/* ignore broken pipe signals.  We'll find
					out about broken pipes when we try
					to do a write			*/
   signal(SIGALRM,ringring);
   setitimer(ITIMER_REAL,&ringinterval,0);
   ringring();			/* do the first ring now.	*/

   /* This is the loop we'll sit in for most of the time	*/

   while (1) {
      readmask = buildreadmask();
      sigsetmask(0);		/* enable signals		*/

      while ((nfound = select(15,&readmask,0,0,0)) < 0)
	 if (errno != EINTR)
	    errorshutdown();
      
      sigsetmask(~0);		/* disable all signals except for
				   SIGKILL, SIGCONT, and SIGSTOP	*/
      decodereadmask(nfound,readmask);
   }

}  /* end of main for the phone daemon  */

ringring()	/* Rings people's terminals */
{
   FILE *tf;
   int i;
   int callerid;
   char tnam[30];
   char mess[110];
   for (i=0;i<MAXSLOT;i++) 
      if (slots[i].inuse && slots[i].ringsleft > 0) {
	 (slots[i].ringsleft)--;
	 sprintf(tnam,"/dev/%s",slots[i].ttyname);
	 if ((callerid = slots[i].callerid) < 0 ||
		  slots[callerid].inuse == 0) 
	    killslot(i);	/* won't ring if we don't know who is calling */
	 else if ((tf = fopen(tnam,"w")) == NULL) {
	    killslot(i);	/* won't ring if we can't write to the term */
	    sprintf(mess,
	    "User %s on terminal %s rudely logged off or shut off his phone.",
		    slots[i].loginname,
		    slots[i].ttyname);
	    putmess(callerid,mess);
	 }
	 else {
	    fprintf(tf,
         "\r\n\007Phone: %s on terminal %s is calling you.\r\n",
		    slots[callerid].loginname,
		    slots[callerid].ttyname);
 	    fclose(tf);
	    sprintf(mess,"Ringing %s on %s",slots[i].loginname,
					    slots[i].ttyname);
	    putmessage(mess);
	 }
      }
      else if (slots[i].inuse && slots[i].ringsleft < 0) {
	 /* See if all of our sockets are still alive */
	 sputchar(i,001);	/* try writing a SYN character to each...
				   if sputchar doesn't succeed it will kill
				   the slot for us.  Note:  The processes
				   at the other end of the socket should
				   ignore the SYN character		*/
      }
}

initslots()
{
   int i;
   for (i=0;i<MAXSLOT;i++)
      slots[i].inuse = 0;
}

killslot(sn)
   int sn;	/* slot number to kill */
{
   int i,	/* loop index */
       nu;	/* number used */

   slots[sn].inuse = 0;
   killsocketnamefile(sn);

   /* Kill of the slots that this user called.			*/
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse && slots[i].ringsleft >= 0 &&
	  slots[i].callerid == sn)
	 slots[i].inuse = 0;
   
   /* Inform rest of conversation of the termination of a user	*/
   if (slots[sn].ringsleft == -1)	/* make sure they are really in. */
      for (i=0;i<MAXSLOT;i++)
	 if (slots[i].inuse && slots[i].ringsleft == -1) {
	    putheader(i,'t');
	    sputchar(i,'0'+sn);	/* we're terminating sn */
	 }

   
   /* Make a count of the remaining slots and see if there is anyone left */
   for (nu=0,i=0;i<MAXSLOT;i++)
      if (slots[i].inuse)
	 nu++;
   
   if (nu < 2) {	/* Shut everything down */
	/* ADD CODE here to inform any remaining users that we're shutting
	   down operation					*/
      shutdown(lsock,2);	/* Shut down the socket we're listening on */
      close(lsock);		/* Then close it			*/
      for (i=0;i<MAXSLOT;i++)
	 if (slots[i].inuse && slots[i].ringsleft < 0) {
	    shutdown(slots[i].sd,2);	/* Shut down the rest of the sockets */
	    close(slots[i].sd);		/* And close 'em up		*/
	    killsocketnamefile(i);
	 }
      unlink(dsockname);
      exit(0);			/* This is where we exit normally.	*/
   }
}

emptyslot()  /* returns the first free slot  */
{
   int i;
   for (i=0; i<MAXSLOT && slots[i].inuse;i++);
   return((i>=MAXSLOT) ? -1 : i);
}

setslot ( sn,rl,loginname,ttyname,realname,callerid,socketdesc)
   int sn;
   int rl;
   char *loginname;
   char *ttyname;
   char *realname;
   int callerid;
   int socketdesc;
{
   slots[sn].inuse	= 1;
   slots[sn].ringsleft	= rl;
   strcpy(slots[sn].loginname,loginname);
   strcpy(slots[sn].ttyname,ttyname);
   strcpy(slots[sn].realname,realname);
   slots[sn].callerid	= callerid;
   slots[sn].sd		= socketdesc;
   slots[sn].lastwriter	= -1;
}

makesocketnamefile(sn)
   int sn;
{
   int fd;
   char fname[40];
   sprintf(fname,"/tmp/V2Ph%s",slots[sn].ttyname);
   if ((fd=open(fname,O_CREAT|O_TRUNC|O_WRONLY,0777)) < 0)
      return(-1);
   
   if (write(fd,dsockname,strlen(dsockname)) < 0) {
      close(fd);
      return(-1);
   }

   close(fd);
   return(0);
}

killsocketnamefile(sn)
   int sn;
{
   int fd;
   char fname[40];
   sprintf(fname,"/tmp/V2Ph%s",slots[sn].ttyname);
   unlink(fname);
}

int buildreadmask()
{
   register int retval = 1<<lsock;
   register int sn;

   for (sn=0;sn<MAXSLOT;sn++)
      if (slots[sn].inuse && slots[sn].ringsleft == -1)
	 retval |= 1<<slots[sn].sd;
   
   return(retval);
}

decodereadmask(nfound,m)
   int nfound,m;
{
   register int sn;
   register int i;

   if (m & (1<<lsock))
      acceptconnection();
   
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse && (m & (1<<slots[i].sd)))
	 decodeumessage(i);
}

int findslot(loginname,ttyname)		/* returns slot number of given
					   user...otherwise -1          */
   char *loginname,*ttyname;
{
   int i;
   for (i=0; 
	i < MAXSLOT &&
	  !( /* (strncmp(loginname,slots[i].loginname,8) == 0) && */
	    (strncmp(ttyname,slots[i].ttyname,8) == 0));
	i++);
   return( (i >= MAXSLOT) ? -1 : i);
}

acceptconnection()

/*
 *  BUG:  What happens if someone decides to leave in the middle of being
 *	informed of a new user?
 */

{
   int nsd;		/* new socket descriptor	*/
   char loginname[40];
   char ttyname[40];
   char realname[80];
   char c;
   int sn;		/* slot number			*/
   int callerid;	/* person who called this user  */
   int i;		/* loop index			*/


   if ((nsd=accept(lsock,0,0)) < 0)
      errorshutdown();
   
   if (sgetc(nsd) != ':') {
      shutdown(nsd,2);
      close(nsd);
      return(-1);
   }

   sreadstr(nsd,loginname);
   sreadstr(nsd,ttyname);
   sreadstr(nsd,realname);

   if ((sn = findslot(loginname,ttyname)) >= 0) {
      callerid = slots[sn].callerid;
   }
   else {
      callerid = -1;
      sn = emptyslot();
   }

   setslot( sn,
	    -1,		/* active in conversation */
	    loginname,
	    ttyname,
	    realname,
	    callerid,
	    nsd );

   if (write(nsd,"a",1) < 0) {
      /* abruptly disconnected ?? */
      /* Should we do anything else here? */
      shutdown(nsd,2);
      close(nsd);
      slots[sn].inuse = 0;	/* make slot available for use again	*/
      return;
   }

   putheader(sn,'a');
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse && slots[i].ringsleft == -1 && i != sn) {
	 putheader(i,'a');	/* put out the message header */
	 putuser(i,sn);		/* add sn to user i */
	 termmsg(i);		/* terminate message to i */
	 putuser(sn,i);		/* add i to user sn (the one we just added) */
      }
   termmsg(sn);		/* terminate the message to sn */

   makesocketnamefile(sn);

   return(0);		/* successful */
}

errorshutdown()
{
   int i;
   printf("\007\r\n");
   perror("Phone Daemon Error Shutdown");
   /* This would be a good place for an error logger message */
   printf("\007\r\n");
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse && slots[i].ringsleft < 0 && slots[i].sd >= 0) {
	 shutdown(slots[i].sd,2);
	 close(slots[i].sd);
      }
   shutdown(lsock,2);
   close(lsock);
   unlink(dsockname);
   exit(1);
}

int sgetc(sd)
   int sd;	/* the socket descriptor */
{
   int ch;
   /* add timeout (use virtual timer) later on to prevent blocking */
   ch = 0;
   if (read(sd,&ch,1) < 0)
      return(-1);
   else
      return(ch);
}

sreadstr(sd,buf)
   int sd;	/* socket descriptor */
   char *buf;	/* the buffer to put the stuff in...we read til : */
{
   char ch;
   while ((*buf = sgetc(sd)) != ':') buf++;
   *buf = '\0';	/* null terminate */
}

/*
 * The DLE character (020 octal) is used to introduce a message from a
 * different source.  The character immediately following the DLE character
 * identifies the source.
 *
 *	Character	Meaning
 *	'0' thru '>'	characters following were entered by a user for
 *			display.  '0' corresponds to slot 0, '1' to slot 1
 *			and so on.
 *	'a'		Add user(s).  One or more sequences of 
 *			userid:login name:tty:real life name:
 *			are sent from the daemon.  This entire sequence is
 *			terminated with another DLE character.  The user
 *			context that was present before the message will
 *			resume. The user id will be one of the 0 thru > chars.
 *	't'		Terminate a user.  A single character corresponding
 *			to the slot number (0 thru >) is sent immediately
 *			following the 't' indicating which user has left
 *			the conversation.
 *	'm'		characters following is a message from the daemon
 *			for display on the user's terminal.  A DLE terminates
 *			the daemon message.
 *
 */

sputstr(sn,s)
   int sn;		/* slot number (this is NOT the socket descriptor) */
   char *s;		/* string to write out				   */
{

   if (!slots[sn].inuse)
      return;		/* someone doesn't know that this slot is out of
			   commission yet  (or else there is a bug)	*/

   if (write(slots[sn].sd,s,strlen(s)) < 0)
      if (errno == EPIPE) 
	 killslot(sn);
      else
	 perror("Phone Daemon Debug (sputstr)");
}

sputchar(sn,c)
   int sn;		/* slot number (this is NOT the socket descriptor) */
   char c;		/* character to write out to socket desc associated
			   with the slot				   */
{
   if (!slots[sn].inuse)
      return;		/* Get out */
   
   if (write(slots[sn].sd,&c,1) < 0)
      if (errno == EPIPE)
	 killslot(sn);
      else
	 perror("Phone Daemon Debug (sputchar)");
}

putheader(sn,c)
   int sn;
   char c;
{
   char buf[3];
   buf[0] = 020;	/* DLE character */
   buf[1] = c;
   buf[2] = '\0';
   sputstr(sn,buf);
}

termmsg(sn)
   int sn;
{
   sputchar(sn,'\020');
}

putuser(sinf,snew)
   int sinf,	/* index into slots of user we're informing */
       snew;	/* new (?) user we're informing the other guy about */
{
   char buf[300];	/* declare a big buffer */
   sprintf(buf,"%c:%s:%s:%s:",('0'+snew),	/* The user id */
			      slots[snew].loginname,
			      slots[snew].ttyname,
			      slots[snew].realname);
   sputstr(sinf,buf);
}

putmess(sn,mess)
   int sn;
   char *mess;
{
   short len = strlen(mess);

   if (!slots[sn].inuse)
      return;

   putheader(sn,'m');
   if (write(slots[sn].sd,&len,sizeof len) < 0)
      killslot(sn);
   else
      sputstr(sn,mess);
}

putmessage(mess)
   char *mess;
{
   int i;
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse && slots[i].ringsleft == -1)
	 putmess(i,mess);
}

/*
 *  User Messages:
 *	Normally, we just take the character that the user has sent us and
 *  ship it out to everyone else in the conversation.  There are exceptions
 *  to this however.  The DLE character is used to start a special user
 *  message to the phone daemon.   Following the DLE character (octal 020),
 *  is a character indicating the type of message (or action to perform).
 *  Following this character may be other information depending upon the type
 *  of message.
 *
 *	Message Char		Comment
 *	q			quit this phone conversation
 *	c:login name:tty name:	call user
 *	C:login name:tty name:	cancel call to user
 *
 *
 */

decodeumessage(sn)
   int sn;	/* Slot number that character came in on */
{
   char c;
   if (read(slots[sn].sd,&c,1) < 0 || c == 0) {
      killslot(sn);
      return;
   }

   if (c != '\020')
      shipout(sn,c);
   else {
      if (read(slots[sn].sd,&c,1) < 0) {
	 killslot(sn);
	 return;
      }
      switch (c) {
	 case 'q' :	killslot(sn);
			break;
	 case 'c' :	calluser(sn);
			break;
	 case 'C' :	cancall(sn);
			break;
      }
   }
}

shipout(sn,c)	/* send out character c to everyone but sn */
   int sn;
   char c;
{
   int i;
   for (i=0; i < MAXSLOT; i++)
      if (slots[i].inuse && slots[i].ringsleft == -1 && i != sn) {
	 if (sn != slots[i].lastwriter) {
	    slots[i].lastwriter = sn;
	    putheader(i,'0'+sn);
	 }
	 sputchar(i,c);
      }
}

calluser(sn)
   int sn;
{
   char loginname[40];
   char ttyname[40];
   char msg[80];
   if (sgetc(slots[sn].sd) != ':')
      return;
   sreadstr(slots[sn].sd,loginname);
   sreadstr(slots[sn].sd,ttyname);
   if (findslot(loginname,ttyname) != -1) {
      sprintf(msg,"%s on terminal %s is already in the conversation.",
		  loginname,ttyname);
      putmess(sn,msg);
      return;
   }

   setslot( emptyslot(),	/* get a new slot */
	    20,			/* twenty rings   */
	    loginname,		/* login name	  */
	    ttyname,		/* the tty name   */
	    "",			/* real name	  */
	    sn,			/* the caller	  */
	    -1);		/* no socket descr yet */
}

cancall(sn)
   int sn;
{
   char loginname[40];
   char ttyname[40];
   char theirslot;
   char msg[80];
   if (sgetc(slots[sn].sd) != ':')
      return;
   sreadstr(slots[sn].sd,loginname);
   sreadstr(slots[sn].sd,ttyname);
   if ((theirslot=findslot(loginname,ttyname)) == -1) {
      sprintf(msg,"%s on terminal %s has never been called.",loginname,ttyname);
      putmess(sn,msg);
      return;
   }

   if (slots[theirslot].callerid != sn) {
      /* Inform canceller that they can't cancel the call  */
      sprintf(msg,"You are not permitted to cancel the call to %s on %s.",
		  loginname,ttyname);
      putmess(sn,msg);
      return;
   }

   if (slots[theirslot].ringsleft < 0) {
      /* Tell the caller that they can't cancel someone already in
	 conversation					*/
      sprintf(msg,"%s on %s is in the conversation and can't be canceled.",
		  loginname,ttyname);
      putmess(sn,msg);
      return;
   }

   slots[theirslot].inuse = 0;
}
//E*O*F phoned.c//

echo x - phonemain.c
cat > "phonemain.c" << '//E*O*F phonemain.c//'
/*
 * Version 2 Phone  --  phonemain.c
 *
 * This is the main driving procedure for the version 2 phone.
 *
 * Program Author:  Kevin Buettner
 * Creation Date:   7/9/84
 * Revision:
 * 
 * I grant permission for coping and modifying this software
 * in any matter as the user see fit, as long as this message 
 * remains in the code.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <errno.h>
#include <signal.h>
#include <sgtty.h>
#include "phone.h"

int lastwriter;		/* slotnumber of last writer to our phone	*/
extern errno;		/* error number					*/
int daemonsocket;	/* socket to the daemon				*/
extern cleanup();

phonemain(socketname,loginname,ttyname,realname)
{
   struct sockaddr sname;
   char message[200];			/* message to send to daemon    */
   char dmess[4];			/* message from daemon		*/

   daemonsocket = socket(AF_UNIX,SOCK_STREAM,PF_UNSPEC);
   strcpy(sname.sa_data,socketname);
   sname.sa_family = PF_UNSPEC;
   if (connect(daemonsocket,&sname,
	       sizeof(sname.sa_family) + strlen(sname.sa_data)) < 0)
      errexit("Error connecting to daemon");

   sprintf(message,":%s:%s:%s:",loginname,ttyname,realname);
   if (write(daemonsocket,message,strlen(message)) < 0)
      errexit("Error writing to daemon");

   ttyinit();
   inittdfv();
   signal(SIGINT,cleanup);
   signal(SIGPIPE,cleanup);
   if (read(daemonsocket,dmess,1) < 0)
      errexit("Error reading from daemon");
   if (dmess[0] == 'a') {
      /* Eat the DLE */
      if (sgetchar(daemonsocket) != '\020');	/* ADD CODE to handle this */
      /* Eat the a */
      if (sgetchar(daemonsocket) != 'a');	/* ADD CODE to handle this */
      getusers(daemonsocket);
      converse(daemonsocket);
   }
   else {
      printf("Connection rejected.\r\n");
      shutdown(daemonsocket,2);			/* shut down the socket	*/
      _exit(0);
   }
}

errexit(m)
   char *m;
{
   perror(m);
   printf("\r\n");
   _exit(1);
}

converse(s)
   int s;
{
   int readfds;
   while (1) {
      readfds = 1 | (1<<s);

      sigsetmask(0);	/* Let all signals get through */

      while (select(15,&readfds,0,0,0) < 0)
	 if (errno != EINTR) {
	    setcooked();
	    errexit("Select Error");
	 }
      
      sigblock(1<<(SIGALRM-1));	/* don't want timer interrupting us later */

      if (readfds & (1<<s))
	 getdaemoninformation(s);
      
      if (readfds & 1)
         getkeyboardinformation(s);
   }
}

getusers(sd) /* Get added users  */
   int sd;
{
   int userid;	/* character user id */
   char loginname[40];
   char ttyname[40];
   char realname[80];
   while ((userid=sgetchar(sd)) != '\020') {
      if (sgetchar(sd) != ':')	/* handle this later */ ;
      sgetstr(sd,loginname);
      sgetstr(sd,ttyname);
      sgetstr(sd,realname);
      setslot(userid-'0',loginname,ttyname,realname);
      announcearrival(userid-'0');
   }
   plotscr();
}

sgetchar(sd)
   int sd;	/* socket descriptor */
{
   int ch;
   ch = 0;
   if (read(sd,&ch,1) < 0  || ch == 0)
      cleanup();	/* Things have gone badly indeed */
   else
      return(ch);
}

sgetstr(sd,buf)
   int sd;	/* socket descriptor */
   char *buf;	/* return buffer     */
{
   while ((*buf = sgetchar(sd)) != ':') 
      if (*buf) buf++;	/*don't take zero's*/
   *buf = '\0';	/* null terminate	*/
}


getdaemoninformation(sd)
   int sd;
{
   int ch;
   ch = sgetchar(sd);
   if (ch == 001) 	/* the syn character is sent every so often */
      return;
   else {
      if (ch != '\020')
	 puttoscr(lastwriter,ch);
      else {
	 ch = sgetchar(sd);
	 if ('0' <= ch && ch <= '>') {
	    lastwriter = ch-'0';
	    ch = sgetchar(sd);
	    puttoscr(lastwriter,ch);
	 }
	 else if (ch == 'a')
	    getusers(sd);
	 else if (ch == 'm')
	    getdmessage(sd);
	 else if (ch == 't')
	    terminateuser(sd);
      }
   }
}


getkeyboardinformation(sd)
   int sd;
{
   char c;
   c = getch();
   if (c == '\004')
      cleanup();
   else if (c == '\033' /* Escape */) {
      if (oncommandline)
	 killcommandline();
      else
	 setupcommandline();
   }
   else if (oncommandline)
      putcommandcharacter(c);
   else {
      puttoscr(MYSLOT,c);
      if (write(sd,&c,1) < 0)	/* send it away			*/
	 cleanup();
   }
}


cleanup()
{
   char buf[2];
   int flags;
   buf[0] = '\020';	/* The DLE character */
   buf[1] = 'q';	/* A q for quit	     */
   write(daemonsocket,buf,2);
   clearscreen();
   ttyend();
   printf("\r\n\n");
   shutdown(daemonsocket,2);	/* shut the socket down */
   close(daemonsocket);
   flags = FREAD;
   ioctl(0,TIOCFLUSH,&flags);
   _exit(0);
}

getdmessage(sd)
   int sd;
{
   short len;
   char buf[512];
   char *b = buf;
   if (read(sd,&len,sizeof len) < 0)
      cleanup();
   if (read(sd,buf,len) < 0)
      cleanup();
   buf[len] = '\0';
   putmessage(buf);
}

terminateuser(sd)
   int sd;
{
   char ch;
   ch = sgetchar(sd);	/* get the slot number to terminate */
   announcedeparture(ch-'0');
   killslot(ch-'0');	/* kill the slot		*/
   if (nslots < 2)
      cleanup();	/* no one left to talk to	*/
   else
      plotscr();
}
//E*O*F phonemain.c//

echo x - phoneparse.c
cat > "phoneparse.c" << '//E*O*F phoneparse.c//'
#include "cparse.h"
#include "phone.h"
#include <utmp.h>
#include <sys/file.h>

extern cleanup();
extern int msgtimeout;

/* functions that our parser definitions need to know about */
wholist(); calluser(); helpsummary(); setmtimeout(); showmtimeout();
cancelcall();


DEFSTATE(callparams)
   DATA_NS(callparams),
   NULLSTR
ENDSTATE

DEFSTATE(sparam)
   DATA,
   ERRORMSG("Integer value expected")
ENDSTATE

DEFSTATE(singleparam)
   NS("=",sparam),
   NULLSTR_NS(sparam)
ENDSTATE

DEFSTATE(setcommands)
   PFUNC_NS("mi[nterval",setmtimeout,singleparam),
   ERRORMSG("Invalid set option")
ENDSTATE

DEFSTATE(showcommands)
   VFUNC("mi[nterval",showmtimeout),
   ERRORMSG("Invalid show option")
ENDSTATE

DEFSTATE(canparams2)
   NS("to",callparams),
   NS("for",callparams),
   NULLSTR_NS(callparams)
ENDSTATE

DEFSTATE(canparams)
   NS("call",canparams2),
   NULLSTR_NS(callparams)
ENDSTATE

DEFSTATE(primary)
   VFUNC("h[elp",helpsummary),			/* help commands */
   VFUNC("?",helpsummary),
   VFUNC("qui[t",cleanup),			/* quit commands */
   VFUNC("ex[it",cleanup),
   VFUNC("han[gup",cleanup),
   VFUNC("w[ho",wholist),			/* who command */
   PFUNC_NS("cal[l",calluser,callparams),	/* call a user */
   PFUNC_NS("can[cel",cancelcall,canparams),	/* cancel a call */
   NS("se[t",setcommands),
   NS("sh[ow",showcommands),
   ERRORMSG("Unrecognized Command")
ENDSTATE

docommand(l)
   char *l;
{
   extern int (*cp_puterrormessage)();
   extern parsermessage();

   /* the following initializations should probably be done elsewhere */
   cp_puterrormessage = parsermessage;
   cp_setctab('=',CP_CCTOKEN);	/* the equal sign is a token by itself */

   cparse(primary,l);
}

parsermessage(code)
   int code;
{
   char buf[200];
   switch (code) {
      case CP_EEXTRAINPUT :
	 sprintf(buf,"Error: Extra input encounted ('%s').",cp_badtoken);
	 putmessage(buf);
	 break;
      case CP_EUNRECOGNIZEDTOKEN :
	 sprintf(buf,"Error: %s ('%s').",cp_errormessage,cp_badtoken);
	 putmessage(buf);
	 break;
      default :
	 putmessage("Error:  Unknown Parser Error.");
	 break;
   }
}

helpsummary(code)
   int code;
{
   putmessage("help: Command Summary...");
   putmessage("help:   help      -- displays this summary");
   putmessage("help:   who       -- lists users on the system");
   putmessage("help:   exit      -- leaves the phone program");
   putmessage("help:   call User [TTYName]  -- calls a user");
   putmessage("help:   cancel User [TTYName]  -- cancel a call");
   putmessage("help:   set minterval Value     -- sets message interval");
   putmessage("help:   show minterval          -- shows message interval");

}

setmtimeout(argc,argv)
   int argc;
   char **argv;
{
   int val;
   if (sscanf(argv[0],"%d",&val) != 1)
      putmessage("set: numeric parameter required for mtimeout");
   else if (val < 0 || val > 60)
      putmessage("set: reasonable values required for mtimeout");
   else {
      char buf[100];
      sprintf(buf,"set: mtimeout, old value=%d  new value=%d",msgtimeout,val);
      msgtimeout = val;
      putmessage(buf);
   }
}

showmtimeout(argc,argv)
{
   char buf[100];
   sprintf(buf,"show: mtimeout = %d",msgtimeout);
   putmessage(buf);
}

#define UTSIZE (sizeof (struct utmp))
#define UTLSIZE (sizeof urec.ut_line)
#define UTNSIZE (sizeof urec.ut_name)

wholist(p)
   int p;	/* dummy parameter */
{
   int ufd;	/* utmp file descriptor */
   struct utmp urec;
   char user[50];
   char ttyname[50];
   char msg[100];
   char buf[100];
   static char facname[] = "who: ";

   if ((ufd = open("/etc/utmp",O_RDONLY,0)) < 0)
      putmessage("Error opening the utmp file.");
   else {
      strcpy(msg,facname);
      while (UTSIZE == read(ufd,&urec,UTSIZE)) {
	 if (urec.ut_name[0] != '\0') {
	    user[0] = '\0';
	    strncat(user,urec.ut_name,UTNSIZE);
	    ttyname[0] = '\0';
	    strncat(ttyname,urec.ut_line,UTLSIZE);
	    sprintf(buf,"%s (%s)",user,ttyname);
	    if (strlen(buf)+strlen(msg) > 70) {
	       putmessage(msg);
	       strcpy(msg,facname);
	       strcat(msg,buf);
	    }
	    else {
	       if (strlen(msg) > strlen(facname))
		  strcat(msg,", ");
	       strcat(msg,buf);
	    }
	 }
      }
      putmessage(msg);
      close(ufd);
   }
}

calluser(argc,argv)
   int argc;
   char **argv;
{
   userinformation *u;
   char buf[100];
   extern int daemonsocket;
   userinformation *getuser();

   if (argc == 0) {
      putmessage("You must tell me who to call.");
      return;
   }

   if (argc > 2) {
      putmessage("Too many parameters to call.  Usage is: call user ttyname.");
      return;
   }

   if ((u = getuser(argv[0],((argc == 2) ? argv[1] : ""))) <= 0) {
      if ((int) u < 0)
	 putmessage("Error accessing /etc/utmp.");
      else if (argc == 2)
	 putmessage("User not logged in on that terminal.");
      else
	 putmessage("User not logged in.");
      return;
   }

   if (u->psockname[0] != '\0') {
      putmessage("User already in a phone conversation.");
      return;
   }

   if (u->canwriteto == 0) {
      putmessage("call: Permission Denied. (Can't write to terminal)");
      return;
   }

   sprintf(buf,"\020c:%s:%s:",u->username,u->ttyname);
   write(daemonsocket,buf,strlen(buf));

   sprintf(buf,"Attempting to call %s on terminal %s.",u->username,
						       u->ttyname);
   putmessage(buf);
}

cancelcall(argc,argv)
   int argc;
   char **argv;
{
   userinformation *u;
   char buf[100];
   extern int daemonsocket;
   userinformation *getuser();

   if (argc == 0) {
      putmessage("You must tell me who to cancel the call to.");
      return;
   }

   if (argc > 2) {
      putmessage("Too many parameters to cancel.  Usage is: cancel user ttyname.");
      return;
   }

   if ((u = getuser(argv[0],((argc == 2) ? argv[1] : ""))) <= 0) {
      if ((int) u < 0)
	 putmessage("Error accessing /etc/utmp.");
      else if (argc == 2)
	 putmessage("User not logged in on that terminal.");
      else
	 putmessage("User not logged in.");
      return;
   }


   sprintf(buf,"\020C:%s:%s:",u->username,u->ttyname);
   write(daemonsocket,buf,strlen(buf));

   sprintf(buf,"Attempting to cancel call to %s on terminal %s.",u->username,
						                 u->ttyname);
   putmessage(buf);
}
//E*O*F phoneparse.c//

echo x - slotmanager.c
cat > "slotmanager.c" << '//E*O*F slotmanager.c//'
/*
 * Version 2 Phone  -- slotmanager.c
 *
 * Each person has a section of the screen called a slot.  This code manages
 * the slots.
 *
 * Program Author:  Kevin A. Buettner
 * Creation Date:   7-8-84
 * Revisions:
 */

#include "phone.h"


#define MAXLINE 200			/* number of lines in the free line
						pool                         */

linetype lines[MAXLINE];		/* lines                             */
slottype slots[MAXSLOT];		/* slots			     */
int nslots;				/* number of slots in use	     */
int maxlines;				/* maximum number of lines any given
					   slot is allowed to have...this will
					   vary with the number of slots in
					   use.				     */
int freelinepool;			/* index of first line in the free
					   line pool			     */
int nslines = 24;			/* number of screen lines available  */
int nflines = 1;			/* number of free lines at bottom of
					   screen. We must have at least one */

/* Procedure Name: initslots
 * Description:    initializes the inuse field of each slot to zero.
 * Parameters:     None.
 * Side Effects:   Obvious.
 */

initslots()
{
   int i;
   for (i=0;i<MAXSLOT;i++)
      slots[i].inuse = 0;
   
   nslots = 0;		/* No slots used yet		      */

   initlines();		/* also initialize the free line pool */
}


/* Procedure Name: initlines
 * Description:    initializes the free lines pool
 * Parameters:     None.
 * Side Effects:   Obvious.
 */

initlines()
{
   register int i;
   for (i=0;i<MAXLINE;i++)
      lines[i].nextl = i+1;
   lines[MAXLINE-1].nextl = -1;
}


/* Function Name: newslot
 * Description:   Returns the index of the first unused slot found.
 * Parameters:    None.
 * Side Effects:  None.
 */

int newslot()
{
   int i;
   for (i=0; i<MAXSLOT && !slots[i].inuse; i++);
   return((i>=MAXSLOT) ? -1 : i);
}

/* Procedure Name: setslot
 * Description:    Fills in the login name, tty, and real life name from
 *		the supplied parameters.
 * Parameters:  slotnum		-- index of slot to fill in
 *		loginname	-- the login name
 *		ttyname		-- terminal
 *		realname	-- Real name of the user
 * Returns:	slotnum
 * Side Effects: See for yourself.
 */

setslot(slotnum,loginname,ttyname,realname)
   int slotnum;
   char *loginname;
   char *ttyname;
   char *realname;
{
#define SLT slots[slotnum]
   
   SLT.inuse	= 1;
   strcpy(SLT.loginname,loginname);
   strcpy(SLT.ttyname,ttyname);
   strcpy(SLT.realname,realname);
   SLT.xpos	= 0;			/* initial x position */
   SLT.ypos     = 0;			/* initial y position (within this
					   slot)		*/
   SLT.lcount	= 1;			/* We will soon have one line	*/
   SLT.lqhead	= allocline();		/* Get a line from the free line pool */
   SLT.lqtail	= SLT.lqhead;		/* set the tail to the head	*/
   nslots++;
   adjslots();				/* This will adjust the top and
					   bottom line for all slots      */
   return(slotnum);			/* plot/replot screen		     */
}

/* Procedure Name: killslot
 * Description: Frees up a slot and lines associated with that slot.
 * Parameters:  slotnum -- number of slot to remove.
 * Side Effects: Other used slots will be adjusted as well.
 */

killslot(slotnum)
   int slotnum;
{
   register int s,t;
   s = SLT.lqhead;
   while (s != -1) {
      t = lines[s].nextl;
      freeline(s);
      s = t;
   }
   SLT.inuse = 0;
   nslots--;
   adjslots();				/* Adjust the rest of the slots     */
   return(slotnum);
}

adjslots()
{
   register int i,s;
   maxlines = (nslines - nflines - 2*nslots + 1) / nslots;
   i = 0;
   s = 0;
   for (s=0;s < MAXSLOT; s++)
      if (slots[s].inuse) {
	 slots[s].topline = 1 + i*(maxlines + 2);
	 slots[s].bottomline = slots[s].topline + maxlines - 1;
	 i++;
      while (slots[s].lcount > maxlines)
	 killtopline(s);
      }
}




/* Function Name: allocline
 * Description:   Returns an index of a line allocated out of the free line
 *		pool.
 * Parameters:  None.
 * Side Effects:  The global variable freelinepool is modified.
 */

allocline()
{
   register int t = freelinepool;
   freelinepool = lines[freelinepool].nextl;
   lines[t].llen = 0;
   lines[t].line[0] = '\0';
   lines[t].nextl = -1;
   return(t);
}


/* Procedure Name: freeline
 * Description:    Returns a line to the free line pool.
 * Parameters:     l  -- line to return to the free line pool.
 * Side Effects:   The global variable freelinepool is modified.
 */

freeline(l)
   int l;
{
   lines[l].nextl = freelinepool;
   freelinepool = l;
}


/* Procedure Name: killtopline
 * Description:    Removes a line from the head of the line queue associated
 *	with a given slot.
 * Parameters:	   slotnum  -- number of slot to remove line from head
 * Side Effects:   The queue head and the number of lines in the queue
 *	are updated for the slot.
 */

killtopline(slotnum)
   int slotnum;
{
   int t = SLT.lqhead;
   SLT.lqhead = lines[t].nextl;
   freeline(t);
   SLT.lcount--;
}

/* Procedure Name: addbottomline
 * Description:    adds a line to the tail of the queue (the bottom line)
 * Parameters:	   slotnum  -- number of slot to add a blank line to.
 * Side Effects:   The top line may be deleted if the number of lines in 
 *	the slot is already equal to maxlines.  The lcount and lqtail fields
 *	of the slot should also change.
 */

slt_addbottomline(slotnum)
   int slotnum;
{
   int t = allocline();
   lines[SLT.lqtail].nextl = t;
   SLT.lqtail = t;
   SLT.lcount++;
   if (SLT.lcount > maxlines)
      killtopline(slotnum);
}

slt_addcharacter(slotnum,ch)
   int slotnum;
   char ch;
{
   linetype *l;
   l = &lines[SLT.lqtail];
   l->line[l->llen] = ch;
   (l->llen)++;
   l->line[l->llen] = '\0';
}

slt_delcharacter(slotnum)
   int slotnum;
{
   linetype *l;
   l = &lines[SLT.lqtail];
   if (l->llen > 0) {
      (l->llen)--;
      l->line[l->llen] = '\0';
   }
}

slt_wipeline(slotnum)
   int slotnum;
{
   linetype *l;
   l = &lines[SLT.lqtail];
   l->llen = 0;
   l->line[0] = '\0';
}

char *ldelword(l,max)
   linetype *l;
   int max;	/* maximum number of characters to search back */
{
   static char buf[100];	/* here is where we store the word just
				   deleted				*/
   char *w;
   char *s;

   s = &l->line[(l->llen)-1];
   if (l->llen < max)
      max = l->llen;
   
   while (max > 0 && *s == ' ') {
      s--;			/* skip over trailing blanks	*/
      max--;
   }

   while (max > 0 && *s != ' ') {
      s--;			/* skip to previous blank	*/
      max--;
   }
   
   s++;
   l->llen = s - l->line;
   w = buf;
   while (*w++ = *s++);
   l->line[(l->llen)] = '\0';
   return(buf);
}

char *slt_delword(slotnum,max)
{
   return(ldelword(&lines[SLT.lqtail],max));
}
//E*O*F slotmanager.c//

echo x - ttycmio.c
cat > "ttycmio.c" << '//E*O*F ttycmio.c//'
#include "phone.h"
#include <sys/time.h>
#include <signal.h>

extern int ttype_scroller;
extern int ttype_dumb;

int (*announcearrival)();
int (*announcedeparture)();
int (*plotscr)();
int (*puttoscr)();
int (*putmessage)();
int (*killcommandline)();
int (*setupcommandline)();
int (*putcommandcharacter)();

nullfunc(){}
cm_plotscr();
cm_puttoscr();
cm_putmessage();
cm_killcommandline();
cm_setupcommandline();
cm_putcommandcharacter();
dumb_announcearrival();
dumb_announcedeparture();
dumb_puttoscr();
dumb_putmessage();
dumb_killcommandline();
dumb_setupcommandline();
dumb_putcommandcharacter();

extern int rcvals[];

char prompt[] = "Command> ";
int oncommandline;	/* flag that tells us we are currently on the
			   command line					*/
int curslot = 0;	/* slot to get the x and y coordinates from */
int cmdidx;		/* index into line structure of command that
			   user is typing			    */
int msgqh;		/* head of the message queue		*/
int msgqt;		/* tail of the message queue		*/
int curmcidx;		/* index of current line displayed in
			   the message/command area of the
			   screen				*/
int msgtimeout = 3;	/* three seconds as the initial timeout	*/

inittdfv()	/* initialize terminal dependent function variables */
{
   oncommandline = 0;
   if (ttype_dumb) {
      int i;
      announcearrival = dumb_announcearrival;
      announcedeparture = dumb_announcedeparture;
      putmessage        = dumb_putmessage;
      plotscr = nullfunc;
      puttoscr = dumb_puttoscr;
      killcommandline = dumb_killcommandline;
      setupcommandline = dumb_setupcommandline;
      putcommandcharacter = dumb_putcommandcharacter;
      for (i=0;i<MAXSLOT;i++)
	 rcvals[i]=0;
      curslot=MYSLOT;
   }
   else {
      announcearrival = nullfunc;
      announcedeparture = nullfunc;
      plotscr      = cm_plotscr;
      puttoscr     = cm_puttoscr;
      putmessage	= cm_putmessage;
      killcommandline = cm_killcommandline;
      setupcommandline = cm_setupcommandline;
      putcommandcharacter = cm_putcommandcharacter;
      cmdidx	= -1;	/* these initializations might better be done */
      msgqh	= -1;	/* elsewhere				*/
      msgqt	= -1;
      curmcidx	= -1;
   }
}


/*
 * Below are the functions for terminals capable of moving the cursor
 * about.
 */
cm_plotscr()
{
   char buf[90];
   int i,j,k,l;
   clearscreen();
   for (i=0,j=0;i<MAXSLOT;i++)
      if (slots[i].inuse) {
	 j++;
	 sprintf(buf,"%s on %s (%s)",slots[i].loginname,
				    slots[i].ttyname,
				    slots[i].realname);
	 setpos(0,slots[i].topline-1);
	 putstr(buf);
	 slots[i].ypos = 0;
	 slots[i].xpos = 0;
	 for (l=slots[i].lqhead,k=0; l != -1; l=lines[l].nextl,k++) {
	    setpos(0,slots[i].topline+k);
	    putstr(lines[l].line);
	    slots[i].ypos = k;
	    slots[i].xpos = lines[l].llen;
	 }

	 curslot = i;

	 if (j < nslots) {
	    for (k=0;k<78;k++)
	       buf[k] = '-';
	    buf[78] = '\0';
	    setpos(0,slots[i].bottomline+1);
	    putstr(buf);
	 }
      }
   if (oncommandline) {
      curslot = -1;
      curmcidx = -1;
      cm_putcommandline();
   }
}

#define SLT slots[sn]
cm_puttoscr(sn,ch)
   int sn;
{
   int i;

   /* Move the cursor if it is in the wrong place	*/
   if (sn != curslot) {
      curslot = sn;
      setpos(SLT.xpos,SLT.topline+SLT.ypos);
   }

   if (ch < ' ') {
      switch (ch) {
	 case 007	: beep();
			  break;
	 case 010	: /* backspace */
			  if (SLT.xpos > 0) {
			     SLT.xpos--;
			     slt_delcharacter(sn);
			     putstr("\b \b");
			  }
			  break;
	 case 011	:
			  /* ctrl-i  -- tab */
			  if (SLT.xpos < 72) {
			     do {
				putch(' ');
				SLT.xpos++;
				slt_addcharacter(sn,' ');
			      } while (SLT.xpos % 8  != 0);
			   }
			   break;
	 case 012	:
	 case 015	: /* newline */
			  cm_newline(sn);
			  break;
	 case 014	: /* CTRL-L  -- Replot     */
			  if (sn == MYSLOT)
			     plotscr();
			  break;
	 case 016	: /* CTRL-N */
			  cm_clearslot(sn);
			  break;
	 case 027	:
			  /* CTRL-W  -- Clear word */
			  i = strlen(slt_delword(sn,80));
			  while (i > 0) {
			     SLT.xpos--;
			     putstr("\b \b");
			     i--;
			  }
			  break;
	 case 030	: /* CTRL-X  -- Clear line */
			  cm_clearcurrent(sn);
			  break;
	 default	: /* ignore  */
			  if (sn == MYSLOT)
			     beep();
			  break;
      }
   }
   else {
      if (SLT.xpos > 78) {
	 putch('-');
	 slt_addcharacter(sn,'-');
	 cm_newline(sn);
      }
      putch(ch);
      SLT.xpos++;
      slt_addcharacter(sn,ch);
   }

   if (oncommandline) {
      curslot = -1;
      setcommandpos();
   }
}

cm_newline(sn)
   int sn;
{
   if (SLT.ypos >= maxlines-1) {
      if (ttype_scroller)
	 scrollup(SLT.topline,SLT.bottomline);
      else {
         SLT.ypos = 0;
	 cm_cleartop(sn);
      }
   }
   else if (SLT.lcount == maxlines && !ttype_scroller) {
      SLT.ypos++;
      cm_cleartop(sn);
   }
   else
      SLT.ypos++;

   slt_addbottomline(sn);
   SLT.xpos = 0;

   setpos(SLT.xpos,SLT.topline+SLT.ypos);
}

cm_cleartop(sn)
   int sn;
{
   setpos(0,SLT.topline+SLT.ypos);
   if (cleartoend() < 0) {
      char buf[80];
      int i,l;
      l = lines[SLT.lqhead].llen;
      for (i=0 ; i < l ; i++)
	 buf[i] = ' ';
      buf[l] = '\0';
      putstr(buf);
   }
}

cm_clearcurrent(sn)
   int sn;
{
   setpos(0,SLT.topline+SLT.ypos);
   if (cleartoend() < 0) {
      char buf[80];
      int i,l;
      l = lines[SLT.lqtail].llen;
      for (i=0 ; i < l ; i++)
	 buf[i] = ' ';
      buf[l] = '\0';
      putstr(buf);
   }
   slt_wipeline(sn);
   SLT.xpos = 0;
   setpos(0,SLT.topline+SLT.ypos);
}

cm_putmess()
{
   int oldlen;
   int i;
   char buf[80];


   if (curmcidx == cmdidx && msgqh == -1)
      return;	/* nothing to do. */
   
   if (curmcidx == -1)
      oldlen = 0;
   else
      oldlen = lines[curmcidx].llen;
   
   if (curmcidx == msgqh)
      delfromlq(&msgqh,&msgqt);
   
   /* Now set curmcidx and set up buf */
   if (msgqh != -1)
      curmcidx = msgqh;
   else
      curmcidx = cmdidx;
   
   if (curmcidx == -1) 
      buf[0] = '\0';
   else
      strcpy(buf,lines[curmcidx].line);
   
   setpos(0,23);
   if (strlen(buf) < oldlen && cleartoend() < 0) {
      for (i=strlen(buf) ; i < oldlen ; i++)
	 buf[i] = ' ';
      buf[oldlen] = '\0';
   }
   putstr(buf);
   if (curmcidx != -1 && curmcidx != cmdidx)
      cm_starttimer();
   if (curslot >= 0)
      setpos(slots[curslot].xpos,slots[curslot].ypos+slots[curslot].topline);
   else 
      setcommandpos();
      
}

cm_starttimer()
{
   struct itimerval tval;
   timerclear(&tval.it_interval);
   timerclear(&tval.it_value);
   tval.it_value.tv_sec = msgtimeout;
   signal(SIGALRM,cm_putmess);
   setitimer(ITIMER_REAL,&tval,0);
}

cm_putmessage(buf)
   char *buf;
{
   int m;
   m = allocline();
   strcpy(lines[m].line,buf);
   lines[m].llen = strlen(buf);
   addtolq(&msgqh,&msgqt,m);
   if (curmcidx == -1) 
      cm_putmess();
   else if (curmcidx == cmdidx)
      cm_putmess();	/* might want to do this another way */
}

addtolq(qhp,qtp,e)	/* this should probably be moved to slotmanager.c */
   int *qhp,	/* pointer to the queue head	*/
       *qtp,	/* pointer to the queue tail	*/
       e;	/* element to add to the queue	*/
{
   if (*qhp == -1) {	/* queue is empty  */
      *qhp = e;
      *qtp = e;
      lines[e].nextl = -1;
   }
   else {		/* queue isn't empty */
      lines[*qtp].nextl = e;
      lines[e].nextl = -1;
      *qtp = e;
   }
}

delfromlq(qhp,qtp)	/* deletes the first element from a line queue	*/
   int *qhp,	/* pointer to the queue head index	*/
       *qtp;	/* pointer to the queue tail index	*/
{
   int t;
   if (*qhp == -1)	/* queue is empty	*/
      return;		/* What is there to do with an empty queue?	*/
   else {
      t = *qhp;
      if ((*qhp = lines[*qhp].nextl) == -1)
	 *qtp = -1;
      freeline(t);
   }
}


cm_clearslot(sn)	/* clears a section of screen for a user */
   int sn;		/* slot number to clear	*/
{
   int y;	/* y-pos within slot to clear next */
   register int i,l;
   register int done;
   char buf[100];

   y = SLT.ypos - SLT.lcount + 1;
   if (y<0)
      y=y+maxlines;
   done = 0;
   while (!done) {
      if ((l=lines[SLT.lqhead].llen) > 0) {
	 setpos(0,SLT.topline+y);
	 if (cleartoend() < 0) { /* then we gotta fake it */
	    for (i=0;i<l;i++)
	       buf[i] = ' ';
	    buf[l] = '\0';
	    putstr(buf);
	 }
      }
      y = (y + 1) % maxlines;
      if (SLT.lqhead != SLT.lqtail)
	 killtopline(sn);
      else {
	 SLT.ypos = 0;
	 SLT.xpos = 0;
	 lines[SLT.lqtail].llen = 0;
	 lines[SLT.lqtail].line[0] = '\0';
	 done = 1;
      }
   }
   setpos(SLT.xpos,SLT.topline+SLT.ypos);
}

cm_cleartoend(xpos,ypos,len)
   int xpos,ypos,len;
{
   char buf[100];
   register int i;
   setpos(xpos,ypos);
   if (cleartoend() < 0) {
      for (i=0;i<len;i++)
	 buf[i] = ' ';
      buf[len] = '\0';
      putstr(buf);
   }
}


cm_killcommandline()
{
   curslot = MYSLOT;
   if (curmcidx == cmdidx) {
      cm_cleartoend(0,23,lines[cmdidx].llen);	/* clear the line */
      curmcidx = -1;
   }
   freeline(cmdidx);
   cmdidx = -1;		/* no more command */
   oncommandline = 0;
   setpos(slots[MYSLOT].xpos,slots[MYSLOT].topline+slots[MYSLOT].ypos);
}

cm_putcommandline()
{
   int oldlen;
   register int i;
   char buf[100];
   if (curmcidx == cmdidx)
      return;		/* no need to put anything out */
   if (curmcidx < 0)
      oldlen = 0;
   else
      oldlen = lines[curmcidx].llen;
   
   if (curmcidx == msgqh) 
      delfromlq(&msgqh,&msgqt);		/* get rid of message line */

   strcpy(buf,lines[cmdidx].line);
   setpos(0,23);
   if (strlen(buf) < oldlen && cleartoend() < 0) {
      for (i=strlen(buf) ; i < oldlen ; i++)
	 buf[i] = ' ';
      buf[oldlen] = '\0';
   }
   putstr(buf);

   if (strlen(buf) != lines[cmdidx].llen)
      setpos(lines[cmdidx].llen,23);	/* move to the proper spot */
   
   curmcidx = cmdidx;
}



cm_setupcommandline()
{
   cmdidx = allocline();
   strcpy(lines[cmdidx].line,prompt);
   lines[cmdidx].llen = strlen(prompt);
   curslot = -1;
   oncommandline = 1;
   cm_putcommandline();
}

setcommandpos()
{
   if (cmdidx != curmcidx)
      setpos(0,23);
   else
      setpos(lines[cmdidx].llen,23);
}

cm_putcommandcharacter(ch)
   char ch;
{
   cm_putcommandline();	/* put it out if it isn't already there. */
   if (ch < ' ')
      switch (ch) {
	 case 010 : 
	    if (lines[cmdidx].llen > strlen(prompt)) {
	       putstr("\b \b");
	       lines[cmdidx].llen--;
	       lines[cmdidx].line[lines[cmdidx].llen] = '\0';
	    }
	    else
	       beep();
	    break;
	 case 012 :
	 case 015 :
	    docommand(&lines[cmdidx].line[strlen(prompt)]);
	    cm_killcommandline();
	    break;
	 case 030 :
	    cm_cleartoend(strlen(prompt),23,lines[cmdidx].llen-strlen(prompt));
	    lines[cmdidx].line[strlen(prompt)] = '\0';
            setpos(strlen(prompt),23);
	    break;
	 default  :
	    beep();
	    break;
      }
   else if (lines[cmdidx].llen > 78)
      beep();
   else {
      putch(ch);
      lines[cmdidx].line[lines[cmdidx].llen] = ch;
      lines[cmdidx].llen++;
      lines[cmdidx].line[lines[cmdidx].llen] = '\0';
   }
}
//E*O*F ttycmio.c//

echo x - ttydumbio.c
cat > "ttydumbio.c" << '//E*O*F ttydumbio.c//'
#include "phone.h"
#include <sys/time.h>
#include <signal.h>


#define REPLOT_C_INTERVAL 18	/* 18 characters between replots */
#define REPLOT_T_INTERVAL 6

int rcvals[MAXSLOT];
int executingcommand = 0;	/* indicates that a command is executing */
extern char prompt[];		/* commandline prompt		*/
extern int cmdidx;		/* index of command line in line table */

dumb_announcearrival(sn)
{
   char buf[100];
   sprintf(buf,"\r\nYou are talking to %s on %s (%s).\r\n",
	       slots[sn].loginname,
	       slots[sn].ttyname,
	       slots[sn].realname);
   putstr(buf);
   dumb_replot();
}

dumb_announcedeparture(sn)
   int sn;
{
   char buf[100];
   sprintf(buf,"\r\n%s on %s (%s) is leaving the conversation.\r\n",
	       slots[sn].loginname,
	       slots[sn].ttyname,
	       slots[sn].realname);
   putstr(buf);
   dumb_replot();
}


dumb_putmessage(mess)
   char *mess;
{
   char buf[100];
   if (executingcommand)  {
      sprintf(buf,"\r\n%s",mess);
      putstr(buf);
   }
   else
   {
      sprintf(buf,"\r\n%s\r\n",mess);
      putstr(buf);
      dumb_replot();
   }
}


dumb_replot()
{
   putstr("\r\n");
   if (curslot == -1) {
      curslot = MYSLOT;		/* fake out putcommandline */
      dumb_putcommandline();
   }
   else {
      dumb_putprompt(curslot);
      putstr(lines[slots[curslot].lqtail].line);
      slots[curslot].xpos += strlen(lines[slots[curslot].lqtail].line);
   }
}

dumb_putprompt(sn)
   int sn;
{
   char buf[100];
   if (sn < 0)
      return;
   if (sn == MYSLOT)
      strcpy(buf,">");
   else if (nslots == 2)
      strcpy(buf,":");
   else
      sprintf(buf,"%s(%s):",slots[sn].loginname,slots[sn].ttyname);
   slots[sn].xpos = strlen(buf);
   putstr(buf);
}

dumb_printslots()
{
   int i;
   int l;
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse) {
	 printf("\r\n%s on %s (%s)::\r\n",
		slots[i].loginname,
		slots[i].ttyname,
		slots[i].realname);
	 l=slots[i].lqhead;
	 while (l != -1) {
	    printf("%s\r\n",lines[l].line);
	    l = lines[l].nextl;
	 }
      }
   printf("---\r\n");
   dumb_replot();
}


#define SLT slots[sn]
#define NEWSLOT if (curslot >= 0) rcvals[curslot]=0; rcvals[sn]=0; curslot=sn; dumb_replot();
#define PUTCH(c) if (sn == curslot) putch(c);rcvals[sn]++
#define PUTSTR(s) if (sn == curslot) putstr(s);rcvals[sn]++

dumb_puttoscr(sn,ch)
   int sn;
   char ch;
{
   int i;

   if (sn != curslot && (rcvals[sn] > REPLOT_C_INTERVAL || sn == MYSLOT)) {
      NEWSLOT;
   }

   if (ch < ' ') {
      switch (ch) {
	 case 007	: /* bell */
			  if (curslot != sn) {
			      NEWSLOT
			  }
			  beep();
			  break;
	 case 010	: /* backspace */
			  if (SLT.xpos > 0) {
			     SLT.xpos--;
			     slt_delcharacter(sn);
			     PUTSTR("\b \b");
			  }
			  break;
	 case 011	: /* CTRL-I  -- tab */
			  if (SLT.xpos < 72) {
			     do {
				PUTCH(' ');
				SLT.xpos++;
				slt_addcharacter(sn,' ');
			     } while (SLT.xpos % 8 != 0);
			  }
			  break;
	 case 012	:
	 case 015	: /* newline */
			  if (curslot != sn) {
			     NEWSLOT
			  }
			  PUTSTR("\n");
			  slt_addbottomline(sn);
			  SLT.xpos = 0;
			  dumb_putprompt(sn);
			  break;
	 case 014	: /* CTRL-L -- Replot     */
			  if (sn == MYSLOT)
			     dumb_printslots();
			  break;
	 case 016	: /* CTRL-N -- Clear Slot */
			  /* how do we do this on a hardcopy?? */
			  break;
	 case 027	:
			  /* CTRL-W  -- Clear word */
			  i = strlen(slt_delword(sn,80));
			  while (i > 0) {
			     SLT.xpos--;
			     PUTSTR("\b \b");
			     i--;
			  }
			  break;
	 case 030	: /* CTRL-X -- Clear Line */
			  slots[sn].xpos -= lines[SLT.lqtail].llen;
			  if (sn == curslot) {
			     int i;
			     for (i=lines[SLT.lqtail].llen; i > 0; i--)
				putstr("\b \b");
			  }
			  rcvals[sn]++;
			  slt_wipeline(sn);
			  break;
	 default	: /* ignore */
			  if (sn == MYSLOT)
			     beep();
			  break;
      }
   }
   else {
      if (SLT.xpos > 78) {
	 if (sn != curslot) {
	    NEWSLOT
	 }
	 PUTSTR("-\n");
	 slt_addcharacter(sn,'-');
	 slt_addbottomline(sn);
	 SLT.xpos = 0;
	 dumb_putprompt(sn);
      }
      PUTCH(ch);
      SLT.xpos++;
      slt_addcharacter(sn,ch);
   }

   dumb_starttimer();
}


dumb_newslot()
{
   int i;
   int midx;
   if (curslot == -1)
      curslot = MYSLOT;	/* can't have it messing up on us	*/
   midx = curslot;
   rcvals[midx] = 0;
   for (i=0;i<MAXSLOT;i++)
      if (slots[i].inuse && rcvals[i] > rcvals[midx])
	 midx = i;
   if (rcvals[midx] > 0) {
      curslot = midx;
      dumb_replot();
      dumb_starttimer();
   }
   else if (oncommandline && curslot == MYSLOT)
      curslot = -1;	/* put curslot back if we didn't do anything */
}

dumb_starttimer()
{
   struct itimerval tval;
   timerclear(&tval.it_interval);
   timerclear(&tval.it_value);
   tval.it_value.tv_sec = REPLOT_T_INTERVAL; 
			/* three seconds to read a message */
   signal(SIGALRM,dumb_newslot);
   setitimer(ITIMER_REAL,&tval,0);
}

dumb_killcommandline()
{
   curslot = MYSLOT;
   freeline(cmdidx);
   oncommandline=0;
   dumb_replot();
}

dumb_setupcommandline()
{
   cmdidx = allocline();
   strcpy(lines[cmdidx].line,prompt);
   lines[cmdidx].llen = strlen(prompt);
   oncommandline = 1;
   dumb_putcommandline();
}

dumb_putcommandline()
{
   if (curslot == -1) {
      /* Then there's nothing to do */
   }
   else {
      putstr("\r\n");
      putstr(lines[cmdidx].line);
      curslot = -1;
   }
}

dumb_putcommandcharacter(ch)
   char ch;
{
   int i;
   dumb_putcommandline();	/* put out command line if need be */
   if (ch < ' ')
      switch (ch) {
	 case 010 : 
	    if (lines[cmdidx].llen > strlen(prompt)) {
	       putstr("\b \b");
	       lines[cmdidx].llen--;
	       lines[cmdidx].line[lines[cmdidx].llen] = '\0';
	    }
	    else
	       beep();
	    break;
	 case 012 :
	 case 015 :
	    executingcommand = 1;
	    docommand(&lines[cmdidx].line[strlen(prompt)]);
	    executingcommand = 0;
	    dumb_killcommandline();
	    break;
	 case 030 :
	    for (i=lines[cmdidx].llen-strlen(prompt);i>0;i--)
	       putstr("\b \b");
	    lines[cmdidx].line[strlen(prompt)] = '\0';
	    lines[cmdidx].llen = strlen(prompt);
	    break;
	 default  :
	    beep();
	    break;
      }
   else if (lines[cmdidx].llen > 78)
      beep();
   else {
      putch(ch);
      lines[cmdidx].line[lines[cmdidx].llen] = ch;
      lines[cmdidx].llen++;
      lines[cmdidx].line[lines[cmdidx].llen] = '\0';
   }
}
//E*O*F ttydumbio.c//

echo x - ttyio.c
cat > "ttyio.c" << '//E*O*F ttyio.c//'
#include <sys/types.h>
#include <sgtty.h>
#include <signal.h>
#include <errno.h>
#include "phone.h"

static char *_al,	/* add new blank line */
	    *_ce,	/* clear to end of line */
	    *_cl,	/* clear screen */
	    *_cm,	/* cursor motion (heavily used) */
	    *_cs,	/* change scrolling region */
            *_dl,	/* delete line (rest of lines should scroll up) */
	    *_do,	/* down one line */
	    *_kb,	/* sequence sent by backspace key */
	    *_kd,	/* sequence sent by the down arrow key */
	    *_kl,	/* sequence sent by the left arrow key */
	    *_kr,	/* sequence sent by the right arrow key */
	    *_ku,	/* sequence sent by the up arrow key */
	    *_ke,	/* sequence to end cursor key mode */
	    *_ks,	/* sequence to set cursor key mode */
	    *_nd,	/* nondestructive space (cursor right) */
	    *_nl,	/* newline character */
	    *_se,	/* end standout mode */
	    *_sf,	/* scroll forwards */
	    *_so,	/* enter stand out mode */
	    *_sr,	/* scroll reverse */
	    *_ta,	/* tab character (sent) */
	    *_te,	/* string to end programs that use cm */
	    *_ti,	/* string to start programs that use cm */
	    *_up;	/* Upline (cursor up) */


static char tbuf[1024];	/* the buffer needed and used by termlib */
static char area[500];	/* this is the place where all of the strings for
			   the capabilities above are stored. The capabilities
			   above are just pointers into this area */
static char *areap;	/* area pointer */
static char ttype[40];	/* terminal type found from the environment    */
char *UP;		/* global variable needed by libtermcap.a 
				(spec. tgoto)	                       */
short int ospeed;	/* the output speed that we're transmitting at */
int ttype_dumb;		/* terminal is incapable of moving cursor	*/
int ttype_scroller;	/* terminal can scroll sections    		*/


setrare()
{
   struct sgttyb termb;

   ioctl(0,TIOCGETP,&termb);
   termb.sg_flags |= CBREAK;
   termb.sg_flags &= ~ECHO;
   ioctl(0,TIOCSETP,&termb);
}

setcooked()
{
   struct sgttyb termb;

   ioctl(0,TIOCGETP,&termb);
   termb.sg_flags &= ~CBREAK;
   termb.sg_flags |= ECHO;
   ioctl(0,TIOCSETP,&termb);
}

ttyinit()
{
   extern int errno;
   int tgetent();
   char *getenv();

   strcpy(ttype,getenv("TERM"));

   switch (tgetent(tbuf,ttype)) {
      case -1 : printf("Error opening the termcap file\r\n");
		printf("Dumb terminal assumed.\r\n");
		ttype_dumb = 1;
		ttype_scroller = 0;
		break;
      case 0  : printf("Terminal type not found in termcap file\r\n");
		printf("Hardcopy terminal assumed.\r\n");
		ttype_dumb = 1;
		ttype_scroller = 0;
		break;
      default : gettermvars();
		break;
   }

   setrare();
}

ttyend()	/* cleanup */
{
   setcooked();
}


gettermvars()
{
   char *tgetstr();
   int  tgetflag();
   areap = area;
   _al = tgetstr("al",&areap);
   _ce = tgetstr("ce",&areap);
   _cl = tgetstr("cl",&areap);
   _cm = tgetstr("cm",&areap);
   _cs = tgetstr("cs",&areap);
   _dl = tgetstr("dl",&areap);
   _do = tgetstr("do",&areap);
   _kb = tgetstr("kb",&areap);
   _kd = tgetstr("kd",&areap);
   _kl = tgetstr("kl",&areap);
   _kr = tgetstr("kr",&areap);
   _ku = tgetstr("ku",&areap);
   _ke = tgetstr("ke",&areap);
   _ks = tgetstr("ks",&areap);
   _nd = tgetstr("nd",&areap);
   _nl = tgetstr("nl",&areap);
   _se = tgetstr("se",&areap);
   _sf = tgetstr("sf",&areap);
   _so = tgetstr("so",&areap);
   _sr = tgetstr("sr",&areap);
   _ta = tgetstr("ta",&areap);
   _te = tgetstr("te",&areap);
   _ti = tgetstr("ti",&areap);
   _up = tgetstr("up",&areap);
   UP = _up;
   ttype_dumb = (_cm == 0 || _cl == 0);
   ttype_scroller = (_cs != 0 || (_al != 0 && _dl != 0));
}   


getch()
{
   char ch;
   read(1,&ch,1);
   return(ch);
}

putch(c)
char c;
{
   write(1,&c,1);
}

putstr(s)
char *s;
{
   char buf[150];
   int l = 0;
   while (*s)
      buf[l++] = (*s++);
   write(1,buf,l);
}


beep()
{
   putch(7);
}

clearscreen()
{
   if (_cl != 0)
      tputs(_cl,1,putch);
}

static char tempstr[150],*tsp;

static addtempc(c)
char c;
{
   *tsp++ = c;
   *tsp = '\0';
}

#define isdigit(c) ('0' <= (c) && (c) <= '9')
static my_tputs(cp)	/* ignores padding..puts stuff in tempstr */
register char *cp;
{
   if (cp == 0)
      return;
   while (isdigit(*cp))
      cp++;
   if (*cp == '.')
      cp++;
   while (isdigit(*cp))
      cp++;
   if (*cp == '*')
      cp++;
   while (*tsp++ = *cp++);
   tsp--;
}


setpos(xpos,ypos)
int xpos,ypos;
{
   char *tgoto();
   tsp  = tempstr;
   *tsp = '\0';
   if (_cm != 0) {
      my_tputs(tgoto(_cm,xpos,ypos));
      putstr(tempstr);
   }
}

cleartoend()
{
   int i;
   if (_ce == 0)
      return -1;
   else {
      tputs(_ce,1,putch);
      return 0;
   }
}


/*
 * The following routine scrolls a range of lines up
 */

scrollup(top,bottom)
int top,bottom;
{
   char *tgoto();

   tsp = tempstr;
   *tsp = '\0';
   if (_cs != 0) {
      my_tputs(tgoto(_cs,bottom,top));
      my_tputs(tgoto(_cm,0,bottom));
      my_tputs("\n");
      my_tputs(tgoto(_cs,23,0));
      putstr(tempstr);
   }
   else {
      my_tputs(tgoto(_cm,0,top));
      my_tputs(_dl);
      my_tputs(tgoto(_cm,0,bottom));
      my_tputs(_al);
      putstr(tempstr);
   }
}

//E*O*F ttyio.c//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      54     145    1338 Makefile
     235     793    5820 cparse.c
     110     501    4569 cparse.h
      78     270    2185 getuser.c
      81     417    2406 phone.1
     117     319    2879 phone.c
      65     296    2150 phone.h
     675    2191   16850 phoned.c
     231     624    5013 phonemain.c
     260     623    6185 phoneparse.c
     286     964    6522 slotmanager.c
     513    1318   11015 ttycmio.c
     308     702    6351 ttydumbio.c
     246     683    5135 ttyio.c
    3259    9846   78418 total
!!!
wc  Makefile cparse.c cparse.h getuser.c phone.1 phone.c phone.h phoned.c phonemain.c phoneparse.c slotmanager.c ttycmio.c ttydumbio.c ttyio.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0

jhhur@kaist.UUCP (Hur, Jinho) (01/14/85)

The phone program has some anomaly.
When user A su'ed on the login by B, phone to C, then the message to C
says that A rings C. But when C tries to ring A by 'phone A', 
'User not logged in' message appears. It is since phone uses 
getpwuid(getuid()) for the local user name. It is to be corrected as follows
to avoid such anomaly.

*** phone.c	Sun Jan 13 22:51:31 1985
--- phone.c.old	Sun Jan 13 22:50:09 1985
***************
*** 26,32
     int  dpid;				/* daemon pid */
     char psockname[80];			/* phone socket name */
     struct passwd *pw;			/* pointer to password structure */
-    struct passwd *getpwnam();
     int nodaemon(),daemonstarted();	/* predeclare for signal	*/
     char *getlogin();			/* to avoid confusion. */
  

--- 26,31 -----
     int  dpid;				/* daemon pid */
     char psockname[80];			/* phone socket name */
     struct passwd *pw;			/* pointer to password structure */
     int nodaemon(),daemonstarted();	/* predeclare for signal	*/
  
     /* Check the number of arguments */
***************
*** 28,34
     struct passwd *pw;			/* pointer to password structure */
     struct passwd *getpwnam();
     int nodaemon(),daemonstarted();	/* predeclare for signal	*/
-    char *getlogin();			/* to avoid confusion. */
  
     /* Check the number of arguments */
     if (argc < 2) {

--- 27,32 -----
     char psockname[80];			/* phone socket name */
     struct passwd *pw;			/* pointer to password structure */
     int nodaemon(),daemonstarted();	/* predeclare for signal	*/
  
     /* Check the number of arguments */
     if (argc < 2) {
***************
*** 53,60
     }
  
     /* Get my user name from the uid and the passwd file */
!    pw = getpwnam(getlogin());
!    /* pw = getpwuid(getuid()); */
     if (pw == 0) {
        printf("Can't get your login name.");
        _exit(1);

--- 51,57 -----
     }
  
     /* Get my user name from the uid and the passwd file */
!    pw = getpwuid(getuid());
     if (pw == 0) {
        printf("Can't get your login name.");
        _exit(1);
--
Hur, Jinho	Dept of Computer Science, KAIST
uucp: hplabs!kaist!jhhur
csnet: jhhur%kaist@csnet-relay.csnet
-- 
real:	Hur, Jinho	Dept of Computer Science, KAIST
uucp:	..!hplabs!kaist!jhhur
csnet:	jhhur%kaist.csnet@csnet.arpa