[net.sources] source to "phone" system

broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)

#-----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:
#	NOTE
#	READ_ME
#	phone.1
#	Makefile
#	common.h
# This archive created: Sat Dec 28 01:18:51 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'NOTE'" '(2690 characters)'
if test -f 'NOTE'
then
	echo shar: will not over-write existing file "'NOTE'"
else
cat << \!Funky!Stuff! > 'NOTE'

Well, this is the first part of the source to my "phone" program, as promised.
I received quite a few responses, many with weird addresses, thus the posting 
instead of a mailing.  Be warned that this source is *not* a finished product, 
but more a "gamma test" version, although it has been running on a dozen UCB 
machines for several months and seems to work reasonably well.  For those of 
you who may feel compelled to hack on it, I have laced the sources with quite 
a few comments about possible improvements. I would also welcome any 
suggestions/code you may come up with.  Hopefully I will be able to send out 
a finished product sometime in the coming few months ... 

A few of the letters I received mentioned an interest in porting this to SysV
machines and such ... Good luck! (Last I heard, AT&T had just heard of IPC!)
This has only been tested on 4.[23] machines (Vax, Sun, and Perkin/Elmer), but
will hopefully be easy enough to port to other hardware. 

Installing phone: 

  Create an empty directory to put the sources in.  Unshar the archive files
