source to "phone" system (part 2 of 4)

Jonathan C. Broome broome at ucbvax.BERKELEY.EDU
Sun Dec 29 18:40:59 AEST 1985


#-----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	defs.h
#	alias.c
#	calls.c
#	check_invite.c
#	connect_daemon.c
#	cmd.c
#	getdaemon.c
#	kb.c
#	main.c
#	message.c
#	misc.c
#	names.c
#	parse.c
#	readctl.c
#	readrc.c
#	readstream.c
#	runprog.c
#	sendit.c
#	set.c
#	sig.c
#	stop.c
#	strsave.c
#	tilde.c
#	timer.c
#	who.c
#	windows.c
# This archive created: Sat Dec 28 01:11:00 1985
export PATH; PATH=/bin:$PATH
mkdir client
cd client
echo shar: extracting "'Makefile'" '(4691 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'
#
#   Makefile for phone client          20 December 1985
#


# What all this stuff means::
#  SERVICES   - define this is you have an entry for phone in /etc/services
#  PORT       - if SERVICES is not defined, set this to the port number that
#               phone should use (this overrides the default)
#  LOCAL_ECHO - if this is defined, what the user types will be echoed
#               locally, and the remote echo will be ignored. The option
#               is really an old relic, and should always be on.
#
# Also:
#   o  Add "-lresolv" to the library if you want to use the network name
#      server and have the library routines.
#   o  Add "-Dvoid=int" if your compiler doesn't know about the void type.


CC     = cc
CFLAGS = -O -DSERVICES -DLOCAL_ECHO
LIBS   = -lcurses -ltermlib -lresolv

LPR    = lpr -Pvax
RDEST  = /usr/local/phone

HDRS = defs.h

SRCS = alias.c calls.c check_invite.c connect_daemon.c cmd.c \
		getdaemon.c kb.c main.c message.c misc.c names.c \
		parse.c readctl.c readrc.c readstream.c runprog.c \
		sendit.c set.c sig.c stop.c strsave.c tilde.c timer.c \
		who.c windows.c

OBJS = alias.o calls.o check_invite.o connect_daemon.o cmd.o \
		getdaemon.o kb.o main.o message.o misc.o names.o \
		parse.o readctl.o readrc.o readstream.o runprog.o \
		sendit.o set.o sig.o stop.o strsave.o tilde.o timer.o \
		who.o windows.o

DEST = phone


.DEFAULT:
	co $<

all:	${DEST}

${DEST}: ${OBJS}
	/bin/rm -f ${DEST}
	${CC} ${CFLAGS} -o ${DEST} ${OBJS} ${LIBS}

${OBJS}: ${HDRS}

install: ${DEST}
	/bin/rm -f ${RDEST}
	cp ${DEST} ${RDEST}

print:	${HDRS} ${SRCS}
	pr -f ${HDRS} ${SRCS} | expand -4 | ${LPR}

tags:	/dev/null
	ctags -w ${HDRS} ${SRCS}

tar: ${HDRS} ${SRCS}
	tar cf client.tar ${HDRS} ${SRCS} Makefile

shar:	Makefile ${HDRS} ${SRCS}
	shar -v Makefile ${HDRS} ${SRCS} > ../shar.client

clean:
	/bin/rm -f ${OBJS} ${DEST} core

lint: ${HDRS} ${SRCS}
	lint ${CFLAGS} ${SRCS} > lint.out

dist:	${DEST}
	-rcp ${DEST} buddy:${RDEST}
	-rcp ${DEST} franny:${RDEST}
	-rcp ${DEST} holden:${RDEST}
	-rcp ${DEST} seymour:${RDEST}
	-rcp ${DEST} zooey:${RDEST}
	-rcp ${DEST} miro:${RDEST}
	-rcp ${DEST} cory:${RDEST}


depend: ${SRCS}
	mv Makefile makefile.old
	sed '/^# Dependencies follow/,$$d' makefile.old > Makefile
	echo '# Dependencies follow' >> Makefile
	includes -so ${SRCS} >> Makefile
	echo ' ' >> Makefile
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile
	echo '# see depend: above' >> Makefile

# DO NOT DELETE THE FOLLOWING LINE
# Dependencies follow

who.o: /usr/include/sys/file.h

who.o: /usr/include/utmp.h

runprog.o: /usr/include/fcntl.h

runprog.o: /usr/include/sys/resource.h

runprog.o: /usr/include/sys/wait.h

tilde.o names.o: /usr/include/pwd.h

windows.o stop.o runprog.o readstream.o misc.o kb.o: /usr/include/sgtty.h

windows.o stop.o runprog.o readstream.o misc.o kb.o: /usr/include/curses.h

windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \
/usr/include/sys/ttydev.h

windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \
/usr/include/sys/ttychars.h

windows.o windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \
/usr/include/sys/ioctl.h

runprog.o main.o getdaemon.o: /usr/include/errno.h

windows.o strsave.o stop.o sig.o message.o main.o getdaemon.o \
connect_daemon.o: /usr/include/signal.h

sendit.o runprog.o getdaemon.o calls.o: /usr/include/time.h

sendit.o runprog.o getdaemon.o calls.o: /usr/include/sys/time.h

who.o names.o connect_daemon.o calls.o: /usr/include/netdb.h

who.o runprog.o readstream.o readctl.o names.o main.o getdaemon.o \
connect_daemon.o check_invite.o calls.o: ./../common.h

windows.o strsave.o stop.o runprog.o readstream.o readrc.o names.o misc.o \
message.o main.o kb.o connect_daemon.o calls.o: /usr/include/stdio.h

windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \
readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \
check_invite.o calls.o alias.o: /usr/include/netinet/in.h

windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \
readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \
check_invite.o calls.o alias.o: /usr/include/sys/socket.h

windows.o who.o stop.o set.o sendit.o runprog.o runprog.o readstream.o \
readrc.o readctl.o names.o misc.o message.o main.o kb.o getdaemon.o \
connect_daemon.o check_invite.o calls.o alias.o: /usr/include/sys/types.h

windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \
readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \
check_invite.o calls.o alias.o: ./defs.h
 
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see depend: above
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'defs.h'" '(1720 characters)'
if test -f 'defs.h'
then
	echo shar: will not over-write existing file "'defs.h'"
else
cat << \!Funky!Stuff! > 'defs.h'
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BUFFER      512				/* internal character buffer size */

char	*sprintf();
char	*index();
char	*strsave();
char	*malloc();
char	*basename();
char	*expalias();

char	*login;						/* user's login name       */
char	*tty;						/* tty he's on             */
char	*home;						/* home directory          */
char	*shell;						/* preferred shell         */
extern	char	host[];				/* local host name         */
extern	char	realname[];			/* from password file      */
int		maxx;						/* max legal x coordinate  */
int		maxy;						/* max legal y coordinate  */
int		stream;						/* stream socket filedes   */
int		ctl;						/* control socket fd       */
int		reading;					/* currently reading kb?   */
extern	char	buf[];				/* general-use buffer      */
extern	int		pending;			/* number of pending calls */
int		connected;					/* number of connected people */
int		touched25;					/* message changed line 25    */
struct	sockaddr_in locaddr;		/* localhost address   */
extern	char	convaddr[];			/* conversation address    */
int		port;						/* phoned socket port      */

#ifndef	ESC
#define	ESC		'\033'
#endif

#define PROMPT  "Command> "

#define ctrl(C)     ('C'-'@')
#define isdigit(c)  ('0' <= c && c <= '9')
#define	equal(a,b)	(strcmp (a,b) == 0)


/*
 *  Various oft-used flags.
 */

int		Interval;					/* seconds between messages */
int		Inverse;					/* allow use of inverse video */
int		Stop;						/* print "Stopped" to socket on ^Z */
int		Debug;						/* verbose tracing stuff */
int		Bells;						/* let ^G come through as bell */
int		Hold;						/* send input to child process */
int		Echo;						/* send input to socket when running prog */
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'alias.c'" '(1316 characters)'
if test -f 'alias.c'
then
	echo shar: will not over-write existing file "'alias.c'"
else
cat << \!Funky!Stuff! > 'alias.c'
#include "defs.h"


/*
 *  Routines to handle phone aliases, much like mail aliases.
 */

struct alias {
	char	*name;		/* name of alias */
	char	*real;		/* and what it expands to */
	struct	alias *next;
};

static	struct	alias	*aliases = (struct alias *) 0;


/*
 *  Set up an alias.
 */

alias (argc, argv)
int   argc;
char *argv[];
{
	struct	alias *ap;

	if (argc == 0) {		/* just show aliases */
		message ("Showing all aliases:");
		for (ap = aliases; ap; ap = ap->next) {
			sprintf (buf, "%s --> %s", ap->name, ap->real);
			message (buf);
		}
		message ("That's all!");
		return;
	}
	if (argc == 1) {
		putmessage ("alias: arg count must be 0 or 2!");
		return;
	}
	ap = (struct alias *) malloc (sizeof (struct alias));
	if (ap == (struct alias *) 0) {
		error (0, "alias: cannot allocate memory for alias");
		return;
	}
	ap->name = strsave (argv[0]);
	ap->real = strsave (argv[1]);

	ap->next = aliases;
	aliases = ap;
}



/*
 *  Expand an alias.  It copies the expansion into a buffer so 
 *  that the calling routines can munge it to their heart's content.
 */