by feeding to "sh"  (or use "unshar").  Run "make", then "make install" (use
"make -n" first if you're paranoid!)  Add "phone" to /etc/services, the line 
should look like:

	phone		1167/udp		# phone - conference calling (broome)

A note on port numbers: there is no need for phone to use priviledged port
numbers (think about it), and it would really be best for you to stick with 
port 1167 (what we use at Berkeley), at least if you want to be able to 
communicate with the outside world.  I am working on an rfc-type spec in 
order to get a legitimately-assigned port number, but it will take a little 
while, so let's use 1167, okay? 

Back to installation:  If your site runs the inetd, also add this line to
/etc/services:

	phone	dgram	udp	wait	root	/etc/phoned	phoned

Finally,  if you do not use inetd, start up the phoned by typing "/etc/phoned",
else do a "ps ax | grep inetd" to find inetd's pid, then kill -HUP pid to have 
it reread the config file.

The manual page is presently pretty skimpy, so you may want to look at the
routines in kb.c and cmd.c to get some idea as to what is going on.
(Brownie points to anyone who writes a better man page!)


For those of you with arpanet access, the sources will also be available in
 ~ftp/pub/phone.tar on ucbvax for your ftp'ing enjoyment.

Good luck, and enjoy!

==============================================================================
 Jonathan C. Broome                      University of California at Berkeley
    ...!ucbvax!broome
   broome@ucb-vax.berkeley.edu
==============================================================================

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'READ_ME'" '(1102 characters)'
if test -f 'READ_ME'
then
	echo shar: will not over-write existing file "'READ_ME'"
else
cat << \!Funky!Stuff! > 'READ_ME'
Stuff for installing phone:                                    22 Dec 1985
===========================                                    ===========

Phone consists of three parts - the client ("phone"), the master daemon 
("phoned"), and the conversation daemon ("convd").

"Phoned" is the master server on each machine.  It receives requests from
the client process and acts on them.  Its main purpose is to receive,
store, and process call requests.  All communication to phoned is through
udp/ip datagrams.  The master daemon can be compiled to run standalone or 
under the inetd as a single-threaded datagram-oriented server. 

The conversation daemon is invoked by the master daemon when a user requests
a conversation. Its purpose is simply to relay what each person types back
to all the others in a single conversation.  Each user connects to the convd
over a tcp/ip stream connection, and the convd reads a buffer from each
client and resends the buffer to the other users.

"Phone" is the user client program, responsible for managing windows and
sending control packets to request calls.

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'phone.1'" '(4210 characters)'
if test -f 'phone.1'
then
	echo shar: will not over-write existing file "'phone.1'"
else
cat << \!Funky!Stuff! > 'phone.1'
.TH PHONE 1
.UC 5
.SH NAME
phone \- communicate with other users in real-time
.SH SYNOPSIS
phone [ user@host [tty] ]
.SH DESCRIPTION
\fIPhone\fR allows for two or more people to interact in 
a conversation across a machine or network, providing a form 
of conference calling.
Each participant has a window in which to type. The first line
of each window is a header showing who is in the window, like:
.sp 1
.nf 
.in +3
---- root@cory on console (Commodore Cory) --------------
.in -3
.fi
.sp 1
The login name and tty are automatically determined, and the
real name is taken from the password file, which may be overridden
by setting the \fINAME\fR environment variable (see \fIcsh\fR(1) for
further details.)
Users may join or leave a conversation at any point in time, and the
windows will be automatically resized and redrawn.
.SH USAGE
When you are being paged by another person, a message like this will 
appear on your screen:
.br
.sp 1
.nf
.in +5
Message from the Telephone_Operator@host at time ...
phone: connection requested by user@host
phone: respond with "phone user@host"
.in -5
.sp 1
.fi
You may answer the phone simply by typing "phone", which will 
answer the pending call, and connect you directly.
.br
.sp 1
\fIPhone\fR has two \fImodes\fR, much like the \fIvi\fR editor.  These
two modes are called \fIconversation\fR and \fIcommand\fR modes, and
are toggled through the escape (<esc>) and return (<ret>) keys.
.br
.sp 1
When in the \fIconversation\fR mode, anything typed
on the keyboard is sent to everyone in the current conversation.  This
is the default mode.
.br
.sp 1
The \fIcommand\fR mode is used to execute commands, and is entered by 
pressing the escape key. When in this mode, \fIphone\fR will clear
the bottom line of the screen and print the prompt "Command>".  
At this point anything typed in is
added to the command buffer, and will be executed when the return key
is pressed.  To exit command mode without executing the acommand,
press the escape key a second time.
.br
.sp 1
To ivite another user to join the current conversation from within \fIphone\fP,
enter command  mode by pressing the escape key, then type
.br
.sp 1
.in +5
call \fIuser@host\fR
.in -5
.sp 1
followed by the <return> key. 
The user will receive a message like the one shown above if
he is logged in. 
The \fIhost\fR part of the name may be omitted if the
both you and the other person are on the same machine.
.br
.sp 1
\fIPhone\fR also allows a user to execute shell commands inside his window 
with any keyboard input being fed to the process.  The program's output
is sent to all users in the conversation.
A shell command is executed within \fIphone\fR through the use of
the \fIrun\fR or \fI!\fR command.  An example of this is:
.br
.sp 1
.in +5
run adb a.out core
.in -5
.sp 1
to run the \fIadb\fR command with the arguments \fIa.out\fR and \fIcore\fR.
Note that tilde expansion (ie. ~user) is done by \fIphone\fR, but
wildcarding, piping, and i/o redirection are performed by the user's shell.
It is unlikely that anyone actually cares, of course.
Also, the use of
visually-oriented programs such as \fIvi\fR and \fIrogue\fR is not
recommended, as this usually results in strange and unpredictable things 
happening. If your terminal goes up in a puff of smoke, you were warned.
.br
.sp 1
To find about the other commands available with \fIphone\fR, type
\fIhelp\fR or \fI?\fR in command mode.
.PP
You can allow or disallow \fIphone\fR messages to your terminal through 
the use of the \fImesg\fR command.  When you first log on, messages are enabled.
.SH BUGS
\fICsh\fR is unhappy being fed through pipes, but it's a dumb program anyway.
.br
The manual page is horrendous at best.
.br
Please send any problems, questions, or suggestions to the author.
.SH AUTHOR
Jonathan C. Broome (broome@ucb-vax.berkeley.edu)
.br
The original user interface is borrowed from a previous program (also 
called \fIphone\fR) posted to the network in late 1984, author unknown.
.SH FILES
.ta \w'/etc/passwd      'u
.br
/etc/hosts	to find the recipient's machine
.br
/etc/utmp	to find the recipient's tty
.br
/etc/passwd	to find each user's real name
.SH "SEE ALSO"
mail(1), mesg(1), talk(1), who(1), write(1)
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(310 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'
#
#  Master makefile for the phone program         18 December 1985
#
#  You'll need to edit the makefile in each of the subdirectories
#  to change the compilation flags.
# 

DIRS	=	client master conv

.DEFAULT:	
	for i in ${DIRS} ; do \
		cd $$i ; make MFLAGS="${MFLAGS}" $@ ; cd .. ; \
	done

default: all	
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'common.h'" '(1266 characters)'
if test -f 'common.h'
then
	echo shar: will not over-write existing file "'common.h'"
else
cat << \!Funky!Stuff! > 'common.h'
/*
 *   Defines common to all the parts of the phone system.
 */


#ifndef		ESC
#define		ESC			'\033'		/* precedes all commands */
#endif

#define		ACK			'y'			/* good response code */
#define		NAK			'n'			/* not-so-good code   */

/*
 *   Commands sent from conversation daemon to client.
 */
 
#define		META		0200			/* high bit for command characters */
#define		ADDUSER		(01<<5)			/* add a user to the conversation  */
#define		DELUSER		(02<<5)			/* delete a user from conversation */
#define		UPDATE		(03<<5)			/* set screen update mode */

/*
 *   Commands sent from or master daemon to client.
 */

#define		MESSAGE		'M'				/* following is message text       */

/*
 *   Commands sent by client to conversation or master daemons.
 */

#define		ANSWER		'A'			/* he got the invite, will answer  */
#define		CALLING		'C'			/* daemon is calling the user      */
#define		PAGE		'P'			/* page a user                     */
#define		INQUIRE		'I'			/* inquire as to whether invited   */
#define		REINVITE	'R'			/* renew a request for paging      */
#define		DAEMON		'D'			/* create a daemon for me          */
#define		WHO			'W'			/* tell me who's on ...            */
#define		KILL		'K'			/* cause the daemon to exit        */

#ifndef PORT
#define		PORT	1167
#endif
!Funky!Stuff!
fi # end of overwriting check
#	End of shell archive
exit 0

broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)

#-----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@host [tty]",		pagehelp	},
	{ "page",	page,	2,	3,	"user@host [tty]",		pagehelp	},
	{ "phone",	page,	2,	3,	"user@host [tty]",		pagehelp	},
	{ "cancel",	cancel,	2,	3,	"user@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@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

broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)

#-----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
#	../common.h
#	child.c
#	daemon.c
#	dopage.c
#	forward_program.c
#	inquire.c
#	list.c
#	main.c
#	page.c
#	pagetty.c
#	reinvite.c
#	strsave.c
#	utmp.c
# This archive created: Sat Dec 28 01:11:09 1985
export PATH; PATH=/bin:$PATH
mkdir master
cd master
echo shar: extracting "'Makefile'" '(4014 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'
# 
#   Makefile for phoned            20 December 1985
#


# What the flags mean:
#  INETD    - set this if the phoned is to run a a single-threaded service
#             under /etc/inetd. It expects for fd 0 to be a datagram socket
#             bound to the service address that it wil receive from.
#  FORK     - set this when *not* under inetd if you want to server to
#             fork upon startup, with the parent exiting. This is usually
#             set, and does nothing if INETD is also defined.
#  DPATH    - the full pathanme of the conversation daemon. If phoned
#             cannot find it here, it will try to find "convd" in 
#             /usr/local/lib, /usr/lib, and /etc.
#  SERVICES - set this if phone is listed as a datagram service in 
#             /etc/services. This has no effect under the inetd.
#  PORT     - if INETD and SERVICES are not defined, this is the port 
#             number to listen on (overriding the default in ../common.h)
#  NO_WHO	- Define this if you want your site to be "secure" and not allow
#             outside users to use the "who" command to see who's on ...

#CFLAGS = -O -DDPATH=\"/usr/local/lib/convd\"
CFLAGS  = -O -DINETD -DDPATH=\"/usr/local/lib/convd\"

LPR     = lpr -Pvax

HDRS	= defs.h ../common.h
SRCS	= child.c daemon.c dopage.c forward_program.c\
			inquire.c list.c main.c page.c pagetty.c\
			reinvite.c strsave.c utmp.c

OBJS	= child.o daemon.o dopage.o forward_program.c\
			inquire.o list.o main.o page.o pagetty.o\
			reinvite.o strsave.o utmp.o

DEST	= phoned
RDEST	= /etc/phoned


.DEFAULT:
	co $<

all:	${DEST}

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

${OBJS}: ${HDRS}

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

clean:
	/bin/rm -f ${DEST} core *.o

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

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

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

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

dist:	${DEST}
	-rcp ${DEST} buddy:${RDEST}
	-rcp ${DEST} franny:${RDEST}
	-rcp ${DEST} holden:${RDEST}
	-rcp ${DEST} seymour:${RDEST}
	-rcp ${DEST} zooey:${RDEST}
	-rcp ${DEST} cory:${RDEST}
	-rcp ${DEST} miro:${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

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

utmp.o pagetty.o: /usr/include/sys/file.h

page.o main.o: /usr/include/netdb.h

main.o list.o: /usr/include/signal.h

pagetty.o page.o list.o: /usr/include/time.h

pagetty.o page.o list.o: /usr/include/sys/time.h

page.o forward_program.o: /usr/include/pwd.h

utmp.o dopage.o: /usr/include/sys/stat.h

pagetty.o main.o daemon.o: /usr/include/syslog.h

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

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/errno.h

utmp.o reinvite.o pagetty.o pagetty.o page.o main.o main.o list.o list.o \
inquire.o forward_program.o dopage.o daemon.o daemon.o child.o: \
/usr/include/stdio.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/netinet/in.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/sys/socket.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/sys/types.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: ./defs.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o dopage.o daemon.o \
child.o: ./../common.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'" '(2842 characters)'
if test -f 'defs.h'
then
	echo shar: will not over-write existing file "'defs.h'"
else
cat << \!Funky!Stuff! > 'defs.h'
/*
 * $Header: defs.h,v 1.1 85/10/28 17:38:15 broome Exp $
 */

/*
 * $Log:	defs.h,v $
 * Revision 1.1  85/10/28  17:38:15  broome
 * Initial revision
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>

#define SOCKADDR	struct sockaddr_in    /* shorter to type */

/*
 *   One of these structures is used for each pending invitation.
 */

struct invite {
	/*  info about the person requesting a call  */
	char      *caller;           /* login name of person making invitation */
	char      *host;             /* figured out from control port address  */
	SOCKADDR  ctladdr;           /* inviter's control port address         */
	char      *convaddr;         /* inviter's conversation port address    */
	char      *callno;			 /* unique per-user message id from caller */

	/*  info about the person being requested  */
	char      *callee;           /* login name of person being requested   */
	char      *tty;              /* user's tty, if any                     */
	char      *ptty;             /* tty we are actually paging             */
	char      *home;             /* his home directory                     */
	int       uid, gid;          /* used for forwarding programs           */

	/*  and bookkeeping information about the invitation itself  */
	int       type;              /* normal page or being forwarded?        */
	int       rings;             /* send a new ring when rings == 0        */
	int       pid;               /* child notification pid                 */
	int       flags;             /* various stuff about status             */
	char      id[10];			 /* identification for this request        */
	struct    invite *prev;      /* previous in doubly-linked list         */
	struct    invite *next;      /* next most recent invitation            */
};

typedef struct invite INV;
#define NIL       ((INV *) 0)
#define eq(a,b)   (strcmp(a,b) == 0)

/*  often-used functions  */
char    *malloc();
char    *strsave();
char    *findtty();
INV     *lookup();

#define SIZ 512
char    host[32];               /* name of this host */
char	buf[SIZ];				/* general-purpose buffer */
extern	int errno;
int		misc;					/* socket used to send out */

/*  Error return values from paging routines  */
#define NOT_HERE   1
#define ERR        3
#define THRESHOLD  (60*10)    /* ten minutes */

#define	PROG		(1<<0)		/* was forwarded to a program        */
#define FORWARD		(1<<1)		/* forwarded to another user/host    */
#define	DONTFORWARD	(1<<2)		/* forwarding failed - don't forward */
#define	NOT_ON		(1<<3)		/* user is not logged on */
#define	MESG_OFF	(1<<4)		/* user is refusing messages */

#define	BUSYFILE	"/.busy"	/* name of forwarding file */

INV		*invitations;			/* list of pending invitations */
INV		*freelist;				/* list of free invite structs */

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'../common.h'" '(1266 characters)'
if test -f '../common.h'
then
	echo shar: will not over-write existing file "'../common.h'"
else
cat << \!Funky!Stuff! > '../common.h'
/*
 *   Defines common to all the parts of the phone system.
 */


#ifndef		ESC
#define		ESC			'\033'		/* precedes all commands */
#endif

#define		ACK			'y'			/* good response code */
#define		NAK			'n'			/* not-so-good code   */

/*
 *   Commands sent from conversation daemon to client.
 */
 
#define		META		0200			/* high bit for command characters */
#define		ADDUSER		(01<<5)			/* add a user to the conversation  */
#define		DELUSER		(02<<5)			/* delete a user from conversation */
#define		UPDATE		(03<<5)			/* set screen update mode */

/*
 *   Commands sent from or master daemon to client.
 */

#define		MESSAGE		'M'				/* following is message text       */

/*
 *   Commands sent by client to conversation or master daemons.
 */

#define		ANSWER		'A'			/* he got the invite, will answer  */
#define		CALLING		'C'			/* daemon is calling the user      */
#define		PAGE		'P'			/* page a user                     */
#define		INQUIRE		'I'			/* inquire as to whether invited   */
#define		REINVITE	'R'			/* renew a request for paging      */
#define		DAEMON		'D'			/* create a daemon for me          */
#define		WHO			'W'			/* tell me who's on ...            */
#define		KILL		'K'			/* cause the daemon to exit        */

#ifndef PORT
#define		PORT	1167
#endif
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'child.c'" '(1702 characters)'
if test -f 'child.c'
then
	echo shar: will not over-write existing file "'child.c'"
else
cat << \!Funky!Stuff! > 'child.c'
#ifndef lint
static char RCSid[] = "$Header: child.c,v 1.1 85/10/28 17:38:11 broome Exp $";
#endif

/*
 * $Log:	child.c,v $
 * Revision 1.1  85/10/28  17:38:11  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"
#include <sys/wait.h>


/*
 *   A more complicated sigchld handler -
 *   looks for the pid in the list of invitations
 *   and sends appropriate status messages to the callers.
 */

sigchld ()
{
	register INV *inv;
	union    wait status;
	char     mbuf[SIZ];
	int      pid;
	int      exitstat;

	while ((pid = wait3 (&status, WNOHANG, 0)) > 0) {	/* any children? */
		if (WIFSTOPPED (status)) {		/* shouldn't happen */
			kill (pid, 9);
			exitstat = 1;
		} else
			exitstat = status.w_retcode;

		for (inv = invitations; inv; inv = inv->next)
			if (inv->pid == pid)		/* does pid match? */
				break;
		if (inv->pid != pid)			/* didn't find child - continue */
			continue;

		if (exitstat && (inv->flags & PROG))		/* their program has problems */
			inv->flags |= DONTFORWARD;
		
		/*
		 *  Now send a message to the user.
		 *  The multiple sprintf()'s aren't very pretty ...
		 */
		
		if (exitstat == 0) {				/* good exit status - ok */
			sprintf (buf, "%s%sing user %s@%s",
						inv->id, (inv->flags & PROG) ? "Forward" : "Ring", 
						inv->callee, host);
			if ((inv->flags & PROG) == 0) {
				strcat (buf, " on ");
				strcat (buf, inv->ptty);
			}
		} else {
			sprintf (buf, "%sCannot ring %s@%s - Unknown error", 
						inv->id, inv->callee, host);
		}
		sprintf (mbuf, "%c%c%c%s", ESC, CALLING, exitstat ? NAK : ACK, buf);
		
		if (sendto (misc, mbuf, strlen (mbuf), 0, &inv->ctladdr,
					sizeof (inv->ctladdr)) < 0)
			perror ("child: sendto");
	}
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'daemon.c'" '(2810 characters)'
if test -f 'daemon.c'
then
	echo shar: will not over-write existing file "'daemon.c'"
else
cat << \!Funky!Stuff! > 'daemon.c'
#ifndef lint
static char RCSid[] = "$Header: daemon.c,v 1.1 85/10/28 17:38:13 broome Exp $";
#endif

/*
 * $Log:	daemon.c,v $
 * Revision 1.1  85/10/28  17:38:13  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <stdio.h>
#include <syslog.h>


/*
 *  The guy wants a daemon, so give him one ...
 */

daemon (addr)
struct sockaddr_in addr;
{
	struct sockaddr_in sin;		/* address of new daemon */
	extern char myaddr[];		/* address of this host */
	char   *error();
	int    sock;
	int    pid;
	int    i, len;

	if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		sprintf (buf, "%c%c%cCannot create socket: %s", 
					ESC, DAEMON, NAK, error());
		sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		return;
	}

	i = 1;
	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i)))
		syslog (LOG_ERR, "daemon: setsockopt: %m");

	bzero ((char *)&sin, sizeof (sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	sin.sin_family = AF_INET;

	len = sizeof (sin);
	if (bind (sock, &sin, len) < 0) {
		sprintf (buf,"%c%c%cCannot bind socket: %s", ESC, DAEMON, NAK, error());
		sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		return;
	}

	if (pid = fork ()) {		/* parent */
		if (pid == -1) {		/* failed */
			sprintf (buf, "%c%c%cFork failed: %s", ESC, DAEMON, NAK, error());
			sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		} 
		(void) close (sock);
		return;
	}
	
	len = sizeof (sin);
	if (getsockname (sock, &sin, &len) < 0) {
		sprintf (buf, "%c%c%cCannot get socket name: %s", 
					ESC, DAEMON, NAK, error());
		(void) sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		_exit (1);
	}

	/* life is good */
	sprintf (buf,"%c%c%c%s/%d", ESC, DAEMON, ACK, myaddr, ntohs (sin.sin_port));
	sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));

	listen (sock, 5);

	if (sock != 0)
		if (dup2 (sock, 0)) {		/* set socket to be stdin */
			perror ("dup2");
			syslog (LOG_ERR, "daemon: dup2 failed: %m");
		}

	for (i = 1; i < getdtablesize(); i++)	/* close anything else */
		(void) close (i);
	
#ifdef DPATH		/* sure hope this is it! */
	execl (DPATH, "convd", 0);
#else !DPATH
	execl ("/usr/local/lib/convd", "convd", 0);
	execl ("/usr/lib/convd", "convd", 0);
	execl ("/etc/convd", "convd", 0);
#endif DPATH

#ifdef DPATH
	syslog (LOG_ERR, "cannot execl %s: %m", DPATH);
#else
	syslog (LOG_ERR, "cannot execl convd: %m");
#endif

	sprintf (buf, "%c%c%cExecl failed: %s", ESC, DAEMON, NAK, error());
	sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
	_exit (-99);
}


/*
 *  Return a string with the error message.
 */

char *
error ()
{
	extern int  errno;
	extern char *sys_errlist[];
	extern int  sys_nerr;

	if (errno > sys_nerr)
		return ("Unknown error.");
	else
		return (sys_errlist[errno]);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'dopage.c'" '(1924 characters)'
if test -f 'dopage.c'
then
	echo shar: will not over-write existing file "'dopage.c'"
else
cat << \!Funky!Stuff! > 'dopage.c'
#ifndef lint
static char RCSid[] = "$Header: dopage.c,v 1.1 85/10/28 17:38:16 broome Exp $";
#endif

/*
 * $Log:	dopage.c,v $
 * Revision 1.1  85/10/28  17:38:16  broome
 * Initial revision
 */


#include "../common.h"
#include "defs.h"
#include <sys/stat.h>

/*
 *   Handle paging one user - one invitation.
 *   Checks for ~/.busy, tries to do 
 *   the right thing.
 */

_dopage (inv)
INV    *inv;
{
	FILE     *fp;			/* forward file   */
	char     *tty;			/* tty they're on */
	int      pid;			/* child process  */
	int      mode;			/* tty status     */
	struct   stat statb;

	/*
	 *  We won't check for forwarding if it has failed already.
	 *  We also make sure that home isn't null - otherwise unknown
	 *  users might be able to take advantage of root somehow ...
	 */

	if ((inv->flags & DONTFORWARD) == 0 && inv->home) {
		strcpy (buf, inv->home);
		strcat (buf, BUSYFILE);
		if (stat (buf, &statb) == 0 && (statb.st_mode & (04<<3)) &&
		  statb.st_uid == inv->uid && (fp = fopen (buf, "r"))) {
			/* file exists */
			while (fgets (buf, SIZ, fp))		/* read a line */
				if (*buf != '\n' && *buf != '#')
					break;
			fclose (fp);
			if (*buf == '/' || *buf == '~')		/* path to program */
				return (forward_program (buf, inv));	/* so invoke it */
		}
	}

	inv->flags &= ~PROG;

	/* 
	 *  If we're here, we didn't forward it, so look for them
	 *  on a tty and send a message to their terminal.
	 */
	
	tty = findtty (inv->callee, inv->tty, &mode);
	if (mode == NOT_ON)
		return (NOT_ON);
	
	if (inv->ptty)
		free (inv->ptty);
	inv->ptty = strsave (tty+5);	/* save name of tty being paged */

	if (mode == MESG_OFF)
		return (MESG_OFF);
	
	if (pid = fork ()) {		/* parent */
		if (pid == -1) {			/* fork failed */
			perror ("fork");
			inv->rings = 0;		/* so try again next time */
		} else
			inv->pid = pid;		/* save child's id */
		return (0);
	}
	pagetty (inv, tty);			/* child - do it */
	/*NOTREACHED*/
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'forward_program.c'" '(3308 characters)'
if test -f 'forward_program.c'
then
	echo shar: will not over-write existing file "'forward_program.c'"
else
cat << \!Funky!Stuff! > 'forward_program.c'
#ifndef lint
static char RCSid[] = "$Header: forward_program.c,v 1.1 85/10/28 17:38:17 broome Exp $";
#endif

/*
 * $Log:	forward_program.c,v $
 * Revision 1.1  85/10/28  17:38:17  broome
 * Initial revision
 */

#include "defs.h"
#include <pwd.h>

/*
 *  Take the pathname of a forwarding program
 *  and start it up.
 *
 *  Features: the ability to give a printf-like
 *  format string to fill in with the caller and such.
 *
 *		"%R" - name of recipient 
 *		"%C" - name of caller
 *      "%H" - caller's host
 */

forward_program (buf, inv)
char *buf;
INV  *inv;
{
	int      pid;			/* child process               */
	int      fd;			/* fds to close                */
	int      argc;			/* count of words in command   */
	char     *argv[32];		/* command in forward file     */

	inv->flags |= PROG;				/* mark as being piped */
	if (pid = fork()) {				/* parent returns immediately */
		if (pid == -1)				/* fork failed */
			inv->rings = 0;			/* try again next time */
		else
			inv->pid = pid;			/* save process id */
		return (0);
	}

	/*
	 *  We're the child process, so clean up 
	 *  and exec the program.
	 */

	sigblock (0);					/* ignore all signals */

	if (argc = expand (buf, argv, inv)) {		/* it contains something */

		setgid (inv->gid);				/* set up permissions */
		initgroups (inv->callee, inv->gid);
		setuid (inv->uid);				/* ... fix security   */
		for (fd = 0; fd < getdtablesize(); fd++)	/* clean up */
			(void) close (fd);
		execv (argv[0], argv);			/* and do it */
		_exit (-99);					/* bad format?? */
	}
}



/*
 *  Given a line from the .phonerc file, expand ~user and also '%'
 *  substitutions (like printf) and parse into an argument vector.
 *
 *  We use a static buffer to stick the string sinto, so as to 
 *  avoid the malloc/free-in-interrupt routine problem.
 *
 *  The allowed '%' substitutions are:
 *
 *		"%R" - name of the recipient.
 *		"%C" - name of the calling party.
 *		"%H" - calling party's host.
 */

expand (inbuf, argp, inv)
char  *inbuf;
char  **argp;
INV   *inv;
{
	struct		passwd *pwd;
	static		char outbuf[10240];
	register	char *i;
	register	char *o;
	register	char *n;
	char		*start;
	char		name[32];
	int			first;
	char		**ap;

	i = inbuf;
	o = outbuf;
	ap = argp;

	while (*i) {
		start = o;		/* save front of this word */

		while (*i && *i == ' ' || *i == '\t')
			i++;
		
		for (first = 1; *i && *i != ' ' && *i != '\t'; i++) {
			if (*i == '~' && first) {		/* ~user */
				for (i++, n = name; *i && *i != '/' && *i != ' ' && *i != '\t';)
					*n++ = *i++;
				i--;
				*n = '\0';
				if (*name == '\0') {	/* use $HOME */
					n = inv->home;
				} else {				/* lookup user in passwd file */
					if (pwd = getpwnam (name))
						n = pwd->pw_dir;
					else
						n = (char *) 0;
				}
				while (n && *n)		/* copy dir over */
					*o++ = *n++;
			} else if (*i == '%') {		/* do printf-like stuff */
				switch (*++i) {
					case 'R':	n = inv->callee;	/* recipient */
								break;
					case 'C':	n = inv->caller;	/* caller */
								break;
					case 'H':	n = inv->host;		/* calling host */
								break;
					case '%':	n = "%";			/* normal percent */
								break;
				}
				while (n && *n)
					(*o++ = *n++);
			} else
				*o++ = *i;
		}
		*o++ = '\0';
		if (*start != '\0')
			*ap++ = start;
	}
	*ap = (char *) 0;
	return (ap - argp);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'inquire.c'" '(1200 characters)'
if test -f 'inquire.c'
then
	echo shar: will not over-write existing file "'inquire.c'"
else
cat << \!Funky!Stuff! > 'inquire.c'
#ifndef lint
static char RCSid[] = "$Header: inquire.c,v 1.1 85/10/28 17:38:21 broome Exp $";
#endif

/*
 * $Log:	inquire.c,v $
 * Revision 1.1  85/10/28  17:38:21  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"

/*
 *  Check to see if there are any pending calls for this user,
 *  send back the first address and delete the invite if any are found.
 */

inquire (argv, sin)
char   *argv[];
struct sockaddr_in sin;
{
	INV  *inv;

	if (inv = lookup (argv[0], argv[1])) {		/* had one pending */
		sprintf (buf, "%c%c%c%s", ESC, INQUIRE, ACK, inv->convaddr);
		delete (inv);
	} else
		sprintf (buf, "%c%c%cNo messages pending.", ESC, INQUIRE, NAK);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
}


/*
 *  They say they answered the call, so delete it from the list.
 *  This routine is for future use - not used now ...
 */

answer (argv)
char   *argv[];
{
	INV *inv;
	
	for (inv = invitations; inv; inv = inv->next)
		if (eq (argv[0], inv->callee) && eq (argv[1], inv->convaddr)) {
			sprintf (buf, "%c%c%c%s", ESC, ANSWER, ACK, inv->id);
			(void) sendto (misc, buf, strlen(buf), 0, &inv->ctladdr, 
				sizeof (inv->ctladdr));
			delete (inv);
			return;
		}
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'list.c'" '(3769 characters)'
if test -f 'list.c'
then
	echo shar: will not over-write existing file "'list.c'"
else
cat << \!Funky!Stuff! > 'list.c'
#ifndef lint
static char RCSid[] = "$Header: list.c,v 1.1 85/10/28 17:38:22 broome Exp $";
#endif

/*
 *  Routines for managing the list of pending calls, including
 *  creating and looking for invitations.
 */

/*
 * $Log:	list.c,v $
 * Revision 1.1  85/10/28  17:38:22  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>



static int  requests = 0;              /* number of pending invitations */

/*
 *  Insert a request into the pending list.
 */

insert (inv)
INV    *inv;
{
	/* insert into top of list */
	if (invitations == NIL) {
		inv->next         = NIL;
		invitations       = inv;
	} else {
		inv->next         = invitations;
		invitations->prev = inv;
		invitations       = inv;
	}

	if (requests++ == 0)    /* need to set alarm interrupt */
		alarm (1);			/* so start up alarm */
}


/*
 *  Called on SIGALRM to process pending calls ---
 *  go through the list of pending invitations, removing any old
 *  ones, trying to ring the rest.
 */

ring ()
{
	register	INV   *inv;
	register	INV   *next;

	if (!requests)   /* no pending requests **/
		return;

	readutmp ();	/* reread /etc/utmp */

	for (inv = invitations; inv; inv = next) {
		next = inv->next;                	/* save the next one          */
		if (inv->rings++ == 0)				/* time to page this one ???  */
			dopage (inv);               	/* I guess so ...             */
		else if (inv->rings > 30)			/* more than five minutes old */
			delete (inv);
	}
	if (requests > 0)                        /* any requests left ???     */
		alarm (5);                           /* set the next alarm        */
#ifdef INET		  /* if running under inetd, exit when no more work to do */
	else
		exit (0);
#endif INETD
}


/*
 *  Page the given invitation and return a status message.
 *  Notice the incredible amount of indirection going on here -
 *  up to three (or is it four) levels !! Have to clean this up 
 *  when we get forwarding working properly.
 */

dopage (inv)
INV    *inv;
{
	char  rbuf[10];
	int   ret;
	char  *name;
	char  *tty;

	name = inv->callee;
	ret = _dopage (inv);

	sprintf (rbuf, "%c%c%c%s", ESC, CALLING, NAK, inv->id);

	if (ret == 0)		/* all looks to be good */
		return;

	if (ret == MESG_OFF)
		sprintf (buf, "%s%s@%s is refusing messages", rbuf, name, host);
	else if (ret == NOT_ON)
		sprintf (buf, "%s%s@%s is not logged in", rbuf, name, host);

	if ((tty = inv->tty) && *tty) {
		strcat (buf, " on ");
		strcat (buf, tty);
	}

	delete (inv);
	sendto (misc, buf, strlen (buf), 0, &inv->ctladdr, sizeof (inv->ctladdr));
}


/*
 *  Check to see if the named user is being invited by the right person.
 *  Returns a pointer to the invitation in question.
 *
 *  A wildcard "*" is acceptable as the caller name - this is 
 *  useful for answering machine programs.
 */

INV *
lookup (callee, caller)
char   *callee, *caller;
{
	register	INV  *inv;
	register	int  all = (eq (caller, "*"));

	for (inv = invitations; inv; inv = inv->next) {
		if (all || eq (inv->caller, caller))     /* caller match */
			if (eq (callee, inv->callee))        /* callee match */
				return (inv);
	}
	return (NIL);
}


/*
 *  The usual linked-list deletion routine, with a minor
 *  difference - instead of deallocating the space, we simply
 *  place the element on a free list for future use (LIFO form)
 */

delete (ptr)
INV *ptr;
{
	requests--;                      	/* decrement number pending  */
	if (ptr->prev)
		ptr->prev->next = ptr->next;	/* set previous's next pointer */ 
	else 
		invitations = ptr->next;

	if (ptr->next)
		ptr->next->prev = ptr->prev;	/* set next's previous pointer */
	
	ptr->next = freelist;				/* add on tail of free list */
	freelist = ptr;						/* and make this the top */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'main.c'" '(4574 characters)'
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
cat << \!Funky!Stuff! > 'main.c'
#ifndef lint
static char RCSid[] = "$Header: main.c,v 1.1 85/10/28 17:38:28 broome Exp $";
#endif

/*
 * $Log:	main.c,v $
 * Revision 1.1  85/10/28  17:38:28  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <netdb.h>

#ifdef FORK
#include <sys/ioctl.h>
#endif



char	myaddr[20];			/* internet host address in ascii dot notation */

/*
 *   Master phone daemon, sits on known socket address and
 *   listens for requests...  Handles invitations, acts as
 *   central clearinghouse for conversations.
 */

main (argc, argv)
int   argc;
char *argv[];
{
#if defined(SERVICES) && !defined(INETD)
	struct   servent *sp, *getservbyname();
#endif
	struct   sockaddr_in  sin;
	struct   hostent *hp, *gethostbyname();
	extern   int   ring();
	extern   int   sigchld();
	int      sock;
#ifndef INETD
	int      port = PORT;
#endif
	int      tty, i;

	invitations = NIL;
	freelist    = NIL;

#ifdef INETD
	if (argc != 1) {
		fprintf (stderr, "%s takes no options!\n", argv[0]);
		exit (1);
	}
#else  !INETD
	if (argc > 2) {
		fprintf (stderr, "Usage: %s [ port# ]\n", argv[0]);
		exit (1);
	}
#endif INETD

#ifndef INETD
	if (argc == 2) {
		if ((port = atoi (argv[1])) == 0) {
			fprintf (stderr, "Bad port number: %s\n", argv[1]);
			exit (1);
		}
	}
#endif INETD
#if defined(SERVICES) && !defined(INETD)
	else {
		if ((sp = getservbyname ("phone", "udp")) == (struct servent *) 0) {
			fprintf (stderr, "phone/udp: unknown service.\n");
			exit (1);
		}
		port = sp->s_port;
	}
#endif SERVICES

#ifndef INETD
	/*  Open and initialize the socket we will take requests on  */
	if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror ("Cannot create datagram socket");
		exit (2);
	}
	bzero ((char *)&sin, sizeof (sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons (port);
	sin.sin_family = AF_INET;

	if (bind (sock, &sin, sizeof (sin))) {
		perror ("Cannot bind datagram socket");
		exit (3);
	}
#else INETD
	sock = 0;			/* inetd hands us the first packet on stdin */
#endif INETD

	/*  Initialize the work socket as well  */
	if ((misc = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror ("Cannot create datagram work socket");
		exit (4);
	}
	bzero ((char *)&sin, sizeof (sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	sin.sin_family = AF_INET;
	if (bind (misc, &sin, sizeof (sin))) {
		perror ("Cannot bind datagram work socket");
		exit (5);
	}

	signal (SIGCHLD, sigchld);
	signal (SIGALRM, ring);
	gethostname (host, 32);

	if ((hp = gethostbyname (host)) == (struct hostent *) 0) {
		fprintf (stderr, "%s: cannot find my own address!!!\n", argv[0]);
		exit (6);
	}
	bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
	strcpy (myaddr, inet_ntoa (sin.sin_addr.s_addr));

#if defined(FORK) && !defined(INETD)
	if (fork ())
		exit (0);

	if (sock != 0)
		close (0);
	i = open ("/dev/null", 0);
	if (sock != 1)
		dup2 (i, 1);
	if (sock != 2)
		dup2 (i, 2);

	if ((tty = open ("/dev/tty", 0)) != -1) {
		ioctl (tty, TIOCNOTTY);
		close (tty);
	} else 
		setpgrp (0, getpid ());
#endif FORK && !INETD

#ifdef LOG_ODELAY
	openlog ("phoned", LOG_PID | LOG_ODELAY, 0);
#else
	openlog ("phoned", LOG_PID, 0);
#endif
	openutmp ();

	service (sock);
	exit (0);
}


/*
 *  Main service routine.
 *  Listen on the socket, process requests.
 */

service (sock)
register int sock;
{
	int 		mask;		/* can't be in a register ... darn */
	int 		len;
	register	int  omask;
	register	int  rval;
	register	char *av[10];
	register	char buf[SIZ];
	register	int  i;
	struct		sockaddr_in sin;

	omask = 1 << sock;
	for ( ;; ) {
		mask = omask;
		if (select (32, &mask, 0, 0, 0) <= 0)
			continue;
		len = sizeof (sin);
		if ((rval = recvfrom (sock, buf, SIZ, 0, &sin, &len)) <= 0) {
			syslog (LOG_ERR, "recvfrom: %m");
			continue;
		}
		if (*buf != ESC)
			continue;
		buf[rval] = '\0';
		parse (buf+2, av);
		switch (buf[1]) {		/* figure out command */
			case PAGE:		page (av, sin);		break;
			case REINVITE:	reinvite (av, sin);	break;
			case INQUIRE:	inquire (av, sin);	break;
			case ANSWER:	answer (av);		break;
			case DAEMON:	daemon (sin);		break;
			case WHO:		who (sin);			break;
			case KILL:		exit (0);			break;
		}
	}
}


/*
 *  Parse the buffer into an argument vector.
 */

parse (buf, argv)
char  *buf;
char  **argv;
{
	register	char **ap;
	register	char *b;

	ap = argv;

	for (b = buf; b && *b && *b != '\n'; ) {
		*ap++ = b;
		for ( ; b && *b && *b != ':' && *b != '\n'; b++)
			;
		*b++ = '\0';
	}
	*ap = (char *) 0;
	return (ap - argv);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'page.c'" '(3334 characters)'
if test -f 'page.c'
then
	echo shar: will not over-write existing file "'page.c'"
else
cat << \!Funky!Stuff! > 'page.c'
#ifndef lint
static char RCSid[] = "$Header: page.c,v 1.1 85/10/28 17:38:29 broome Exp $";
#endif

/*
 * $Log:	page.c,v $
 * Revision 1.1  85/10/28  17:38:29  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <netdb.h>
#include <pwd.h>
#include <sys/time.h>

/*
 *   He wants us to page someone...
 *
 *   argv points to array like this: "callno : callee:tty:caller:conv_addr"
 *
 *   If we already have a call from the same address with the same call
 *   number then we assume it's a retransmission and just resend the
 *   same message-id (the one generated by us) and hope it will make 
 *   it to them.
 *
 *   We try to get the user's password entry so that we can
 *   look for a .busy forwarding file when we actually ring him.
 */

page (argv, sin)
char   *argv[];
struct sockaddr_in sin;
{
	long     now;
	register INV    *new;
	INV		 *seenit();
	struct   passwd  *pw, *getpwnam();
	struct   hostent *hp, *gethostbyaddr();

	/* first check to see if we already have the request */
	if (new = seenit (argv[0], sin)) {
		(void) sprintf (buf, "%c%c%c%s", ESC, PAGE, ACK, new->id);
		(void) sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
		return;
	}

	if (freelist) {
		new = freelist;			/* grab one from existing list */
		freelist = new->next;
		free (new->caller);		/* deallocate space from last time */
		free (new->callee);
		free (new->host);
		free (new->home);
		free (new->tty);
		free (new->convaddr);
		if (new->ptty)
			free (new->ptty);
	} else {			/* need to malloc new space */
		if ((new = (INV *) malloc (sizeof (INV))) == (INV *) 0) {
			perror ("malloc failed...");
			exit (1);
		}
	}

	new->callno   = strsave (argv[0]);		/* caller's form of call id */
	new->callee   = strsave (argv[1]);		/* person being called */
	new->tty      = strsave (argv[2]);		/* his tty */
	new->caller   = strsave (argv[3]);		/* caller name */
	new->convaddr = strsave (argv[4]);		/* conversation address */

	time (&now);

	/*  
	 *  Acknowledge immediately. We make an id 
	 *  from the lower 4 bits of the time.  
	 */

	sprintf (new->id, "%05ld", (now & 9999L));
	sprintf (buf, "%c%c%c%s", ESC, PAGE, ACK, new->id);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));

	/* figure out host name */
	if (hp = gethostbyaddr (&sin.sin_addr, sizeof (struct in_addr), AF_INET))
		new->host = strsave (hp->h_name);
	else
		new->host = strsave (inet_ntoa (sin.sin_addr.s_addr));

	/* save control address */
	bcopy ((char *)&sin, (char *)&new->ctladdr, sizeof (struct sockaddr_in));

	/* lookup callee in password file */
	if (pw = getpwnam (argv[0])) {
		new->home = strsave (pw->pw_dir);
		new->uid  = pw->pw_uid;
		new->gid  = pw->pw_gid;
	} else
		new->home = (char *) 0;

	new->rings  = 0;		/* so they get it next time around */
	new->ptty   = (char *) 0;
	new->prev   = NIL;

	insert (new);			/* and add to the pending list */
}


/*
 *  Search through the list of invitations, looking for one
 *  from the same address as this, with the same call number as well.
 */

INV *
seenit (callno, addr)
char   *callno;
struct sockaddr_in addr;
{
	register	INV	*inv;

	for (inv = invitations; inv; inv = inv->next)
		if (strcmp (callno, inv->callno) == 0 && 
			bcmp ((char *)&addr, (char *)&inv->ctladdr, 
			sizeof (struct sockaddr_in)) == 0)
				return (inv);
	return (NIL);
}

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'pagetty.c'" '(1602 characters)'
if test -f 'pagetty.c'
then
	echo shar: will not over-write existing file "'pagetty.c'"
else
cat << \!Funky!Stuff! > 'pagetty.c'
#ifndef lint
static char RCSid[] = "$Header: pagetty.c,v 1.1 85/10/28 17:38:31 broome Exp $";
#endif

/*
 * $Log:	pagetty.c,v $
 * Revision 1.1  85/10/28  17:38:31  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <sys/time.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/file.h>


/*
 *  Page a local user to his terminal. 
 *  We compose the message, then send it 
 *  in one big block so as to minimise messing
 *  him up if he's in vi or the like.
 */

pagetty (inv, tty)
INV  *inv;
char *tty;
{
	struct  tm   *tm;
	long    now;
	char    buf[100], mesg[300];
	int     fd;		/* tty file descriptor */

	time (&now);
	tm = localtime (&now);

	/*  Now to compose the message  */
#ifdef notdef
	sprintf (buf, "\r\n\7Message from Phone_Daemon@%s at %d:%02d ...\r\n", 
				host, tm->tm_hour, tm->tm_min);
#endif
	sprintf (buf, "\r\n\7Message from the Telephone_Operator@%s at %d:%02d ...\r\n", 
				host, tm->tm_hour, tm->tm_min);
	strcpy (mesg, buf);

	sprintf (buf, "phone: connection requested by %s@%s\r\n", 
				inv->caller, inv->host);
	strcat (mesg, buf);

	sprintf (buf, "phone: respond with \"phone %s", inv->caller);
	strcat (mesg, buf);

	/* only list host if it differs from our own */
	if (strcmp (inv->host, host)) {
		strcat (mesg, "@");
		strcat (mesg, inv->host);
	}

	strcat (mesg, "\"\r\n\r\n\7");

	/*  And send it  */
	if ((fd = open (tty, O_WRONLY, 0444)) < 0) {   /* shouldn't happen */
		syslog (LOG_ERR, "phoned: can't open %s: %m", tty);
		_exit (-99);
	}
	
	(void) write (fd, mesg, strlen (mesg));
	(void) close (fd);

	_exit (0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'reinvite.c'" '(773 characters)'
if test -f 'reinvite.c'
then
	echo shar: will not over-write existing file "'reinvite.c'"
else
cat << \!Funky!Stuff! > 'reinvite.c'
#ifndef lint
static char RCSid[] = "$Header: reinvite.c,v 1.1 85/10/28 17:38:35 broome Exp $";
#endif

/*
 * $Log:	reinvite.c,v $
 * Revision 1.1  85/10/28  17:38:35  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"

/*
 *  Reinvite the given invitation by resetting the `rings' field to zero.
 */

reinvite (argv, sin)
char   **argv;
struct sockaddr_in sin;
{
	register	INV *inv;
	register	int found = 0;

	for (inv = invitations; inv; inv = inv->next) {
		if (strcmp (inv->id, *argv) == 0 && 
		  bcmp ((char *)&sin, (char *)&(inv->ctladdr), sizeof (sin)) == 0) {
			inv->rings = 0;
			found = 1;
			break;
		}
	}
	sprintf (buf, "%c%c%c%s", ESC, REINVITE, found ? ACK : NAK, *argv);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'strsave.c'" '(407 characters)'
if test -f 'strsave.c'
then
	echo shar: will not over-write existing file "'strsave.c'"
else
cat << \!Funky!Stuff! > 'strsave.c'
#ifndef lint
static char RCSid[] = "$Header: strsave.c,v 1.1 85/10/28 17:38:36 broome Exp $";
#endif

/*
 * $Log:	strsave.c,v $
 * Revision 1.1  85/10/28  17:38:36  broome
 * Initial revision
 * 
 */

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

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

	if (new = malloc (strlen (s) + 1))
		strcpy (new, s);
	return (new);
}

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

#ifndef lint
static char RCSid[] = "$Header: utmp.c,v 1.1 85/10/28 17:38:37 broome Exp $";
#endif

/*
 * $Log:	utmp.c,v $
 * Revision 1.1  85/10/28  17:38:37  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"
#include <sys/stat.h>
#include <sys/file.h>
#include <utmp.h>


/*
 *   Routines for dealing with /etc/utmp.
 *
 *   We use a statically-allocated array because readutmp() is
 *   called at interrupt-level and we don't want to mess up malloc()
 *   and free as a result.
 */

#ifndef	LINELEN				/* length of line for "who()" */
#define	LINELEN		76		/* not 80 - for magic-cookie terminals */
#endif

#ifndef	MAXUSERS
#define	MAXUSERS	64
#endif

static	struct	utmp utbuf[MAXUSERS];				/* contents of file */
static	int		numents;							/* number of users on */
static	int		utfd;								/* file descriptor */


/*
 *  Initialize by opening the file and malloc'ing space.
 */

openutmp ()
{
	register	int i;
	struct		stat statb;

	if ((utfd = open ("/etc/utmp", O_RDONLY)) < 0) {
		perror ("can't open utmp");
		return;		/* exit?? */
	}
}


/*
 *  Reread /etc/utmp from the open file descriptor into the buffer.
 *  We test/set the "here" flag so we don't try to read at interrupt level
 *  if we're already doing it normally at the same time. (in "who()")
 */

readutmp ()
{
	static	int	here = 0;
	int	cc;

	if (here)
		return;
	here = 1;
	lseek (utfd, 0L, 0);	/* rewind */
	cc = read (utfd, utbuf, sizeof (utbuf));	/* and read */
	numents = cc / sizeof (struct utmp);
	here = 0;
}



/*
 *  Go through the utmp buffer, trying to find the named user,
 *  possibly with the tty specified.
 */

char *
findtty (user, tty, mode)
char *user;
char *tty;
int  *mode;
{
	static 		char   ttybuf[15];
	register	int    i;
	register	struct utmp *utmp;
	struct		stat   statb;

	*mode = NOT_ON;							/* start by assuming he's not on */
	for (i = 0; i < numents; i++) {
		utmp = &utbuf[i];
		if (*utmp->ut_name == '\0')              /* noone on this port */
			continue;
		if (strncmp (utmp->ut_name, user, 8))    /* names don't match */
			continue;
		if (tty && *tty && strncmp (tty, utmp->ut_line, 8)) /* not spec'd tty */
			continue;
		strcpy  (ttybuf, "/dev/");
		strncat (ttybuf, utmp->ut_line, 8);
		if (stat (ttybuf, &statb))	/* error on tty? */
			continue;
		if ((statb.st_mode & 02) == 0) {
			*mode = MESG_OFF;		/* refusing messages */
			if (tty && *tty)		/* they specified a particular tty */
				break;
			else
				continue;			/* see if we can find another one */
		} else {					/* all is okay, use this one */
			*mode = 0;
			return (ttybuf);
		}
	}
	return ((char *) 0);
}


/*
 *  Send a "who" message to the given address ...
 *  We go through the utmp buffer, building LINELEN-long buffers
 *  and send them on over to the user.
 */

who (sin)
struct sockaddr_in sin;
{
	register	struct	utmp *utmp;
	register	int		i;
	register	int		ulen;
	register	int		len = LINELEN + 1;
	register	int		users = 0;
	char		buf[LINELEN+5];		/* buffer for whole message */
	char		ubuf[20];			/* buffer for one user and tty */

#ifdef NO_WHO		/* not allowed here ... */
	sprintf (buf, "%c%c%cwho@%s: this site doesn't allow remote who.", 
				ESC, NAK, WHO, host);
	(void) sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
	return;
#else !NO_WHO	/* not so paranoid here */

	sprintf (buf, "%c%c%cwho info coming...", ESC, WHO, ACK);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));

	readutmp ();

	buf[0] = '\0';
	for (i = 0; i < numents; i++) {
		utmp = &utbuf[i];
		if (*utmp->ut_name == '\0')		/* noone on that line */
			continue;
		sprintf (ubuf, " %.8s(%.5s)", utmp->ut_name, utmp->ut_line);
		ulen = strlen (ubuf);
		if (ulen + len + 1 < LINELEN)
			strcat (buf, ubuf);
		else {
			if (users)
				(void) sendto (misc, buf, len+4, 0, &sin, sizeof (sin));
			sprintf (buf, "%c%c%cwho@%s:", ESC, MESSAGE, ACK, host);
			len = strlen (buf) - 4;
			strcat (buf, ubuf);
		}
		len += ulen;
		users++;
	}
	if (users == 0) {
		sprintf (buf, "%c%c%c%s: Noone logged on.", 
				ESC, MESSAGE, ACK, host);
		sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
	} else if (len)
		(void) sendto (misc, buf, len+4, 0, &sin, sizeof (sin));

#endif NO_WHO
}
!Funky!Stuff!
fi # end of overwriting check
#	End of shell archive
exit 0

broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)

#-----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
#	convd.c
# This archive created: Sat Dec 28 01:11:14 1985
export PATH; PATH=/bin:$PATH
mkdir convd
cd convd
echo shar: extracting "'Makefile'" '(1515 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'
#
#  Makefile for phone conversation daemon        20 December 1985
#

# This one is pretty straightforward - no special flags for it.

CFLAGS	=	-O
SRCS	=	convd.c
DEST	=	convd
RDEST	=	/usr/local/lib/convd

all: ${DEST}

${DEST}:	${SRCS}
	/bin/rm -f ${DEST}
	cc ${CFLAGS} -o ${DEST} ${SRCS}

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

clean:
	/bin/rm -f ${DEST} core *.o

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

dist: ${DEST}
	-cp  ${DEST} ${RDEST}
	-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

convd.o: /usr/include/errno.h

convd.o: /usr/include/sys/ttydev.h

convd.o: /usr/include/sys/ttychars.h

convd.o: /usr/include/sys/ioctl.h

convd.o: /usr/include/signal.h

convd.o: /usr/include/netdb.h

convd.o: /usr/include/netinet/in.h

convd.o: /usr/include/sys/socket.h

convd.o: /usr/include/sys/uio.h

convd.o: /usr/include/sys/types.h

convd.o: /usr/include/stdio.h

convd.o: ./../common.h
 
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see depend: above
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'convd.c'" '(9758 characters)'
if test -f 'convd.c'
then
	echo shar: will not over-write existing file "'convd.c'"
else
cat << \!Funky!Stuff! > 'convd.c'
#define	KERNEL_BUG

#ifndef lint
static char RCSid[] = "$Header: convd.c,v 1.1 85/10/29 14:20:06 broome Exp $";
#endif


/*
 *   The conversation daemon --- does all the main work 
 *   for one conversation.  It is invoked with stdin (fd 0)
 *   on the service port; it listens for requests there and
 *   does the right thing.
 *
 *   See the comments in ../client/readstream.c for an
 *   explanation of the command encoding scheme used here.
 *
 *   NOTE: this code relies heavily upon the writev() system call
 *   which provides for scatter/gather arrays of data, thus allowing
 *   us to to write out multiple arrays of characters in a single
 *   system call, thus avoiding having to copy data from one buffer
 *   to another.
 *
 *   Also ... note that we don't use slot #0 - the client program
 *   wants to remap the user's window into slot zero, so we help
 *   out by never assigning *anyone* that slot.
 */


/*
 * $Log:	convd.c,v $
 * Revision 1.1  85/10/29  14:20:06  broome
 * Initial revision
 */


#include "../common.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <errno.h>

#define BUFFER    128				/* size of char buffer to use     */
#define MAXSLOTS  32                /* max users/conversation         */

struct slot {
	int    inuse;                   /* this slot in use?              */
	char   *info;                   /* user's login, host, tty, etc.  */
	char   buffer[BUFFER];			/* text buffer                    */
	int    new;						/* index of most recent character */
	int    old;						/* index of oldest character      */
	int    fd;                      /* open stream file descriptor    */
	int    mask;					/* mask set on this file descrip. */
} slots[MAXSLOTS];        			/* all users in this conversation */

extern	int errno;
int		users;						/* number of users on             */
int		stayaround;					/* still waiting for first users  */
int		currslot;					/* current slot                   */
int		highslot;					/* highest slot number in use     */


#ifdef	KERNEL_BUG
int		warned = 0;
#endif

#define SIZ BUFFER					/* read this many chars from clients */
char	buf[SIZ];
char	*strsave();
char	*malloc();
int		sigalrm();


main ()
{
	register	struct slot *cslot;
	register	char *c;
	register	int  i;
	register	int  sl;
	register	int  fds;
	register	int  r;
	register	int  changed;
	static		int  new, old;
	struct		iovec iov[2];
	char 		recvbuf[SIZ];
	char		sel[1];		/* for selecting current window */
	int  		mask;		/* saved mask ... */
	int			rmask;		/* munged by select() */
	int			ind;

#ifdef NICE
	(void) nice (-3);		/* get a little bit of priority */
#endif

	users = 0;             /*  noone here yet */
	stayaround = 1;
	currslot = -1;
	highslot = -1;
	changed = 0;

	signal (SIGPIPE, SIG_IGN);	/* we'll find out soon enough */
	signal (SIGALRM, sigalrm);	/* to handle timeout */
	alarm (60 * 60);			/* go away if noone home */
	initslots ();         	 	/* clean everything out first */
	mask = 1 << 0;				/* stdin is service socket */

	iov[0].iov_base = sel;
	iov[0].iov_len = 1;
	iov[1].iov_base = recvbuf;

	do {
		rmask = mask;
		if ((fds = select (32, &rmask, 0, 0, 0)) <= 0)
			continue;
		if (rmask & (1 << 0))					/* service port */
			service (&mask);					/* let it modify mask */

		for (sl = 0; sl <= highslot && fds; sl++) {     /* client port */
			cslot = &slots[sl];
			if (cslot->inuse == 0)
				continue;
			if (rmask & cslot->mask) {			/* on this slot */
				fds--;							/* decrement slots to check */
				if ((r = read (cslot->fd, recvbuf, SIZ)) <= 0) {   /* EOF */
					mask &= ~(cslot->mask);		/* remove from mask */
					deluser (sl);
					if (sl == currslot)			/* have to switch windows */
						currslot = -1;			/* just in case ... */
				} else {
					iov[1].iov_len = r;
					new = cslot->new;			/* index of where to add */
					old = cslot->old;			/* index of oldest char  */
					c = &cslot->buffer[new];	/* so point to newest    */
					for (i = 0; i < r; i++) {
						*c++ = recvbuf[i];
						new++;
						if (new == BUFFER) {	/* at end of buffer    */
							new = 0;			/* so loop back around */
							c = cslot->buffer;
						} else if (new == old) {	/* full buffer         */
							old++;					/* so advance the end  */
							if (old == BUFFER)		/* wrapped around here */
								old = 0;
						}
					}
					cslot->new = new;  cslot->old = old; 	/* save pointers */

					if (sl != currslot) {     /* switch to this slot */
						sel[0] = (META | sl);
						currslot = sl;
						ind = 0;
					} else
						ind = 1;
					for (i = 0; i <= highslot; i++)		/* ship out to others */
						if (slots[i].inuse)
							(void) writev (slots[i].fd, &iov[ind], 2-ind);
				}
			}
		}
	} while ((users > 1) || (stayaround == 1));

	shutdown (0, 2);
	exit (17);
}



/*
 *  Set all the slots to an unused state before starting...
 */

initslots ()
{
	int i;
	for (i = 0; i < MAXSLOTS; i++) {
		slots[i].inuse = 0;
		slots[i].fd = -1;
	}
}


/*
 *  Handle a request on the service port.
 *  Modifies the socket select mask appropriately.
 */

service (mask)
int  *mask;
{
	register	int  new;
	register	int  j;
	register	char *i;
	struct		sockaddr_in addr;
	int			len;
	int			r;

	len = sizeof (addr);
	if ((new = accept (0, &addr, &len)) < 0) {
		if (errno != EINTR) 
#ifdef	KERNEL_BUG
			if (warned++ == 0)
#endif	KERNEL_BUG
				fatal (errno);
		return;
	}

	for (j = 1; j < MAXSLOTS; j++)
		if (slots[j].inuse == 0)
			break;

	if (j == MAXSLOTS) {
		write (new, "Too many users!\n", 16);
		shutdown (new, 2);
		close (new);
		return;
	}
	if ((r = read (new, buf, SIZ)) == 0) {	/* EOF ?? */
		close (new);
		return;
	}
	buf[r] = '\0';

	/* save name, host, tty, realname */
	slots[j].info = strsave (buf);

	if (j > highslot)
		highslot = j;

	slots[j].inuse = 1;
	slots[j].fd = new;
	slots[j].new = 0;
	slots[j].mask = 1 << new;
	slots[j].old = 0;
	users++;

	*mask |= (1 << new);					/* add new fd to mask */

	r = 1;
	ioctl (new, FIONBIO, &r);				/* mark socket as non-blocking */

	sprintf (buf, "%c%s%c", META | ADDUSER | j, 
				slots[j].info, META);
	sendit (buf, strlen (buf));		/* tell whole group about me */
	intro (new);					/* and fill me in on things */

	return;
}


/*
 *  Retransmit a message to all the users.
 */

sendit (buf, len)
char   *buf;
int     len;
{
	register int   i;

	for (i = 1; i <= highslot; i++)
		if (slots[i].inuse)
			if (write (slots[i].fd, buf, len) != len)
				perror ("sendit: write");
}


/*
 *  Delete a user from this conversation.
 */

deluser (i)
int i;
{
	int	ch;

	if (--users < 2) {
		shutdown (0, 2);
		close (0);
		exit (0);
	}
	stayaround = 0;
	slots[i].inuse = 0;

	close (slots[i].fd);
	free (slots[i].info);

	ch = META | DELUSER | i;
	sendit (&ch, 1);
}



/*
 *  Save a string.
 */

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

	if (new = malloc (strlen (s) + 1))
		strcpy (new, s);
	return (new);
}



/*
 *   Send the new filedes all the users and all the buffers.
 *   We first send UPDATE | 0 to tell the user to delay updating
 *   the screen until we've sent all the text buffers, at which 
 *   point we send UPDATE | 01 to signal that we're done and
 *   the screen should be updated.
 */

intro (fd)
int  fd;
{
	struct   iovec iov[3];		/* used for multi-buffer writes */
	register int old, new;
	register int  s;			/* slot number */
	register int  i;
	register char *c;
	register int num;			/* number of buffers to write out */
	char     sc;				/* used for slot number selection */

	/* tell user not to update screen until done with intro */
	sc = META | UPDATE | 00;	
	write (fd, &sc, 1);

	/* first go through and add all the windows */
	for (s = 0; s <= highslot; s++) {
		if (slots[s].inuse == 0 || slots[s].fd == fd)
			continue;
		sprintf (buf, "%c%s%c", META | ADDUSER | s, 
			slots[s].info, META);
		write (fd, buf, strlen (buf));
	}

	/* now go through and give him all the buffers */
	for (s = 0; s <= highslot; s++) {
		if (slots[s].inuse == 0 || slots[s].fd == fd)
			continue;

		sc = META | s;				/* switch to this slot */
		iov[0].iov_base = &sc;
		iov[0].iov_len  = 1;

#ifdef notdef
		/raboof ======== foo bar baz zot blip/
			  ^ new      ^ old                ^ BUFFER
		|-----|          |-------------------|
		slots[s].new     BUFFER - slots[s].old
#endif notdef

		new = slots[s].new;
		old = slots[s].old;
		iov[1].iov_base = &slots[s].buffer[old];
		iov[1].iov_len  = (old > new ? BUFFER - old : new - old);
		if (old > new) {
			iov[2].iov_base = slots[s].buffer;
			iov[2].iov_len  = new;
			num = 3;
		} else 
			num = 2;

		writev (fd, iov, num);				/* write all at once!!! */
	}

	/* now he can update the screen */
	sc = META | UPDATE | 01;
	write (fd, &sc, 1);
}


/*
 *  Come here on alarm signal. If less than 2 users, exit.
 */

sigalrm ()
{
	if (users < 2) {
		shutdown (0, 2);
		close (0);
		exit (0);
	}
	return;
}


/*
 *  We have encountered some kind of nasty error.
 *  Tell all the users about it and go away.
 */

fatal (err)
int err;
{
	extern char *sys_errlist[];
	extern int  sys_nerr;
	char   buf[128];
	char   mesg[256];
	char   host[32];
	int    s;

	gethostname (host, 32);

	sprintf (mesg, "\nMessage from phone conversation daemon @ %s:\n", host);

	if (err < sys_nerr)
		sprintf (buf, "Fatal error: %s\n", sys_errlist[err]);
	else
		sprintf (buf, "Fatal error: %d", err);
	
#ifdef KERNEL_BUG		/* try to keep going */
	strcat (mesg, buf);
	strcat (mesg,"Warning: no more users can join this conversation! Sorry.\n");
#endif KERNEL_BUG

	for (s = 0; s <= highslot; s++)
		if (slots[s].inuse)
			write (slots[s].fd, mesg, strlen (mesg));
	
#ifndef	KERNEL_BUG
	chdir ("/");
	abort ();
	exit (1);
#endif	KERNEL_BUG

}
!Funky!Stuff!
fi # end of overwriting check
#	End of shell archive
exit 0