char *
expalias (name)
char *name;
{
	register struct alias *ap;
	static   char   buf[128];

	for (ap = aliases; ap; ap = ap->next)
		if (strcmp (name, ap->name) == 0) {
			strcpy (buf, ap->real);
			return (buf);
		}
	return ((char *) 0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'calls.c'" '(7403 characters)'
if test -f 'calls.c'
then
	echo shar: will not over-write existing file "'calls.c'"
else
cat << \!Funky!Stuff! > 'calls.c'
#include <stdio.h>
#include "defs.h"
#include "../common.h"
#include <netdb.h>
#include <sys/time.h>


/*  client pending call structure  */
struct	call {
	char	*user;					/* user's name             */
	char	*host;					/* his host                */
	char	*tty;					/* his tty                 */
	struct	sockaddr_in	addr;		/* socket address          */
	char	id[11];					/* used to talk to daemon  */
	int		rings;					/* ticks since last ring   */
	int		status;					/* has daemon acted on it? */
	struct	call *next;				/* next one in the list    */
};

static	struct call	*calls = (struct call *) 0;	/* list of pending calls   */
int		pending = 0;							/* number of pending calls */

/*
**  Place a call.
**
**  Parameters:
**		user -	the login name of the person to call
**		host -	his hostname, in either normal form ('ucbarpa') or 
**				internet dotted quad form ('128.32.149.5')
**		tty  -	a specified tty, if any, other the null string ("")
**
**	What we do:
**		1.	Send a request packet to the phone daemon at his address,
**			packet looks like "<esc> P callno : user : tty : myname : convaddr".
**
**          Callno is a unique request identifier that the daemon uses
**			to guard against that duplicate messages occurring due to
**          the time delay between placing the call and getting the response
**          causing the client to retransmit the request. [Got that?]
**
**			If we are using the master/slave protocol, the convaddr is
**			preceded by either 'y' or 'n' to indicate whether or not we 
**			will be willing to give up our convd and go to his. (This 
**			is used for joining an existing conversation.)
**          [Not used yet ...]
**
**		2.	We expect to get a response back from his phoned of the form
**			"<ESC> P y id" where `id' is a small 5-character request id which
**			is to be used when 'renewing' a page request. 
**
**		3.	Then save all of the info (user, host, tty, message id) in 
**			a 'call' structure which is added to the linked list of pending
**			calls.
**
**		4.	As a side effect, the global variable 'pending' is incremented.
*/

placecall (user, host, tty)
char  *user;
char  *host;
char  *tty;
{
	struct	call    *new;			/* new call structure */
	struct	hostent *hp;			/* host info */
	struct	hostent *gethostbyname();
	struct	sockaddr_in sin;		/* network address */
	static	int		callno = 0;
	extern	int users;
	int		rval;

	if (isdigit (*host))			/* numeric address ??? */
		sin.sin_addr.s_addr = inet_addr (host);
	else {
		if ((hp = gethostbyname (host)) == (struct hostent *) 0) {
			sprintf (buf, "Unknown host: %s", host);
			putmessage (buf);
			return;
		}
		bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
	}
	sin.sin_family = AF_INET;
	sin.sin_port   = port;

	sprintf (buf, "Calling %s@%s", user, host);
	message (buf);

#ifdef	SLAVE
	sprintf (buf, "%c%c%03d:%s:%s:%s:%c%s", ESC, PAGE, callno++, user,
					tty ? tty : "", login, users > 1 ? NAK : ACK,  convaddr); 
#else	!SLAVE
	sprintf (buf, "%c%c%03d:%s:%s:%s:%s", ESC, PAGE, callno++, 
					user, tty ? tty : "", login, convaddr);
#endif	SLAVE

	if ((rval = sendit (buf, strlen (buf), sin)) < 0) {	
		message ("No response to call request.");
		return;
	}

	/*  got a response - text is in buf.  */
	buf[rval] = '\0';
	new = (struct call *) malloc (sizeof (struct call));
	new->user  = strsave (user);
	new->host  = strsave (host);
	new->tty   = strsave (tty);
	new->rings = 1;
	strncpy (new->id, buf+3, 10);	/* save the mesg id # */
	bcopy ((char *)&sin, (char *)&new->addr, sizeof (sin));
	new->next = calls;				/* put list together */
	calls = new;

	pending++;		/* increment the number of pending calls */
	return;
}


/*
**	Go through the list of pending calls and reinvite 
**	all the calls that have expired.  Returns 1 if the timer
**	should be reset, 0 if the timer should expire.
**
**	If SLAVE is defined, we check every call to see they have 
**	tried to call us. If they indicate that they are *not* willing
**	to submit (they are in an existing conversation), then we close
**	our existing convd connection and connect to their convd; else
**	we go over only if our convd address is lower than theirs.
**	(Yes, this looks like a hack, but it seems to work best.)
*/

reinvite ()
{
	register	struct call *ptr;
	extern		int users;

	if (pending == 0)		/* nothing to do */
		return (0);
	
	for (ptr = calls; ptr; ptr = ptr->next) {
		/* 
		 *  See if we've connected to anyone,
		 *  if not then first call our local host and see if the
		 *  intended recipient is trying to call us...
		 */

#ifdef SLAVE

		if (users < 2 && pending < 2) {
			int r;
			if (Debug)
				putmessage ("Checking for reverse calls...");
			sprintf (buf, "%c%c%s:%s", ESC, INQUIRE, login, ptr->user);
			if ((r = sendit (buf, strlen (buf), locaddr)) > 0) {
				buf[r] = '\0';
				if (Debug)
					printf ("Received \"%s\"\r\n", buf+1);
				if (buf[2] == ACK && 
				   (buf[3] == NAK || strcmp (convaddr, buf+4) < 0) &&
				   connect_daemon (buf+4) == 0) {
					putmessage ("Switching to conversation already in progress.");
					delete (ptr->id);
					/* need to reset status so windows work */
					users = 0;
					return;
				}
			}
			putmessage ("");
		}
#endif SLAVE

		if (ptr->rings == 0) {		/* time to reinvite */
			buf[0] = ESC;
			buf[1] = REINVITE;

			strcpy (buf+2, ptr->id);
			if (sendit (buf, strlen (buf), ptr->addr) < 0)
				error (0, "reinvite: sendto");
			/* should check response value!!! */
		}
		ptr->rings = (ptr->rings + 1) % 20;		/* reinvite every 20 secs. */
	}
	return (1);
}


/*
**  Delete the call `id' from the pending list.
*/

delete (id)
char *id;
{
	register	struct call *curr;
	register	struct call *prev = (struct call *) 0;
	register	struct call *next;

	for (curr = calls; curr; curr = next) {
		next = curr->next;
		if (strncmp (id, curr->id, 5) == 0) {	/* this is the one */
			if (prev)
				prev->next = next;
			else
				calls = next;
			free (curr->user);
			free (curr->host);
			free (curr->tty);
			free (curr);
			pending--;
			return;
		}
	}
}



/*
**  Front end routine --- Page a user to join in the conversation.
*/

page (argc, argv)
int   argc;
char *argv[];
{
	char    *hishost;
	char    *tty;
	char	*user;
	char	*index();

	if ((user = expalias (argv[0])) == (char *) 0)
		user = argv[0];

	if (hishost = index (user, '@'))
		*hishost++ = '\0';
	else
		hishost = host;
	tty = (argc == 2) ? argv[1] : (char *) 0;
	
	placecall (user, hishost, tty);
}


/*
**  Cancel a call sent out by simply not bothering to "renew" it.
*/

cancel (argc, argv)
int     argc;
char   *argv[];
{
	char	*user;
	char    *hishost;
	char    *tty;
	struct	call *curr, *prev, *next;

	if ((user = expalias (argv[0])) == (char *) 0)
		user = argv[0];

	tty = (argc == 2) ? argv[1] : (char *) 0;
	if (hishost = index (user, '@'))
		*hishost++ = '\0';
	else
		hishost = host;
	
	prev = (struct call *) 0;
	for (curr = calls; curr; curr = next) {
		next = curr->next;
		if (equal (user, curr->user) && equal (hishost, curr->host)) {
			if (prev)
				prev->next = next;
			else
				calls = next;
			free (curr->user);
			free (curr->host);
			free (curr->tty);
			free (curr);
			pending--;
			sprintf (buf, "Cancelling call to %s@%s",
						user, hishost);
			if (tty) {
				strcat (buf, " on ");
				strcat (buf, tty);
			}
			message (buf);
			return;
		}
	}
	sprintf (buf, "No pending calls to %s@%s", user, hishost);
	message (buf);
}

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'check_invite.c'" '(1106 characters)'
if test -f 'check_invite.c'
then
	echo shar: will not over-write existing file "'check_invite.c'"
else
cat << \!Funky!Stuff! > 'check_invite.c'
#include "../common.h"
#include "defs.h"

/*
 *  Check to see if we have any pending invitations from the named user.
 *  Returns the string containing our given address (or NULL)
 */

/*ARGSUSED*/
char *
check_invite (user, host)
char *user;
char *host;
{
	struct  sockaddr_in sin;	/* address of daemon socket */
	char    mbuf[80];			/* one line of incoming message  */
	int     r, tries;

	/*  socket address "locaddr" is already initialised to the local host */
	bcopy ((char *)&locaddr, (char *)&sin, sizeof (sin));
	sin.sin_port = port;

	sprintf (mbuf, "%c%c%s:%s", ESC, INQUIRE, login, user);

	for (tries = 0; tries < 5; tries++) {
		if ((r = sendit (mbuf, strlen (mbuf), sin)) <= 0) {
			if (Debug)
				printf ("\r\ncheck_invite: sendit returned %d.\r\n", r);
			continue;
		}
		buf[r] = '\0';
		if (Debug)
			printf ("check_invite: returned string is \"%s\"\r\n", buf+1);
		if (buf[2] == NAK)
			return ((char *) 0);
		return (strsave (buf+3));	/* skip over ESC I y */
		printf ("Trying to contact phone daemon.\r\n");
	}
	error (1, "Cannot contact local phone daemon.");
	return ((char *) 0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'connect_daemon.c'" '(2397 characters)'
if test -f 'connect_daemon.c'
then
	echo shar: will not over-write existing file "'connect_daemon.c'"
else
cat << \!Funky!Stuff! > 'connect_daemon.c'
#include "../common.h"
#include "defs.h"

#include <signal.h>
#include <stdio.h>
#include <netdb.h>


/*
 *  Connect to the conversation daemon.
 *  Addrstr is a string containing the intended address
 *  in the form "128.32.149.2/9535".
 */

connect_daemon (addrstr)
char *addrstr;
{
	struct  sockaddr_in addr;
	char	*inet_ntoa();
	char    *p, *index();
	char	str[30];
	int     oldval;
	int     (*oldfunc)();
	int     timeout();

	if (addrstr == (char *) 0 || *addrstr == '\0')
		error (1, "Failed to connect to conversation daemon");

	oldval = alarm (0);		/* turn off alarms and save old value */

#ifdef SLAVE
	if (*addrstr == 'y' || *addrstr == 'n')
		addrstr++;
#endif SLAVE

	strcpy (convaddr, addrstr);		/* save the current conversation address */
	strcpy (str, addrstr);			/* and use 'str' for our working copy */

	if (p = index (str, '/'))		/* find slash before port number */
		*p++ = '\0';

	if ((stream = socket (AF_INET, SOCK_STREAM, 0)) < 0)
		error (1, "Cannot open stream socket");

	bzero ((char *)&addr, sizeof (addr));		/* connect trashes the args */
	addr.sin_addr.s_addr = inet_addr (str);
	addr.sin_port = htons (atoi (p));
	addr.sin_family = AF_INET;

	oldfunc = signal (SIGALRM, timeout);		/* save old handler */
	alarm (15);									/* timeout in five seconds */

	if (Debug) {
		printf ("Trying to connect to conversation daemon.\r\n");
		fflush (stdout);
	}

	if (connect (stream, (char *)&addr, sizeof (struct sockaddr_in)) < 0) {
		alarm (0);
		close (stream);
		connected = 0;
		if (Debug)
			printf ("\r\n\r\nNot connected.\r\n");
	} else {									/* say hello */
		if (Debug) {
			printf ("Connected to conversation daemon @ \"%s\"\r\n", convaddr);
			fflush (stdout);
		}
		sprintf (buf, "%s:%s:%s:%s", login, host, tty, realname);
		if (write (stream, buf, strlen (buf)) < 0)
			error (1, "getdaemon: cannot write stream socket");
		connected = 1;
	}

	/*
	 *  Tell the local phoned to delete our invitaton now
	 *  that we've answered it. Not used yet.
	 */

#ifdef notdef
	sprintf (buf, "%c%c%s:%s", ESC, ANSWER, login, convaddr);
	(void) sendto (ctl, buf, strlen (buf), 0, &locaddr, sizeof (locaddr));
#endif notdef

	(void) alarm (oldval);					/* restore old alarm value */
	(void) signal (SIGALRM, oldfunc);		/* and handler routine */

	return (!connected);
}


timeout ()
{
	putmessage ("cannot connect to conversation daemon: connection timed out.");
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'cmd.c'" '(3466 characters)'
if test -f 'cmd.c'
then
	echo shar: will not over-write existing file "'cmd.c'"
else
cat << \!Funky!Stuff! > 'cmd.c'
/*
 *  All the (external) command functions.
 */

extern int
	alias(), cancel(), cd(), dump(), help(), page(), quit(), 
	run(), set(), show(), longhelp(), who(), zip();

static char aliashelp[]  = "Assign an alias to a user name";
static char cancelhelp[] = "Cancel a pending call to user";
static char cdhelp[]     = "Change the working directory";
static char	dumphelp[]   = "Save a copy of the screen image into a file";
static char helphelp[]   = "See help messages for any command";
static char pagehelp[]   = "Invite user to join the conversation";
static char quithelp[]   = "Leave the current conversation";
static char runhelp[]    = "Run a program within the window";
static char sethelp[]    = "Set the value of a variable";
static char showhelp[]   = "See the value of a variable";
static char whohelp[]    = "See who is currently logged on";

static struct cmd {
	char	*name;			/* name this command goes by */
	int		(*func)();		/* function to handle it     */
	int		minargs;		/* minimum number of args    */
	int		maxargs;		/* maximum number of args    */
	char	*usage;			/* usage message text        */
	char	*help;			/* help message text         */
} cmds [] = {
	{ "call",	page,	2,	3,	"user at host [tty]",		pagehelp	},
	{ "page",	page,	2,	3,	"user at host [tty]",		pagehelp	},
	{ "phone",	page,	2,	3,	"user at host [tty]",		pagehelp	},
	{ "cancel",	cancel,	2,	3,	"user at host [tty]",		cancelhelp	},
	{ "quit",	quit,	1,	1,	"",						quithelp	},
	{ "who",	who,	1,	2,	"[user]",				whohelp		},
	{ "?",		help,	1,	2,	"[command]",			helphelp	},
	{ "help",	help,	1,	2,	"[command]",			helphelp	},
	{ "set",	set,	2,	3,	"variable [value]",		sethelp		},
	{ "show",	show,	1,	98,	"[var [var]]",			showhelp	},
	{ "!",		run,	2,	98,	"prog [args]",			runhelp		},
	{ "run",	run,	2,	98,	"prog [args]",			runhelp		},
	{ "dump",	dump,	1,	2,	"[file]",				dumphelp	},
	{ "cd",		cd,		1,	2,	"[directory]",			cdhelp		},
	{ "alias",	alias,	1,	3,	"[alias user]",			aliashelp	},
	{ "accept",	zip,	1,	99,	"",						""			},
	{ "forward",zip,	1,	99,	"",						""			},
	{ 0,		0,		0,	0,	0,						0			}
};


/*
 *  Given a buffer, parse it and execute the command.
 */

execute (buf)
char    *buf;
{
	int		len;
	int		argc;
	char	*argv[99];
	char	line[80];
	struct	cmd *cmd;

	if ((argc = parse (buf, argv)) == 0)   /* null command */
		return;
	
	len = strlen (argv[0]);

	for (cmd = cmds; cmd->name; cmd++) {
		if (strncmp (argv[0], cmd->name, len))  /* is it a prefix? */
			continue;
		if (argc < cmd->minargs || argc > cmd->maxargs) {
			sprintf (line, "%s: bad number of arguments - usage: %s %s",
						cmd->name, cmd->name, cmd->usage);
			putmessage (line);
		} else 
			(*cmd->func)(argc-1, argv+1);
		return;
	}
	sprintf (line, "Unknown command: \"%s\" - try \"?\" for list of commands",
				argv[0]);
	putmessage (line);
}


/*
 *  Print a help message for the given command, or all commands.
 */

help (argc, argv)
int   argc;
char *argv[];
{
	struct cmd *cmd;
	char   buf[80];
	int    all = (argc == 0);
	int    len;

	if (all)
		message ("Showing help for all commands. Commands may be abbreviated.");
	else
		len = strlen (argv[0]);
	
	for (cmd = cmds; cmd->name; cmd++)
		if (all || strncmp (argv[0], cmd->name, len) == 0) {
			if (!all || strcmp (cmd->help, (cmd+1)->help)) {
				sprintf (buf, "help: %s: %s.", cmd->name, cmd->help);
				message (buf);
			}
		}
}


/*
 *  Do nothing. Used for commands in .phonerc that have 
 *  no meaning to the client process.
 */

zip ()
{
	;
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'getdaemon.c'" '(972 characters)'
if test -f 'getdaemon.c'
then
	echo shar: will not over-write existing file "'getdaemon.c'"
else
cat << \!Funky!Stuff! > 'getdaemon.c'
#include "../common.h"
#include "defs.h"
#include <sys/time.h>
#include <signal.h>
#include <errno.h>

/*
 *  Ask the master daemon to give us its first-born child ...
 *  Returns the string that it gave us ...
 */

char *
getdaemon ()
{
	int    r;					/* number of chars read */
	char   mbuf[80];

	if (connected) {
		error (0, "getdaemon(): already connected!");
		return;
	}
	connected = 0;

	if (Debug)
		message ("Asking for a conversation daemon ...");

	mbuf[0] = ESC;
	mbuf[1] = DAEMON;

	for ( ;; ) {
		if ((r = sendit (mbuf, 2, locaddr)) < 0) {
			error (0, "could not get conversation daemon from local daemon");
			return ((char *) 0);
		}
		buf[r] = '\0';
		if (buf[2] == NAK) {
			sprintf (mbuf, "Daemon failed to fork: %s", buf+3);
			putmessage (mbuf);
		} else
			break;
		putmessage ("Trying to connect to conversation daemon.");
	}

	if (Debug)
		message (sprintf (mbuf, "Daemon address is %s", buf+3));

	return (strsave (buf+3));	/* skip ESC D y */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'kb.c'" '(4489 characters)'
if test -f 'kb.c'
then
	echo shar: will not over-write existing file "'kb.c'"
else
cat << \!Funky!Stuff! > 'kb.c'
#include <sys/ioctl.h>
#include <curses.h>
#undef   ctrl()

#include "defs.h"

/*
 *  Handle keyboard input.
 *
 *  Need to handle edit character mapping --- see if char is one
 *  of his edit characters, swap to standard character.
 */

static struct key {
	int		his;		/* his key */
	int		std;		/* what we'll map it to */
} keys [] = {
	{ ctrl(M),	ctrl(J) },		/* <cr> -> newline */
	{ 0, 		ctrl(H) },		/* backspace */
	{ 0, 		ctrl(W) },		/* word erase */
	{ 0, 		ctrl(X) },		/* line kill */
	{ 0, 		0       }
};


keyboard ()
{
	static   char   c;              /* character read in    */
	register int 	k;				/* for doing key maps   */
	static   int    command = 0;	/* on bottom line?      */
	static   int    cpos = 0;		/* position in buffer   */
	static   char   cbuf[80];		/* and command buffer   */
	extern   int	childpid, tochild;
	static	 int y, x;

	if (read (0, &c, 1) != 1)
		return;
	
	/*
	 *  We remap the basic editing characters here to make life easier later.
	 */

	for (k = 0; k < 4; k++) {
		if (keys[k].his == c) {
			c = keys[k].std;
			break;
		}
	}

	if (c == ctrl(L) || c == ctrl(R)) {
		wrefresh (curscr);
		return;
	}

	if (c == ESC) {				/* toggle command mode */
		getyx (stdscr, y, x);	/* save the current cursor position */
		move (maxy, 0);			/* jump to bottom line */
		clrtoeol ();
		command = !command;		/* toggle mode */
		if (command)			/* entering command mode  */
			addstr (PROMPT);
		cpos = 0;				/* and reset buffer index */
		move (y, x);			/* restore cursor position */
		refresh ();
		return;
	} 

	if (command == 0) {						/* not in command mode */
		if ((c == ctrl(D)) && childpid) 	/* send eof to child */
			close (tochild);
		if (childpid == 0 || Echo) {		/* send char to the socket */
#ifdef LOCAL_ECHO
			int old = selwin (0);
			showch (c);
			selwin (old);
			refresh ();
#endif LOCAL_ECHO
			(void) write (stream, &c, 1);
		}
		if (childpid && !Hold)				/* and to child process */
			(void) write (tochild, &c, 1);
		return;
	} else {

		/* 
		 *    The rest is command-mode processing where 
		 *    we fiddle with the command buffer. This 
		 *    should move to a separate routine somewhere.
		 */

		getyx (stdscr, y, x);		/* save the current cursor position */
		if (c == ctrl(J)) {			/* execute the buffer */
			cbuf[cpos] = '\0';		/* null-terminate it  */
			move (maxy, 0);			/* go to beginning of line */
			clrtoeol ();			/* clear line to end  */
			refresh ();				/* update the screen  */
			if (cpos)
				execute (cbuf);		/* and execute it !!! */
			command = cpos = 0;		/* turn off command mode */
			move (y, x);			/* restore cursor position */
			refresh ();
			return;					/* and we're done */
		}

		/*
		 *  The message() routine modified the bottom line,
		 *  so reprint whatever should be down there.
		 */

		if (touched25) {
			move (maxy, 0);			/* go to beginning of line */
			addstr (PROMPT);		/* print prompt */
			cbuf[cpos] = '\0';
			addstr (cbuf);			/* and print buffer */
			clrtoeol ();
			touched25 = 0; 
		}

		move (maxy, strlen (PROMPT) + cpos);

		/*
		 *  If we're here, then we want to add the character to
		 *  the pending command buffer.
		 */

		switch (c) {
			case ctrl(H):	/* backspace */
							if (cpos) {
								cpos--;
								addstr ("\b \b");
							}
							break;

			case ctrl(W):	/* word erase */
							while (--cpos >= 0) {
								if (cbuf[cpos] != ' ') {
									cpos++;
									break;
								}
								addstr ("\b \b");
							}
							while (--cpos >= 0) {
								if (cbuf[cpos] == ' ') {
									cpos++;
									break;
								}
								addstr ("\b \b");
							}
							break;

			case ctrl(U):	/* line kill */
			case ctrl(X):	move (maxy, strlen (PROMPT));
							clrtoeol ();
							cpos = 0;
							break;

			case ctrl(I):	/* tab */
							c = ' ';     /* fall through ... */
			default:		cbuf[cpos++] = c;
							addch (c);
							break;
		}
		cbuf[cpos] = '\0';
		move (y, x);			/* restore cursor position */
		refresh ();
		return;
	}
}


/*
 *   Get our editing characters. Called by setup().
 */

geteditchars ()
{
	struct sgttyb  chars;		/* standard char- and line-erase */
#ifdef TIOCGLTC
	struct ltchars lchars;		/* local - word-erase */
#endif

	if (ioctl (0, TIOCGETP, &chars) < 0) {
		error (0, "ioctl (TIOCGETP) failed");
		return;
	}
	keys[1].his = chars.sg_erase;
	keys[3].his = chars.sg_kill;

#ifdef TIOCGLTC
	if (ioctl (0, TIOCGLTC, &lchars) < 0) {
		error (0, "ioctl (TIOCGLTC) failed");
		return;
	}
	keys[2].his = lchars.t_werasc;
#endif
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'main.c'" '(4570 characters)'
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
cat << \!Funky!Stuff! > 'main.c'
#include "../common.h"
#include "defs.h"
#include <stdio.h>
#include <signal.h>
#include <errno.h>

#define	toggle(var)	var = !var

struct	sockaddr_in	locaddr;	/* our own host address */
char	buf[BUFFER];			/* general purpose character buffer */
char	convaddr[25];			/* address of our conversation in ascii */
char	host[32];				/* local host name */
char	realname[100];			/* user's real name from password file */
extern	int users;				/* number of users connected - from windows.c */
int		pending;				/* number of pending calls */
int		changed_size;			/* caught SIGWINCH, need to resize windows */

main (argc, argv)
int   argc;
char *argv[];
{
	int		answer = 0;
	int		smask;				/* saved socket select mask */
	extern	int errno;
	extern	int timer();
	extern	int quit();
	extern	int badsig();
	extern	int sigint();
	extern	int sigquit();
	extern	int sigstop();
	extern	int sigwinch();
	extern	int sigchld();
	char	*check_invite();
	char	*getdaemon();
	char	*index();
	char	*user;				/* name of user we are trying to call */
	char	*hishost;			/* and his hostname */
	char	*addr;				/* address of conversation from getdaemon () */
	struct	sockaddr_in sin;

	argv++, argc--;

	while (*argv && **argv == '-') {
		while (*++*argv) {
			switch (**argv) {
				case 'a':	if (argc > 1) {		/* given an address */
								addr = *++argv;
								argc--; 
								printf ("Using address \"%s\"\n", addr);
							}
							break;
				case 'b':	toggle (Bells);		break;
				case 'd':	toggle(Debug);		break;
				case 'i':	toggle(Inverse);	break;
			}
		}
		argv++, argc--;
	}

	if (argc == 0)		/* just answer incoming calls */
		answer = 1;
	else if (argc > 2) {
		fprintf (stderr, "Usage: %s [-{bdi}] user at host [ tty ]\n", *argv);
		exit (1);
	}

	if (names ())		/* get all the various info */
		exit (1);
	readrc ();			/* read in and act on the .phonerc */

	if (!answer) {		/* expand any user aliases and see who we're calling */
		if ((user = expalias (argv[0])) == (char *) 0)
			user = argv[0];
		if (hishost = index (user, '@'))
			*hishost++ = '\0';
		else
			hishost = host;
	}

	connected = 0;
	pending = 0;

	/*  set up all the signals  */
	signal (SIGPIPE,  SIG_IGN);
	if (!Debug) {
		signal (SIGHUP,   badsig);
		signal (SIGBUS,   badsig);
		signal (SIGSEGV,  badsig);
	}
	signal (SIGALRM,  timer);
	signal (SIGQUIT,  sigquit);
	signal (SIGINT,   sigint);
#ifdef SIGWINCH
	signal (SIGWINCH, sigwinch);
#endif 
	signal (SIGCHLD,  sigchld);

	/*  
	 *	open datagram control socket.
	 */

	if ((ctl = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
		error (1, "Can't get control socket");

	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	sin.sin_family = AF_INET;

	if (bind (ctl, &sin, sizeof (sin)) < 0)
		error (1, "Can't bind control socket");

	setup ();		/* initialize all the screen stuff */

	/*  Have we been invited ???  */
	if (addr || (addr = check_invite (answer ? "*" : user, hishost))) {
		if (Debug)
			printf ("\r\nCheck_invite returned \"%s\"\r\n", addr);
		connect_daemon (addr);
		if (!connected) {
			if (Debug)
				printf ("Connection failed.\r\n");
			goto again;		/* caller has hung up !!! */
		}
	} else {
again:
		if (Debug)
			printf ("\r\nCheck_invite returned no address.\r\n");
		if (answer) {	/* don't want to place a call */
			fprintf (stderr, "\r\nNo pending calls for %s\r\n", login);
			cleanup ();
			exit (1);
		}
		do {
			putmessage ("");
			if ((addr = getdaemon()) == (char *) 0)
				error (1, "Cannot get conversation daemon address");
			if (Debug)
				printf ("\rGet_daemon returned address of \"%s\"\r\n", addr);
			connect_daemon (addr);
			if (!connected) {
				putmessage ("Trying to connect to conversation daemon.");
				printf ("Trying to connect to conversation daemon.");
				fflush (stdout);
			}
		} while (!connected);
		placecall (user, hishost, argv[1] ? argv[1] : "");
	}

	smask = (1 << 0) | (1 << ctl) | (1 << stream); 
	alarm (1);		/* get things rolling */

	/*
	 *  Main loop - select on sockets and handle appropriately.
	 */

	for ( ;; ) {
		static int mask;
		extern int childpid, fromchild;
#ifdef SIGWINCH
		if (changed_size) {		/* caught SIGWINCH - have to resize windows */
			stretch (1);
			changed_size = 0;
		}
#endif
		mask = smask;
		if (childpid)
			mask |= 1 << fromchild;
		if (select (32, &mask, 0, 0, 0) <= 0) {
			if (errno == EINTR)
				continue;
			error (1, "select");
		}
		errno = 0;
		if (mask & (1 << 0))
			keyboard ();
		if (mask & (1 << stream))
			readstream ();
		if (mask & (1 << ctl))
			readctl ();
		if (childpid && (mask & (1 << fromchild)))
			readchild ();
	}
	/*NOTREACHED*/
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'message.c'" '(2503 characters)'
if test -f 'message.c'
then
	echo shar: will not over-write existing file "'message.c'"
else
cat << \!Funky!Stuff! > 'message.c'
#include "defs.h"
#include <stdio.h>
#include <signal.h>

/*
 *  Routines for dealing with the messages printed 
 *  down on the bottom line of the screen.
 */

struct mesg {
	char   *text;          /* text of this message */
	struct mesg	*next;     /* link to next message */
};

static struct mesg *mesghead = (struct mesg *) 0;  /* top of queue     */
static struct mesg *last     = (struct mesg *) 0;  /* bottom of queue  */
static struct mesg *freelist = (struct mesg *) 0;  /* stuff to free up */
int    msgpending  = 0;          		/* number of pending messages  */


/*
 *  Add a message to the pending message queue.
 */

message (str)
char    *str;
{
	struct mesg *new;
	int    sigs;

	sigs = sigblock (1 << SIGALRM);

	if (!freelist) {		/* need to malloc some up */
		new = (struct mesg *) malloc (sizeof (struct mesg));
		if (!new)
			error (1, "cannot malloc message buffer!");
	} else {				/* take off the top of the list */
		new = freelist;
		freelist = freelist->next;
		free (new->text);
	}
	new->text = strsave (str);
	new->next = (struct mesg *) 0;

	if (last)       				/* add to end of currently pending list */
		last->next = new;
	if (!mesghead)  				/* empty queue - add to top */
		mesghead = new;
	last = new;     				/* update bottom pointer */
	if (msgpending <= 0) { 			/* need to restart alarms */
		alarm (1);
		msgpending = 1;
	} else
		msgpending++;
	(void) sigsetmask (sigs);
}


/*
 *  Called by the alarm interrupt routine - print the 
 *  next pending message and remove it from the queue.
 */

showmessage ()
{
	static int ticks = 0;
	struct mesg *m;

	if ((ticks = (ticks + 1) % Interval) != 0)
		return;

	if (msgpending == 0) {     /* just clean up after last message */
		putmessage ("");
		msgpending = -1;
		return;
	}

	m = mesghead;           /* save pointer to this message */

	putmessage (m->text);
	mesghead = m->next;     /* move down top of queue */
	if (last == m)          /* was the last item, too */
		last = (struct mesg *) 0;

	m->next = freelist;		/* add this item to the free list */
	freelist = m;			/* this way we don't free in an interrupt !!! */
	msgpending--;		    /* and decrement counter  */
}



/*
 *  Flush the pending message buffer.  Not actually used yet.
 */

flush ()
{
	last->next = freelist;				/* add curr freelist to end of list */
	last = (struct mesg *) 0;
	freelist = mesghead;				/* move pending list to free list */
	mesghead = (struct mesg *) 0;		/* zap the head */
	msgpending = 0;						/* and reset the counter */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'misc.c'" '(2640 characters)'
if test -f 'misc.c'
then
	echo shar: will not over-write existing file "'misc.c'"
else
cat << \!Funky!Stuff! > 'misc.c'
#include "defs.h"
#include <curses.h>
#undef   echo()
#undef   noecho()


/*
 *  See if char `c' is contained in string `s'. Returns 1 if true, 0 if not.
 */

any (c, s)
char c;
char *s;
{
	while (*s)
		if (c == *s++)
			return (1);
	return (0);
}


/*
 *  Send an error message to the user.  Much like perror().
 *  If fatal is non-zero, we will terminate with that status.
 */

error (fatal, mesg)
int    fatal;
char  *mesg;
{
	extern int   errno;
	extern char  *sys_errlist[];
	extern int   sys_nerr;
	char   buf[80];

	if (errno) {
		if (errno < sys_nerr) 
			sprintf (buf, "%s: %s", mesg, sys_errlist[errno]);
		else
			sprintf (buf, "%s: Error %d", mesg, errno);
	} else
		strcpy (buf, mesg);

	if (connected)	/* and have set up curses */
		putmessage (buf);
	else
		fprintf (stderr, "%s\r\n", buf);

	if (fatal) {
		cleanup ();
		printf ("\n\n");
		exit (fatal);
	}
}

/*
 *  User interface to the screen dump routine.
 */

dump (argc, argv)
int		argc;
char	*argv[];
{
	dodump (argc ? *argv : ".dump");
}


/*
 *  Do a screen dump to the named file.
 */

dodump (name)
char *name;
{
	FILE *fp;
	int  y, x;
	char buf[80];
	extern int connected;

	if (!connected)			/* take it as a normal interrupt */
		quit ();

	if ((fp = fopen (name, "w")) == (FILE *) 0) {
		(void) sprintf (buf, "Cannot create \"%s\" file", name);
		error (0, buf);
		return;
	}
	for (y = 0; y <= maxy; y++) {
		for (x = 0; x < maxx; x++) 
			(void) putc (stdscr->_y[y][x] & 0177, fp);
		(void) putc ('\n', fp);
	}
	(void) fclose (fp);
	(void) sprintf (buf, "Screen dump to \"%s\" complete.", name);
	putmessage (buf);
}



/*
 *  Move to the bottom line and exit.
 */

quit ()
{
	extern int did_screen;
	extern int childpid;

	if (did_screen)
		cleanup ();
	if (connected) 
		printf ("\nConnection closed. Exiting ...");
	printf ("\n");

	if (childpid)	/* clean up rather forcefully */
		kill (childpid, 9);

	exit (0);
}


/*
 *  Catch SIGWINCH and come here - just set a flag and return.
 */

sigwinch ()
{
	extern	int changed_size;
	changed_size = 1;
}



/*
 *   Chdir to the named directory, to $HOME if no args.
 */

cd (argc, argv)
int   argc;
char *argv[];
{
	char	*getenv();
	char	*dir;

	if (argc == 0) {		/* try to go home */
		if ((dir = getenv ("HOME")) == (char *) 0) {
			putmessage ("No HOME set!");
			return;
		}
	} else
		dir = argv[0];
	
	if (chdir (dir)) {
		sprintf (buf, "Cannot chdir to \"%s\"", dir);
		error (0, buf);
	}
}


/*
 *  Given a full pathname, return a pointer to the last part of the path.
 */

char *
basename (path)
char *path;
{
	char	*rindex();
	char	*p;

	if (p = rindex (path, '/'))
		return (p+1);
	return (path);
}

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'names.c'" '(2359 characters)'
if test -f 'names.c'
then
	echo shar: will not over-write existing file "'names.c'"
else
cat << \!Funky!Stuff! > 'names.c'
#include "../common.h"
#include "defs.h"
#include <netdb.h>
#include <stdio.h>
#include <pwd.h>

#undef	NULL
#define	NULL ((char *) 0)

/*
 *  Initialize all the various names and such that we all want to know.
 */

names ()
{
	struct	passwd	*pw, *getpwnam();
	struct	hostent	*hp, *gethostbyname();
#ifdef SERVICES
	struct	servent *sp, *getservbyname();
#endif
	extern	char	*getlogin();
	extern	char	*ttyname();
	extern	char	*getenv();
	extern	char	realname[];
	char	*r, *p, *n;

	if ((pw = getpwuid (getuid ())) == (struct passwd *) 0) {
		fprintf (stderr, "Who are you?\n");
		return (1);
	}

	if ((n = getlogin ()) == NULL)		/* in a window? */
		login = strsave (pw->pw_name);	/* use our acct name */
	else
		login = strsave (n);

	if ((home = getenv ("HOME")) == NULL)
		home = pw->pw_dir;

	if ((shell = getenv ("SHELL")) == NULL)
		shell = "/bin/sh";

	if (p = getenv ("NAME"))
		strcpy (realname, p);
	else {
		for (p=pw->pw_gecos, r=realname; p && *p && *p!=',' && *p!=';'; p++) {
			if (*p == '&') {		/* copy in from login name */
				n = pw->pw_name;	/* and grab the login name */
				if ('a' <= *n && *n <= 'z')	/* capitalize the first character */
					*n += 'A' - 'a';
				while (*n)
					*r++ = *n++;
			} else
				*r++ = *p;
		}
		*r = '\0';
	}

	if ((tty = ttyname (0)) == NULL || !isatty (1)) {
		fprintf (stderr, "Input must be a terminal, not a file or pipe.\n");
		return (1);
	}
	tty = strsave (tty);
	if (strncmp (tty, "/dev/", 5) == 0)
		tty += 5;	/* skip over "/dev/" */
 
	/* find out about our host */
	gethostname (host, 32);
	if ((hp = gethostbyname ("localhost")) == (struct hostent *) 0)
		if ((hp = gethostbyname (host)) == (struct hostent *) 0) {
			fprintf (stderr, "Cannot find network entry for %s\n", host);
			return (1);
		}
	bzero ((char *)&locaddr, sizeof (locaddr));
	bcopy ((char *)hp->h_addr, (char *)&locaddr.sin_addr, hp->h_length);
	locaddr.sin_family = hp->h_addrtype;

#ifdef SERVICES
	if ((sp = getservbyname ("phone", "udp")) == (struct servent *) 0) {
		fprintf (stderr, "This machine doesn't support \"phone\"\r\n");
		return (1);
	}
	locaddr.sin_port = port = sp->s_port;
#else  SERVICES
#ifdef PORT
	locaddr.sin_port = port = htons (PORT);
#else /* PORT */
	fprintf (stderr, 
		"Your site administrator screwed up installing this program!!!\r\n");
	return (1);
#endif PORT
#endif SERVICES

	return (0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'parse.c'" '(1159 characters)'
if test -f 'parse.c'
then
	echo shar: will not over-write existing file "'parse.c'"
else
cat << \!Funky!Stuff! > 'parse.c'
/*
 *  Take a line buffer and turn into an array of words,
 *  doing the right thing with quotes.
 */

parse (line, argv)
char  *line;
char  **argv;
{
	char *lp;
	char *wp;
	char **ap;
	char word[128];
	char quotec = '\0';
	char *exptilde ();

	ap = argv;
	lp = line;

	while (*lp) {
		while (any (*lp, " \t"))     /* skip over leading space */
			lp++;

		quotec = '\0';
		wp = word;

		while (*lp) {    /* get one word */
			if (*lp == '!') {	/* hack to allow !cmd */
				*wp++ = *lp++;
				break;
			}
			if (*lp == '\\') {      /* literal next character */
				*wp++ = *++lp;
				lp++;
				continue;
			}
			if (any (*lp, " \t") && !quotec)    /* wsp not in a quoted string */
				break;
			if (any (*lp, "\"'")) {       /* quote characters */
				if (!quotec) {            /* this must be open quote */
					quotec = *lp++;
					continue;
				} /* else */
				if (*lp == quotec) {      /* matches opening -> close quote */
					quotec = '\0';
					lp++;
					continue;
				}
			}
			*wp++ = *lp++;
		}
		*wp = '\0';
		if (wp > word)
			*ap++ = exptilde (word);
	}
	*ap = (char *) 0;
	if (quotec)
		putmessage ("Unmatched quote.");
	return (ap - argv);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'readctl.c'" '(1238 characters)'
if test -f 'readctl.c'
then
	echo shar: will not over-write existing file "'readctl.c'"
else
cat << \!Funky!Stuff! > 'readctl.c'
#include "defs.h"
#include "../common.h"

/*
 *  Read a message on the control socket and act upon it.
 */

readctl ()
{
	int		ack;			/* = 1 if this is a good message */
	int		r;				/* number of chars read */
	char	buf[512];		/* input buffer */
	int		len;			/* length of return address */
	struct	sockaddr_in from;

	len = sizeof (from);
	if ((r = recvfrom (ctl, buf, 512, 0, &from, &len)) == 0) {
		error (0, "read: control socket");
		return;
	}

	if (*buf != ESC) {		/* bad control packet */
		error (0, "malformed control packet");
		return;
	}

	buf[r] = '\0';

	ack = buf[2] == ACK;

	switch (buf[1]) {	/* command character */
		case CALLING:	if (!ack)			/* can't call a user */
							delete (buf+3);
						putmessage (buf+8);
						break;

		case PAGE:		if (!ack) {			/* couldn't place call */
							pending--;
							putmessage (buf+3);
						}
						break;

		case INQUIRE:	break;				/* don't do anything */

		case DAEMON:	break;				/* and again */

		case MESSAGE:	message (buf+3);	/* show message */
						break;

		case ANSWER:	delete (buf+3);		/* someone answered a call */
						if (Debug) { 
							char mbuf[80];
							sprintf (mbuf, "Answer on id >>%s<<", buf+3); 
							putmessage (mbuf);
						}
						break;
	}
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'readrc.c'" '(519 characters)'
if test -f 'readrc.c'
then
	echo shar: will not over-write existing file "'readrc.c'"
else
cat << \!Funky!Stuff! > 'readrc.c'
#include "defs.h"
#include <stdio.h>

#ifndef PHONERC
#define PHONERC	"/.phonerc"
#endif


/*
 *  Open the .phonerc and do the stuff in it.
 */

readrc ()
{
	char	line[BUFSIZ];
	char	*i, *index();
	FILE	*rc;

	strcpy (buf, home);
	strcat (buf, PHONERC);

	if ((rc = fopen (buf, "r")) == (FILE *) 0)
		return;
	
	while (fgets (line, BUFSIZ, rc)) {
		if (Debug)
			printf ("Rc: %s", line);
		if (*line == '#' || *line == '\n')
			continue;
		if (i = index (line, '\n'))
			*i = '\0';
		execute (line);
	}
	fclose (rc);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'readstream.c'" '(2629 characters)'
if test -f 'readstream.c'
then
	echo shar: will not over-write existing file "'readstream.c'"
else
cat << \!Funky!Stuff! > 'readstream.c'
#include "../common.h"
#include "defs.h"
#include <curses.h>

/*
 *  Read and process some characters from the stream socket.
 *
 *  Commands are passed in one byte by setting the high bit
 *  with info encoded in the following manner:
 *
 *       +---+-------+-------------------+
 *  Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 *       +---+-------+-------------------+
 *       | 1 |  cmd  |   window number   |
 *       +---+-------+-------------------+
 *
 *  For the "set update" command, only the low bit of the window 
 *  number is used, to indicate whether updating should be on or off.
 */

#define	WINMASK		((1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0)) /*037*/					/* lower 5 bits (0-4) set */
#define	CMDMASK		((1<<6)|(1<<5))		/* only bits 5 and 6 set */


readstream ()
{
	static		int   adding = 0;		/* reading an "adduser" string? */
	static		char  combuf[512];		/* command mode buffer */
	char  		buf[512];				/* normal input buffer */
	register	char  *bufptr;			/* pointer into normal buffer */
	static		char  *comptr;			/* pointer into command mode buffer */
	register	int   i, r;
	register	int   c;
	static		int   addslot = 0;		/* slot for adduser() */
	static		int   dorefresh = 0;
	static		int   noupdate = 0;		/* don't refresh screen until told to */

	if ((r = read (stream, buf, 512)) == 0) {	/* daemon hung up */
		quit ();
		connected = 0;
		return;
	}

	bufptr = buf;
	for (i = 0; i < r; i++) {		/* go through each character */
		c = *bufptr++;
		if (c & META) {				/* a command byte */
			switch (c & CMDMASK) {
				case ADDUSER:		/* add a new user */
							comptr = combuf;
							adding = 1;
							addslot = c & WINMASK;
							break;
				case DELUSER:		/* delete an existing user */
							deluser (c & WINMASK);
							break;		
				case UPDATE:		/* set screen updating */
							if (c & 01)		/* turn updating on */
								noupdate = 0;
							else			/* turn it off */
								noupdate = 1;
							break;
				default:			
							if (adding) {	/* end of adduser cmd */
								*comptr = '\0';
								adduser (addslot, combuf);
								adding = 0;
								dorefresh = 1;
							} else			/* select current window */
								selwin (c & WINMASK);
							break;		
			}
		} else {						/* normal character */
			if (adding)					/* add to adduser buffer */
				*comptr++ = c;
			else {						/* add to display */
#ifdef LOCAL_ECHO
				extern int current;			/* defined in windows.c */
				if (current != 0 || !Echo){	/* only show if it's not ours */
#endif
					showch (c);
					dorefresh = 1;
#ifdef LOCAL_ECHO
				}
#endif
			}
		}
	}
	if (dorefresh && noupdate == 0) {
		refresh ();
		dorefresh = 0;
	}
	return;
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'runprog.c'" '(3260 characters)'
if test -f 'runprog.c'
then
	echo shar: will not over-write existing file "'runprog.c'"
else
cat << \!Funky!Stuff! > 'runprog.c'
#include "../common.h"
#include "defs.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>			/* these two are for wait3() */
#include <sys/resource.h>
#include <fcntl.h>
#include <errno.h>
#include <curses.h>

/*
 *  Routines to handle subprocesses inside windows.
 *
 *  Future plans include putting children on ptys,
 *  allowing multiple processes, some kind of simple
 *  process control, setting window size (ioctl) and 
 *  termcap to properly reflect the window size, and 
 *  being able to run screen-oriented programs within
 *  the phone window.  Maybe sometime ...
 */


int	childpid = 0;		/* current child process's pid */
int tochild;			/* fd to write to child */
int fromchild;			/* fd to read from child */
int	killedchild;		/* flag set on intr - flush output */ 


/*
 *   Run a program. We use the user's shell to allow piping and wildcards.
 */

run (argc, argv)
int      argc;
char    *argv[];
{
	char  args[512];
	int   in[2], out[2];
	int   i;

	if (childpid) {
		putmessage ("You are already running a program!!!");
		return;
	}

	/* concatenate all the arguments to pass to a shell */
	for (*args = '\0', i = 0; i < argc; ) {
		strcat (args, argv[i]);
		if (++i < argc)
			strcat (args, " ");
	}

	if (pipe (in) < 0 || pipe (out) < 0) {
		error (0, "Cannot pipe to child process");
		return;
	}
	killedchild = 0;

	switch (childpid = fork ()) {
		case -1:	error (0, "fork");	break;

		case  0:	(void) dup2 (in[0], 0);	/* child's stdin */
					(void) dup2 (out[1], 1);	/* stdout */
					(void) dup2 (out[1], 2);	/* stderr */
					(void) close (in[0]); (void) close (out[1]);
					execl (shell, basename (shell), "-c", args, (char *) 0);
					perror (argv[0]);
					exit (1);

		default:	(void) close (in[0]);
					(void) close (out[1]);
					(void) fcntl (in[1],  F_SETFL, FNDELAY | FASYNC);
					(void) fcntl (out[0], F_SETFL, FNDELAY | FASYNC);
					tochild = in[1];	/* write to child here */
					fromchild = out[0];	/* read from child here */
					return;
	}
}


/*
 *   Read some output from the process.  We make sure that there
 *   aren't any magic characters there to hang anyone, and do
 *   local echoing if need be.
 */

readchild ()
{
	int  r, i;
	char buf[1024];
	register char *c;
	extern int errno, stream;

	if ((r = read (fromchild, buf, 1024)) == -1) {
		if (errno != EWOULDBLOCK)
			perror ("read from child");
		return;
	} else if (r == 0) {		/* child is finished */
		if (Debug)
			putmessage ("Saw EOF on child process.");
		childpid = 0;
		(void) close (tochild);
		(void) close (fromchild);
	} else {					/* send to everyone */
		if (!killedchild) {
#ifdef LOCAL_ECHO
			int oldwin = selwin (0);
#endif LOCAL_ECHO
			for (c = buf, i = 0; i < r; c++, i++) {
				if (*c & META)
					*c &= 0177;
#ifdef LOCAL_ECHO
				showch (*c);
#endif LOCAL_ECHO
			}
			(void) write (stream, buf, r);
#ifdef LOCAL_ECHO
			refresh ();
			(void) selwin (oldwin);
#endif LOCAL_ECHO
		}
	}
}


/*
 *  SIGCHLD handler.
 *  We don't reset the pid and fd states because we'll get those when
 *  we see the eof on the kid's descriptors.
 */

sigchld ()
{
	union  wait status;

	if (childpid == 0)
		return;

	while (wait3 (&status, WNOHANG, (struct rusage *) 0) > 0)
		;

	if (Debug)
		putmessage ("Caught sigchld.");
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'sendit.c'" '(1506 characters)'
if test -f 'sendit.c'
then
	echo shar: will not over-write existing file "'sendit.c'"
else
cat << \!Funky!Stuff! > 'sendit.c'
#include "defs.h"
#include <sys/time.h>

#define	WAIT	2		/* wait this long before re-sending */

/*
 *  Send the given buffer to the given address, with
 *  multiple retries.  Stores the received string
 *  in the global `buf', and returns the number of chars
 *  read, or -1 on error or no response.
 */

sendit (str, len, sin)
char   *str;
int    len;
struct sockaddr_in sin;
{
	struct	timeval timeout;
	int		rval;		/* number of bytes read */
	int		mask;		/* used for select */
	int		tries;		/* number of tries made */
#ifdef DEBUG
	char	mbuf[100];	/* message buffer */
#endif

	timeout.tv_sec  = WAIT;
	timeout.tv_usec = 0;

	for (tries = 0; tries < 5; tries++) {
		if (sendto (ctl, str, len, 0, &sin, sizeof (sin)) < 0) {
			error (0, "sendit: sendto");
			return (-1);
		}
		if (Debug)
			printf ("\r\nsendit: trying #%d.\r\n", tries);
#ifdef DEBUG
		sprintf (mbuf, "sendit: trying #%d ...", tries);
		putmessage (mbuf);
#endif DEBUG
		mask = 1 << ctl;
		if (select (32, &mask, 0, 0, &timeout) <= 0) {	/* nothing to read on */
			if (Debug)
				printf ("\r\nsendit: no response on try #%d.\r\n", tries);
			continue;
		}
		rval = read (ctl, buf, BUFFER);		/* read the response */
		if (rval < 0) {
			error (0, "sendit: read response");
			continue;
		}
		if (Debug)
			printf ("sendit: read %d chars.\r\n", rval);
		return (rval);
	}
	if (Debug)
		printf ("\r\nsendit: response timed out.\r\n");
#ifdef DEBUG
	putmessage ("sendit: response timed out.");
#endif DEBUG
	return (-1);		/* timed out */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'set.c'" '(3287 characters)'
if test -f 'set.c'
then
	echo shar: will not over-write existing file "'set.c'"
else
cat << \!Funky!Stuff! > 'set.c'
#include "defs.h"

/*
 *  Routines to manage the various user-modifiable variables 
 *  that influence the program's behaviour.
 */

int	
	Interval = 2,			/* number of seconds between messages   */
	Inverse = 1,			/* use inverse video in header lines    */
	Stop = 1,				/* print out the "[Stopped]" mesg on ^Z */
	Debug = 0,				/* print out gobs of debuuging info     */
	Bells = 0,				/* allow bells to actually beep         */
	Echo = 1,				/* echo kb input while running program  */
	Hold = 0;				/* hold kb input from child program     */


#define	VALUE	(1<<1)		/* variable must have a _value_ */
#define	BOOLEAN	(1<<2)		/* is just a toggle */

static struct variable {
	char	*name;			/* name of variable as user sees it */
	int		*value;			/* currently - stored value         */
	int		type;			/* boolean or integer-type variable */
} vars[] = {
	{ "interval",		&Interval,	VALUE	},
	{ "inverse",		&Inverse,	BOOLEAN	},
	{ "stop",			&Stop,		BOOLEAN },
	{ "debug",			&Debug,		BOOLEAN },
	{ "bells",			&Bells,		BOOLEAN },
	{ "echo",			&Echo,		BOOLEAN },
	{ "hold",			&Hold,		BOOLEAN },
	{ 0,				0,			0		},
};


/*
 *   Assign a value to a given variable ...
 */

set (argc, argv)
int     argc;
char  **argv;
{
	struct	variable *vp;
	char	*name;
	int		vlen;
	int		toggle = 0;

	/* see if it's a toggle */
	if (strncmp ("no", *argv, 2) == 0) {
		toggle = 1;
		*argv += 2;
	}

	/* search for variable */
	vlen = strlen (*argv);
	for (vp = vars; vp && (name = vp->name); vp++)
		if (strncmp (*argv, name, vlen) == 0)
			break;
	
	if (!vp || !name) {		/* not found */
		sprintf (buf, "%s: No such variable - %s", *argv,
					"use 'show' to list all variable names");
		putmessage (buf);
		return;
	}

	if (argc == 2) {
		if (vp->type == BOOLEAN) {
			sprintf (buf, 
					"%s is a boolean toggle - use 'set %s' or 'set no%s'.", 
					name, name, name);
			putmessage (buf);
		} else {
			if (!isdigit (argv[1][0]) || toggle) {
				sprintf (buf, "%s requires a numeric argument", name);
				putmessage (buf);
			} else
				*vp->value = atoi (argv[1]);
		}
	} else {			/* argc == 1 */
		if (vp->type != BOOLEAN) {
			sprintf (buf, "%s requires a numeric argument", name);
			putmessage (buf);
		} else {
			if (toggle)		/* name was preceeded by 'no' */
				*vp->value = 0;
			else
				*vp->value = 1;
		}
	}
}



/*
 *  Show the value of a variable.
 */

show (argc, argv)
int   argc;
char *argv[];
{
	struct variable *vp;
	char   *name;
	int    vlen;

	if (argc == 0) {		/* list all variables known */
		message ("Listing all variable names:");
		for (vp = vars; vp && vp->name; vp++)
			showvar (vp);
		return;
	}

	while (*argv) {
		/* search for variable */
		vlen = strlen (*argv);
		for (vp = vars; vp && (name = vp->name); vp++)
			if (strncmp (*argv, name, vlen) == 0)
				break;
		
		if (!vp || !name) {		/* not found */
			sprintf (buf, "%s: No such variable - %s", *argv,
						"use 'show' to list all variable names");
			putmessage (buf);
			return;
		}

		/* print out the variable */
		showvar (vp);
		argv++;
	}
}



/*
 *  Print out one variable nicely...
 */

showvar (vp)
struct variable *vp;
{
	if (vp->type == BOOLEAN)
		(void) sprintf (buf, "%s is %s", vp->name, *vp->value ? "on" : "off");
	else
		(void) sprintf (buf, "%s = %d", vp->name, *vp->value);
	message (buf);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'sig.c'" '(2331 characters)'
if test -f 'sig.c'
then
	echo shar: will not over-write existing file "'sig.c'"
else
cat << \!Funky!Stuff! > 'sig.c'
#include <signal.h>
#include <sys/ioctl.h>

/*
 *  Interrupt handler - if we have a child, kill it off, else exit.
 */

sigint ()
{
	extern   int	tochild, fromchild;
	extern   int	childpid;
	extern   int	killedchild;
	extern   int	connected;
	extern   int	did_screen;
	unsigned char c;

	if (childpid > 0) {
		kill (childpid, SIGHUP);	/* it's sure to die sooner or later!!! */
		kill (childpid, SIGINT);
		kill (childpid, SIGKILL);
		killedchild = 1;
		putmessage ("Child has been killed!!!!");
	} else {
		if (connected && did_screen) {
			putmessage ("Really quit? ");
			read (0, &c, 1);
			c &= 0177;
			if (c == 'y' || c == 'Y')
				quit ();
			putmessage (" ");
		} else {
			quit ();
		}
	}
}


/*
 *  Sigquit handler - save a copy of the screen into a ".dump" file.
 */

sigquit ()
{
	dodump (".dump");
}


#ifdef vax

/*
 *  Generic bad signal handler. Very non-portable,
 *  at least according to the documentation.
 */

badsig (sig)
int sig;
{
	char  *s;

	cleanup ();

	switch (sig) {
		case SIGHUP:	s = "SIGHUP";	break;
		case SIGINT:	s = "SIGINT";	break;
		case SIGQUIT:	s = "SIGQUIT";	break;
		case SIGILL:	s = "SIGILL";	break;
		case SIGTRAP:	s = "SIGTRAP";	break;
		case SIGIOT:	s = "SIGIOT";	break;
		case SIGEMT:	s = "SIGEMT";	break;
		case SIGFPE:	s = "SIGFPE";	break;
		case SIGKILL:	s = "SIGKILL";	break;
		case SIGBUS:	s = "SIGBUS";	break;
		case SIGSEGV:	s = "SIGSEGV";	break;
		case SIGSYS:	s = "SIGSYS";	break;
		case SIGPIPE:	s = "SIGPIPE";	break;
		case SIGALRM:	s = "SIGALRM";	break;
		case SIGTERM:	s = "SIGTERM";	break;
		case SIGURG:	s = "SIGURG";	break;
		case SIGSTOP:	s = "SIGSTOP";	break;
		case SIGTSTP:	s = "SIGTSTP";	break;
		case SIGCONT:	s = "SIGCONT";	break;
		case SIGCHLD:	s = "SIGCHLD";	break;
		case SIGTTIN:	s = "SIGTTIN";	break;
		case SIGTTOU:	s = "SIGTTOU";	break;
		case SIGIO:		s = "SIGIO";	break;
		case SIGXCPU:	s = "SIGXCPU";	break;
		case SIGXFSZ:	s = "SIGXFSZ";	break;
		case SIGVTALRM:	s = "SIGVTALRM";	break;
		case SIGPROF:	s = "SIGPROF";	break;
		case SIGWINCH:	s = "SIGWINCH";	break;
		case SIGUSR1:	s = "SIGUSR1";	break;
		case SIGUSR2:	s = "SIGUSR2";	break;
	}
	printf ("\r\n\r\nCaught %s signal!!!! Exiting...\r\n\r\n", s);
	exit (1);
}

#else

badsig ()
{
	cleanup ();
	printf ("\r\n\r\nCaught an unknown bad signal! Exiting ...\r\n\r\n");
	exit (1);
}

#endif vax
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'stop.c'" '(501 characters)'
if test -f 'stop.c'
then
	echo shar: will not over-write existing file "'stop.c'"
else
cat << \!Funky!Stuff! > 'stop.c'
#include <signal.h>
#include <curses.h>
#include "defs.h"

/*
 *  Routine to handle SIGTSTP and the like.
 */

sigstop ()
{
	extern  int stream;
	extern  int maxy;
	int     (*func)();

	func = signal (SIGTSTP, SIG_DFL);		/* save old SIGTSTP routine */

	if (Stop)
		(void) write (stream, "\n[ Stopped ]", 12);

	tstp ();						/* curses routine to stop and restart */

	if (Stop)
		(void) write (stream, "\r\f", 2);	/* clear this line */

	signal (SIGTSTP, func);			/* and restore SIGTSTP routine */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'strsave.c'" '(324 characters)'
if test -f 'strsave.c'
then
	echo shar: will not over-write existing file "'strsave.c'"
else
cat << \!Funky!Stuff! > 'strsave.c'
#include <signal.h>
#include <stdio.h>

/*
 *  Allocate enough space for the given string and copy it over.
 */

char *
strsave (s)
char *s;
{
	register char *new;
	char     *malloc();

	if (new = malloc ((unsigned) (strlen (s) + 1)))
		strcpy (new, s);
	else
		error (1, "strsave: cannot malloc memory");

	return (new);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'tilde.c'" '(866 characters)'
if test -f 'tilde.c'
then
	echo shar: will not over-write existing file "'tilde.c'"
else
cat << \!Funky!Stuff! > 'tilde.c'
#include <pwd.h>

/*
 *  Expand a name containing a tilde to the full pathname.
 */

char *
exptilde (str)
char *str;
{
	char   buf[512];
	char   name[20];
	char   *i, *index();
	char   *getenv(), *strsave();
	struct passwd *pw, *getpwnam();

	if (*str != '~')				/* doesn't contain a tilde */
		return (strsave (str));

	if (i = index (str, '/'))		/* find slash, if any */
		*i = '\0';
	
	strcpy (name, str+1);			/* copy name after tilde */

	if (*name == '\0')				/* was "~/" */
		strcpy (buf, getenv ("HOME"));	/* so use our home */
	else {
		if ((pw = getpwnam (name)) == (struct passwd *) 0) {
			sprintf (buf, "Unknown user: %s", name);
			putmessage (buf);
			return ((char *) 0);
		}
		strcpy (buf, pw->pw_dir);	/* fill in that user's home */
	}
	if (i) {
		*i = '/';					/* and put back trailing pathname */
		strcat (buf, i);
	}
	return (strsave (buf));
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'timer.c'" '(373 characters)'
if test -f 'timer.c'
then
	echo shar: will not over-write existing file "'timer.c'"
else
cat << \!Funky!Stuff! > 'timer.c'
/*
 *  Timer routine.
 *  When we come here, see if we have any pending calls or messages,
 *  call the appropriate routines, then reset the timer.
 */

timer ()
{
	extern	int pending, msgpending;

	if (pending)		/* have pending calls */
		reinvite ();

	if (msgpending >= 0)
		showmessage ();

	if (pending || msgpending >= 0)      /* set the next alarm */
		alarm (1);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'who.c'" '(2104 characters)'
if test -f 'who.c'
then
	echo shar: will not over-write existing file "'who.c'"
else
cat << \!Funky!Stuff! > 'who.c'
#include "../common.h"
#include "defs.h"
#include <netdb.h>
#include <utmp.h>
#include <sys/file.h>

/*
 *  Print out who is on the machine.
 *  If an argument is specified, only show occurrences of that user.
 */

who (argc, argv)
int  argc;
char *argv[];
{
	extern  int maxx;     /* max display line length   */
	int     found = 0;    /* found who we're looking for */
	int     utfd;         /* /etc/utmp file descriptor */
	int     len = 256;    /* length of current line    */
	int		ulen;         /* length of buf this user   */
	struct  utmp utmp;    /* one entry in /etc/utmp    */
	char    line[256];    /* line buffer               */
	char    buf[20];      /* and one entry buffer      */
	char	*host;
	struct	hostent	*hp, *gethostbyname();
	struct	sockaddr_in sin;

	if (argc == 1 && **argv == '@') {
		host = &argv[0][1];
		if (isdigit (*host))
			sin.sin_addr.s_addr = inet_addr (host);
		else if (hp = gethostbyname (host))
			bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
		else {
			sprintf (buf, "who: unknown host: %s", host);
			message (buf);
		}
		sin.sin_port = port;
		sin.sin_family = AF_INET;
		sprintf (buf, "%c%c", ESC, WHO);
		if (sendto (ctl, buf, 2, 0, &sin, sizeof (sin)) < 0)
			error (0, "who: sendto");
		return;
	}
	if ((utfd = open ("/etc/utmp", O_RDONLY)) < 0) {
		message ("Can't open /etc/utmp!!!");
		return;
	}

	line[0] = '\0';

	while (read (utfd, (char *)&utmp, sizeof (struct utmp))) {
		if (utmp.ut_name[0] == '\0')
			continue;
		if (argc && strncmp (argv[0], utmp.ut_name, 8))
			continue;
		found++;
		sprintf (buf, " %.8s(%.5s)", utmp.ut_name, utmp.ut_line);
		ulen = strlen (buf);
		if (len + ulen + 2 < maxx) {        /* just add to current message */
			strcat (line, buf);
		} else {                            /* buf is too long - print line */
			message (line);
			len = Inverse ? 2 : 0;			/* kludge for inverse glitch */
			sprintf (line, "who:%s", buf);
		}
		len += ulen;                      /* add to total line length */
	}
	if (len)
		message (line);

	close (utfd);

	if (argc && !found)
		message ("<not logged on>");
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'windows.c'" '(12693 characters)'
if test -f 'windows.c'
then
	echo shar: will not over-write existing file "'windows.c'"
else
cat << \!Funky!Stuff! > 'windows.c'
/*
 *   Routines to manage the windows.  SOme of this is pretty ugly
 *   stuff, especially as most of the time is spent here.
 *
 *   We manage to resize windows by buffering all parts of a conversation,
 *   so we just (re)print the buffered part.  The buffering also accounts
 *   for most of the lack of speed.  A future enhancement will probably
 *   be to replace the constant buffering with a smarter routine that
 *   will just grab each window from the curses screen buffer when we call
 *   stretch() ...
 */


#include <curses.h>
#include <sys/ioctl.h>
#include <signal.h>
#undef ctrl()			/* defined in curses.h */
#include "defs.h"

#ifndef cbreak()		/* these are defined in *some* versions of curses.h */
#define cbreak() (_tty.sg_flags |= CBREAK, _rawmode = TRUE, stty(_tty_ch,&_tty))
#define nocbreak() (_tty.sg_flags &= ~CBREAK,_rawmode=FALSE,stty(_tty_ch,&_tty))
#endif  !cbreak()

#ifndef WINDOWS
#define WINDOWS 32		  /* we can only fit 23 on a sun !!! */
#endif

struct win {
	int   inuse;          /* 1 if in use, 0 if not      */
	char  *login;         /* login name of this person  */
	char  *host;          /* their host name            */
	char  *tty;           /* tty                        */
	char  *realname;      /* finger name, if any        */
	int   upper;          /* top line of this window    */
	int   lower;          /* bottom line of this window */
	int   y;              /* y coord rel to top of wind */ 
	int   x;              /* last known x coordinate    */
	int   old;            /* index of oldest ch in buf  */
	int   new;            /* where next char goes       */
	char  *buf;           /* text buffer                */
};

static	struct win windows[WINDOWS];
static	struct win *currwin;	/* pointer to current window    */
int		current = 0;     		/* currently active window      */
int		users;					/* number of users/windows      */
int		upper_y;				/* boundaries of current window */
int		lower_y;
int		stretching = 0;
int		did_screen = 0;			/* have set up curses          */
static	int y , x;				/* current screen coordinates  */
extern	int sigstop();			/* ^Z handling routine         */

static int ourwin;				/* the window # that is _ours_ */

/*
 *  Mark all of the windows as not being in use.
 */

setup ()
{
	int  i;

	for (i = 0; i < WINDOWS; i++) {
		windows[i].inuse = 0;
		windows[i].old   = 0;
		windows[i].new   = 0;
	}
	
	initscr();
	did_screen = 1;
	clear ();
	cbreak ();
	noecho ();
	nonl ();
	refresh ();

	signal (SIGTSTP, sigstop);
	geteditchars ();				/* get and save his editing characters */

	maxx = stdscr->_maxx - 1;      			/* save max screen coordinates */
	maxy = stdscr->_maxy - 1;

	windows[0].upper = upper_y = 0;				/* initial screen bounds */
	windows[0].lower = lower_y = maxy - 1;
	windows[0].y = y = 1;						/* initial window position */
	windows[0].x = x = 0;
	currwin = &windows[0];
	move (1, 0);
	users = 0;									/* not talking to anyone yet */
}


/*
 *  Restore everything to how it was before we came...
 */

cleanup ()
{
	move (maxy, 0);
	refresh ();
	nocbreak ();
	echo ();
	endwin ();
	did_screen = 0;
}


/*
 *  Select the new current window to use, set the window bounds,
 *  and set the new cursor position ...  Returns the old window number.
 */

selwin (win)
char    win;
{
	int  old = current;

	if (win == current && !stretching)	/* no change */
		return (win);
	if (win == ourwin)				/* map our window to window 0 */
		win = 0;

	if (windows[win].inuse == 0)
		return (old);

	if (win >= 0 && win < WINDOWS) {
		currwin->y = y;				/* save last coordinates */
		currwin->x = x;
		currwin = &windows[win];	/* set pointer to new window */
		y = currwin->y;				/* and retrieve from new win */
		x = currwin->x;
		upper_y = currwin->upper;	/* get bounds */
		lower_y = currwin->lower;
		move (y + upper_y, x);		/* restore cursor pos */
		current = win;
	}
	return (old);
}



/*
 *  Add a new window to the screen.
 *  Buf looks like "user:host:tty:realname"
 */

adduser (slot, buf)
int     slot;
char    *buf;
{
	char	*i;

	/*
	 *  We rely on the fact that the first window given to us is
	 *  our own, so we remember which one that is and remap it 
	 *  to the top window. This makes people happier.
	 */

	if (users == 0) {
		ourwin = slot;
		slot = 0;
	}

	if (windows[slot].inuse)				/* that window is already in use */
		return;

	windows[slot].inuse = 1;				/* mark as being in use */
	windows[slot].x = 0;					/* and current coordinates */
	windows[slot].y = 1;
	windows[slot].buf = malloc (BUFFER);	/* and grab a buffer for it */
	windows[slot].old = 0;					/* nothing in the buffer yet */
	windows[slot].new = 0;

	/*  get the info out of the buffer line  */
	if (i = index (buf, ':'))    /* name */
		*i++ = '\0';
	else
		return;
	windows[slot].login = strsave (buf);
	buf = i;
	i = index (buf, ':');    /* host */
	*i++ = '\0';
	windows[slot].host = strsave (buf);
	buf = i;
	i = index (buf, ':');    /* tty */
		*i++ = '\0';
	windows[slot].tty = strsave (buf);
	buf = i;
	windows[slot].realname = strsave (buf);  /* real name */

	if (users++ == 1)					/* add another user to the count */
		(void) write (1, "\07\07", 2);	/* initial connection - beep the user */
	stretch (0);						/* and recompute all the windows */
}



/*
 *  Delete a user and his window.
 */

deluser (win)
char     win;
{
	if (win < 0 || win >= WINDOWS)
		return;

	if (windows[win].inuse == 0)
		return;

	windows[win].inuse = 0;			/* this window no longer in use */
	free (windows[win].login);		/* so free up any space allocated for it */
	free (windows[win].host);
	free (windows[win].tty);
	free (windows[win].realname);
	free (windows[win].buf);

	if (--users > 1) {				/* decrement the count */
		stretch (0);				/* and redraw */
		refresh ();
	} else							/* if only one user left .. */
		quit ();					/* then we're done */
}



/*
 *  Show the given character, performing word and line erase.
 */

showch (ch)
char    ch;
{
	if (users == 0)
		return;

	if (!stretching) {
		/* save char into buffer */
		if (currwin->new >= BUFFER)
			error (1, "New >= BUFFER in showch()");
		currwin->buf[currwin->new++] = ch;
		if (currwin->new >= BUFFER)			/* wrap around */
			currwin->new = 0;
		if (currwin->new == currwin->old)	/* tail caught up */
			currwin->old++;
		if (currwin->old >= BUFFER)			/* wrap old around */
			currwin->old = 0;
	}

	switch (ch) {
		case '\0177':	/* delete */
		case ctrl(H):   /* backspace */
						if (x > 0) {
							addstr ("\b \b");
							x--;
						}
						break;

		case ctrl(I):	/* tab */
						addch (' ');
						x++;
						while (x++ % 8)
							addch (' ');
						break;

		case ctrl(J):	/* newline */
						x = 0;
						if ((++y + upper_y) > lower_y)
							y = 1;
						move (y + upper_y, 0);
						clrtoeol ();
						break;

		case ctrl(L):	/* clear to end of current line */
						clrtoeol ();
						break;

		case ctrl(M):	/* carriage return */
						x = 0;
						move (y + upper_y, 0);
						break;

		case ctrl(N):	/* clear window */
						for (y = 1; (y+upper_y) <= lower_y; y++) {
							move (y + upper_y, 0);
							clrtoeol ();
						}
						y = 1; x = 0;
						move (y + upper_y, 0);
						break;

		case ctrl(U):	/* line kill */
		case ctrl(X):	x = 0;
						move (y + upper_y, 0);
						clrtoeol ();
						break;

		case ctrl(W):	/* word erase */
						while (x >= 0) {         /* nuke the spaces */
							if (inch () != ' ')
								break;
							addstr (" \b\b");
							if (x > 0)
								x--;
							else
								break;
						}
						while (x >= 0) {        /* and the non-spaces */
							if ((ch = inch ()) == ' ') {
								addch (ch);
								x++;
								break;
							}
							addstr (" \b\b");
							if (x > 0)
								x--;
							else
								break;
						}
						break;

		case ctrl(^):	/* home cursor */
						y = 1; x = 0;
						move (y + upper_y, 0);
						break;

		case ctrl(G):	/* bell - switchable */
						if (Bells) {
							(void) write (1, "\007", 1);
							break;
						}
						/* fall through ... */

		default:		if (ch < ' ') {
							addch ('^');
							ch += '@';
							x++;
						}
						addch (ch);
						x++;
						break;
	}

	if (x >= maxx) {				/* have wrapped around to next line */
		y++;
		x %= maxx;
		if (y + upper_y > lower_y)	/* wrapped back to top of window */
			y = 1;
		move (upper_y + y, 0);
		clrtoeol ();
	}
}


/*
 *  Print a message to the bottom line.
 */

putmessage (str)
char *str;
{
	int  y, x;
	extern int Inverse;
	extern int did_screen;

	if (!did_screen) {
		printf ("%s\r\n", str);
		fflush (stdout);
		return;
	}
	touched25 = 1;				/* has been changed  */
	getyx (stdscr, y, x);		/* save current pos  */
	move (maxy, 0);				/* go to bottom line */
	clrtoeol ();				/* clear it          */
	refresh ();					/* they see it clear */
	if (Inverse)
		standout ();

	while (*str) {				/* print out the message, removing ctrl chars */
		if (*str < ' ') {
			addch ('^');
			addch (*str + '@');
		} else
			addch (*str);
		str++;
	}

	if (Inverse)
		standend ();
	move (y, x);				/* restore old pos   */
	refresh ();					/* etc ...           */
}


/*
 *  Print out a header for the given slot.
 */

header (num)
int num;
{
	register int i;
	extern   int Inverse;

	if (windows[num].inuse == 0) {
		error (0, "header(): slot not in use");
		return;
	}

	sprintf (buf, "---- %s@%s on %s (%s) ", windows[num].login, 
			windows[num].host, windows[num].tty, windows[num].realname);
	
	for (i = strlen (buf); i < maxx; i++)
		buf[i] = '-';
	buf[maxx-2] = '\0';		/* two (four?) for magic-cookie terminals */

	move (windows[num].upper, 0);
	if (Inverse)
		standout ();
	addstr (buf);
	if (Inverse)
		standend ();
}



/*
 *  Take the current screen and contort it to handle the new windows.
 *
 *  Handles both the screen changing size and just the 
 *  number of windows changing.
 *
 *  We set caught_sig to be non-zero if coming here on SIGWINCH, 0 otherwise.
 *  We also only refresh the screen if we caught the signal....
 */

stretch (caught_sig)
int caught_sig;
{
	register int   i, w;
	register int   extra;
	register int   lines;
	int      oldy, oldx;
	int      newy, newx;
	int      lines_win;
	int      oldcurr = current;

	if (users == 0)		/* nothing to recompute */
		return;

	stretching = 1;

	oldy = maxy + 1;
	oldx = maxx + 1;

	if (caught_sig) {			/* got SIGWINCH, have to redo screen */
#if defined(TIOCGWINSZ) || defined(TIOCGSIZE)
#ifdef TIOCGWINSZ
		struct winsize winsize;
#else  TIOCGSIZE	/* (older?) suns */
		struct ttysize ttysize;
#endif TIOGSIZE

		/* get new screen size */
#ifdef TIOCGWINSZ
		if (ioctl (0, TIOCGWINSZ, &winsize)) {
			error (0, "ioctl: TIOCGWINSZ");
			return;
		}
		newy = winsize.ws_row;
		newx = winsize.ws_col;
#else TIOCGSIZE
		if (ioctl (0, TIOCGSIZE, &ttysize)) {
			error (0, "ioctl: TIOCGSIZE");
			return;
		}
		newy = ttysize.ts_lines;
		newx = ttysize.ts_cols;
#endif TIOCGSIZE 
		
#else /* don't have window size ioctls */
		/* 
		 *  Can't get new window size, so presumably sigwinch 
		 *  doesn't exist and this code will never happen !!!
		 */
		newy = oldy;
		newx = oldx;
#endif TIOCGWINSZ
		maxy = newy - 1;	/* max screen coordinate - zero-based */
		maxx = newx - 1;
	} else {				/* screen size hasn't changed */
		newy = oldy;
		newx = oldx;
	}

	if (newx != oldx || newy != oldy) {			/* have to shut down curses */
		echo ();
		nocbreak ();
		endwin ();
		did_screen = 0;

		initscr ();			/* and restart it */
		did_screen = 1;
		cbreak ();
		noecho ();
		nonl ();
		signal (SIGTSTP, sigstop);
	} else {				/* just have to clear all the lines */
		for (i = 0; i < newy; i++) {
			move (i, 0);
			clrtoeol ();
		}
	}

	lines_win = (newy - 1) / users;		/* recompute window boundaries */
	extra = (newy - 1) - (users * lines_win);	/* total extra lines */
	lines = 0;

	/* 
	 *   Now calculate the new boundaries and
	 *   copy the buffer back to the new window.
	 */
	
	x = 0; y = 1;		/* go back to the beginning of the window */
	for (w = 0; w < WINDOWS; w++) {		/* now go copy boundaries */
		if (windows[w].inuse == 0)
			continue;
		windows[w].upper = lines;	/* new top boundary */
		if (extra-- > 0)			/* extra lines for this window */
			lines++;
		lines += lines_win;			/* add on lines */
		windows[w].lower = lines-1;	/* new bottom boundary */
		header (w);					/* redraw the window header */
		windows[w].x = 0;			/* reset position to top corner */
		windows[w].y = 1;
		selwin (w);					/* switch to this window */
		/* and dump the buffer back into the window */
		for (i = windows[w].old; i != windows[w].new; i = (i+1) % BUFFER)
			showch (windows[w].buf[i]);
	}

	move (y, x);
	selwin (oldcurr);				/* reselect the right window */
	if (caught_sig)
		refresh ();					/* and make it all visible */
	stretching = 0;
}
!Funky!Stuff!
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Comp.sources.unix mailing list