[alt.sources] Realtime talk for Systems V

lasse@daab.se (Lars Hammarstrand) (02/01/89)

#! /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:
#	INSTALLATION
#	MANIFEST
#	Makefile
#	defs.h
#	disconnect.c
#	doconnect.c
#	main.c
#	misc.c
#	notify.c
#	talk.1
#	talk.c
#	time.c
#	window.c
# This archive created: Tue Dec 20 18:48:15 1988
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'INSTALLATION'" '(810 characters)'
if test -f 'INSTALLATION'
then
	echo shar: "will not over-write existing file 'INSTALLATION'"
else
cat << \SHAR_EOF > 'INSTALLATION'
Tue Dec 20 15:37:30 MET 1988  (Lars Hammarstrand: ..!mcvax!enea!daab!lasse)

	Installation procedure for the System V talk program;
	-----------------------------------------------------

1)	run "make talk"

2)	Modify the BINDIR variable in Makefile so make knows where to put the
	talk program for public use.

3)	Add a dummy user named "talk" (with for example user ID 9999) in the
	/etc/passwd file, and a dummy group (with for example group ID 9999)
	in the /etc/group file.

	Why? - Talk will be installed to run as a "set user ID" program with
	this dummy user id, so it will be able to remove old message queues
	and old tmp files. For security reasons: DO NOT USE ROOT OR OTHER
	PRIVILEDGED USER ID'S.

4)	run "make install"

5)	If you want to format the manual into the file talk.doc:
	run "make man"
SHAR_EOF
if test 810 -ne "`wc -c < 'INSTALLATION'`"
then
	echo shar: "error transmitting 'INSTALLATION'" '(should have been 810 characters)'
fi
chmod 644 'INSTALLATION'
fi
echo shar: "extracting 'MANIFEST'" '(116 characters)'
if test -f 'MANIFEST'
then
	echo shar: "will not over-write existing file 'MANIFEST'"
else
cat << \SHAR_EOF > 'MANIFEST'
INSTALLATION
MANIFEST
Makefile
defs.h
disconnect.c
doconnect.c
main.c
misc.c
notify.c
talk.1
talk.c
time.c
window.c
SHAR_EOF
if test 116 -ne "`wc -c < 'MANIFEST'`"
then
	echo shar: "error transmitting 'MANIFEST'" '(should have been 116 characters)'
fi
chmod 644 'MANIFEST'
fi
echo shar: "extracting 'Makefile'" '(1013 characters)'
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
#	Makefile for Sys V talk
#	(with real time screen updates and message queues)
#
#	Before "make install":
#
#	Modify BINDIR so make knows where to put the talk program for
#	public use.
#
#	Add a dummy user named "talk" (user and group number 9999) to the
#	/etc/passwd and /etc/group file. Talk will be installed to run as a
#	"set user ID" program with this dummy user id, so it will be able to
#	remove old message queues and old tmp files. For security reasons:
#	DO NOT USE ROOT OR OTHER PRIVILEDGED USER ID'S.
#

USERID=talk	# User id that should exist in /etc/passwd (uidnr: 9999)
GROUPID=talk	# Group id that should exist in /etc/group (gidnr: 9999)

BINDIR=/usr/lbin
CFLAGS=-O

OBJS=	doconnect.o disconnect.o notify.o main.o misc.o talk.o time.o window.o

talk:	$(OBJS)
	$(CC) $(LDFLAGS) -o talk $(OBJS) -lcurses

talk.doc: talk.1
	nroff -man talk.1 > talk.doc

man:	talk.doc

install: talk
	cp talk $(BINDIR)
	chown $(USERID) $(BINDIR)/talk
	chgrp $(GROUPID) $(BINDIR)/talk
	chmod 6111 $(BINDIR)/talk
SHAR_EOF
if test 1013 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 1013 characters)'
fi
chmod 644 'Makefile'
fi
echo shar: "extracting 'defs.h'" '(3488 characters)'
if test -f 'defs.h'
then
	echo shar: "will not over-write existing file 'defs.h'"
else
cat << \SHAR_EOF > 'defs.h'
/*
 * <LH:881125:v2.0>
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/errno.h>
#include <ctype.h>
#include <curses.h>
#include <signal.h>
#include <pwd.h>
#include <utmp.h>


/* window sizes */

#define         MY_WINLINES              (LINES / 2)
#define         HIS_WINLINES             (LINES - MY_WINLINES - 1)


/*  Message queue commands 	<LH:881125:v2.0>	*/

#define		CONNECT			0xF0
#define		DISCONNECT		0xF3
#define		DELETE			0xFC
#define		END_OF_FILE		0x04
#define		REFRESH			0x0C
#define		BELL			0x07


/*  Exit codes 			<LH:881125:v2.0>	*/

#define		I_DISCONNECTED		1
#define		HE_DISCONNECTED		2
#define		NO_ANSWER		3
#define		NOT_LOGGED_IN		4
#define		TTY_DISABLED		5
#define		PROGRAM_ERROR		6


/* other defines		<LH:881125:v2.0>	*/

#define		TIME_SIZE		19	/* size of window clock */
#define		TICK_INTERVAL		2	/* clock update interval */
#define		MAXTRY			3	/* max tries to notify user */
#define		TRY_TIME		20	/* time between tries */


/* message queue defines	<LH:881125:v2.0>	*/

#define		MSGSIZE			(sizeof (MSG) - sizeof (long))
#define		ANYMSG			(0)
#define		MSGMASK			0600


/* macros			<LH:881125:v2.0>	*/

#define		printable(x)		(x == '\n' || x == '\t' || isprint(x)) 
#define		file_exist(x)		(access(x, 0) == 0)
#define		eq(s1,s2)		(strcmp(s1,s2) == 0)


/* variable and function declarations  <LH:881125:v2.0>	*/

extern	WINDOW	*mywin, *hiswin, *dividewin;	/*  the three windows  */
extern	int	connected;			/* whether actually talking  */
extern	int	time_changed;			/*  to update elapsed time  */
extern	int	master;				/*  whether master or slave  */
extern	int	msgid;				/*  message queue id */

extern	long	time();
extern	int	out_of_date();
extern	void	clock_tick();
extern	void	interrupt();			/* intr trap (disconnect) */
extern	void	disconnect();
extern	void	doconnect();
extern	void	talk();
extern	void	update_time();
extern	void	screen_init();
extern	void	master_queue();
extern	void	slave_queue();
extern	void	error();
extern	void	sendmsg();
extern	void	setutent();
extern	void	add_my_window();
extern	void	add_his_window();
extern	void	wheader();
extern	void	wprevch();
extern	void	update_time_line();

extern	char	*notify();			/*  ring user */
extern	char	*fullname();			/*  full name of login name */
extern	char	*ctime();			/*  get current time str */
extern	char	*strchr();
extern	char	*strrchr();
extern	char	*getlogin();

extern	struct	utmp *getutent();
extern	struct	passwd *getpwnam();

extern	int	mypid;				/*  current PID */
extern	int	readpid;			/*  current keyboard PID */
extern	int	hispid;				/*  other sides PID */
extern	int	errno;

extern	key_t	key;				/*  common key for msgget() */

extern	char	tmp[BUFSIZ];			/*  general purpose !  */
extern	char	tempfile[40];			/*  common temp file */
extern	char	*myname, *hisname, *histty;
extern	char	*progname;			/*  current program name */
extern	char	delchar;			/*  current delete char  */


/*
 * Message queue buffer.  <LH-881125>
 *
 * Messages are sent from your own keyboard-read process to your own displays
 * process (se talk.c). Your display-process will first show it on your window
 * and then send it over to the other side. Messages comming from the other
 * side (i.e: msg.from_pid == hispid) will show up at "his" window.
 */

typedef struct _msg
{
	long	to_pid;			/* want only messages to this PID */
	int	from_pid;		/* PID who sent message */
	int	c;			/* the actual character (or cmd) */
} MSG;

extern	MSG	*getmsg();
SHAR_EOF
if test 3488 -ne "`wc -c < 'defs.h'`"
then
	echo shar: "error transmitting 'defs.h'" '(should have been 3488 characters)'
fi
chmod 644 'defs.h'
fi
echo shar: "extracting 'disconnect.c'" '(1595 characters)'
if test -f 'disconnect.c'
then
	echo shar: "will not over-write existing file 'disconnect.c'"
else
cat << \SHAR_EOF > 'disconnect.c'
#include "defs.h"

/*
 *  Trap the keyboard interrupt signal to force a disconnect. <LH:881125:v2.0>
 */

void
interrupt ()	/*  program interrupted by signal - disconnect */
{
	disconnect (I_DISCONNECTED);
}


/*
 *  Tell other side to disconnect, kill the keyboard-read process, clean up
 *  windows and then exit. <LH:881125:v2.0>
 */

void
disconnect (how)
int how;
{
	static beenhere = FALSE;

	if (beenhere++)
		return;
		
	alarm (0);
	signal (SIGALRM, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGHUP, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
	
	if (readpid)	/* kill keyboard-read process */
	{
		kill (readpid, 9);
		wait ((int *)0);
	}

	sendmsg (hispid, DISCONNECT);

	switch (how)
	{
		case I_DISCONNECTED:
			waddstr (mywin, "\n[You have disconnected]");
			break;

		case HE_DISCONNECTED:
			waddstr (mywin, "\n[Your party has disconnected]");
			beep ();
			break;

		case PROGRAM_ERROR:
			waddstr (mywin, "\n[Program failure - disconnected]");
			break;
			
		case NO_ANSWER:
			wheader (mywin, "[Your party would not respond]\n");
			break;

		case NOT_LOGGED_IN:
			sprintf (tmp, "[Your party is not logged on %]\n",
								histty);
			wheader (tmp);
			break;

		case TTY_DISABLED:
			wheader (mywin, "[Your party has disabled messages]\n");
			break;
	}

        wnoutrefresh (mywin);
        wnoutrefresh (hiswin);
        wnoutrefresh (dividewin);
	doupdate ();
	endwin ();

	msgctl (msgid, IPC_RMID, 0);
	unlink (tempfile);		/* should have been done before */
	printf ("\n");


	if (how == PROGRAM_ERROR)
		exit (4);

	if (!connected)
		notify (DISCONNECT);

	exit(0);
}
SHAR_EOF
if test 1595 -ne "`wc -c < 'disconnect.c'`"
then
	echo shar: "error transmitting 'disconnect.c'" '(should have been 1595 characters)'
fi
chmod 644 'disconnect.c'
fi
echo shar: "extracting 'doconnect.c'" '(2046 characters)'
if test -f 'doconnect.c'
then
	echo shar: "will not over-write existing file 'doconnect.c'"
else
cat << \SHAR_EOF > 'doconnect.c'
#include "defs.h"

/*
 *  Establish connection between users.
 *
 *  <LH:881125:v2.0>
 */

void
doconnect ()
{
	mypid = getpid();

	sprintf (tempfile, "/tmp/ta_%s.%s", hisname, myname);
	
	if (!file_exist (tempfile) || out_of_date (tempfile))
	{
		unlink (tempfile);
		master_queue ();
	}
	else
		slave_queue ();

	connected = TRUE;
	wheader (mywin, "[Connected]\n");
	update_time_line ();
	beep ();
}


/*
 *  Create message queue for connection to slave and try to connect.
 *
 *  <LH:881125:v2.0>
 */

void
master_queue ()
{
	int	try;
	char	*histty;
	MSG	*msgp;

	sprintf (tempfile, "/tmp/ta_%s.%s", myname, hisname);

	if (creat (tempfile, 0) == -1)
		error("can't create %s", tempfile);

	if ((key = ftok (tempfile, '\0')) == (key_t)-1)
		error (tempfile);

	if ((msgid = msgget (key, 0)) >= 0 && msgctl (msgid, IPC_RMID,0) == -1)
		error ("Can't remove old message queue id (%d)", msgid);

	if ((msgid = msgget (key, MSGMASK | IPC_CREAT)) == -1)
		error ("Can't create new message queue.");

	histty = notify (CONNECT);

	sprintf (tmp, "[Waiting for your party to respond on %s]\n", histty);
	wheader (mywin, tmp);
	wnoutrefresh (mywin);
	doupdate ();

	for (try=1; try < MAXTRY; ++try)
	{
		sendmsg ((int)key, CONNECT);
		alarm (TRY_TIME);

		while (msgp = getmsg (mypid))
		{
			if (msgp->c == CONNECT)
			{
				hispid = msgp->from_pid;
				return;
			}
		}

		histty = notify (CONNECT);
		
		sprintf (tmp, "[Ringing your party again on %s (retry %d)]\n",
						histty, try);
		wheader (mywin, tmp);
	}

	disconnect (NO_ANSWER);
	/*NOTREACHED*/
}

/*
 *  Get message queue id for the slave connection.
 *
 *  <LH:881125:v2.0>
 */

void
slave_queue ()
{
	MSG	*msgp;

	sprintf (tempfile, "/tmp/ta_%s.%s", hisname, myname);

	if ((key = ftok (tempfile, '\0')) == (key_t)-1)
		error (tempfile);

	unlink (tempfile);

	if ((msgid = msgget (key, 0)) == -1)
		error ("msgget: can't get msgid.");

	alarm (5);
	
	if ((msgp = getmsg (ANYMSG)) == NULL)
		error ("Can't connect to master");	

	hispid = msgp->from_pid;
	sendmsg (hispid, CONNECT);
}
SHAR_EOF
if test 2046 -ne "`wc -c < 'doconnect.c'`"
then
	echo shar: "error transmitting 'doconnect.c'" '(should have been 2046 characters)'
fi
chmod 644 'doconnect.c'
fi
echo shar: "extracting 'main.c'" '(3121 characters)'
if test -f 'main.c'
then
	echo shar: "will not over-write existing file 'main.c'"
else
cat << \SHAR_EOF > 'main.c'
#ifndef	lint
static char sccsid[] = "@(#)talk.c  2.0 [Lars Hammarstrand] 88-11-23";
static char orgid[] = "@(#)talk.c  1.2 [Nigel Holder (C) -  Baddow] 04/12/85";
#endif


/****************************************************************************
*
*	VERS 2.0 Author: Lars Hammarstrand.	(rewritten from 1.2)
*	VERS 1.2 Orginal Author:  Nigel Holder	(ver 1.2 04/12/85)
*
*
*	VERS 2.0 HISTORY: (Lars Hammarstrand)
*
*	1)	fixed erasechar to work.
*	2)	added a message queue as a communikation channel instead
*		of named pipes. (much faster)
*	3)	added realtime updates of screen.
*	4)	added a realtime keyboard-read process.
*	5)	replaced and cleaned up exit functions.
*	6)	cleaned up clock funcktions so it will show true elapsed time.
*
*
*	VERS 1.2 HISTORY: (Nigel Holder)
*
*	Date     :  12 June 1985
*		     4 December 1985		changed elapsed time stuff
*						to be synchronous to windows
*
*	Copyright (C) 1986  by Nigel Holder
*
*	   Permission to use this program is granted, provided it is not
*	sold, or distributed for direct commercial advantage, and includes
*	the copyright notice and this clause.
*
*
*	   Talk - an interactive communication program that allows users
*	to talk on a character basis (as opposed to a line basis, as for
*	the system write utility).
*
*	   Written for System V as it uses named pipes !
*	   (and BSD already has a talk utility).
*
*	Old 1.2 bugs:
*
*	1.   Not as good as BSD talk, but it suffices.
*	     (restricted to current host machine)
*
*	2.   Really need select() type statement (BSD) instead of sleeping
*	     for 1 second between no input or output activity.
*	     (version 8 should fix this).
*
*	3.   Should check for name fields in dividewin overflowing screenwidth
*
*	4.   Probably should disable CTRL-c stopping program when connected.
*
****************************************************************************/

#include "defs.h"

int	connected = FALSE;			/* whether actually talking  */
int	time_changed = FALSE;			/* to update elapsed time  */

WINDOW	*mywin, *hiswin, *dividewin;		/* the three windows  */
int	hispid, mypid, readpid, msgid;

char	tmp[BUFSIZ];				/* general purpose !  */
char	tempfile [40];
char	*myname	= NULL;
char	*hisname = NULL;
char	*histty = NULL;
char	*progname;

char	delchar;
key_t	key;

main (argc, argv)
int	argc;
char	*argv[];
{

	if (progname = strrchr (argv[0], '/'))
		++progname;
	else
		progname = argv[0];

	if (argc < 2)
	{
		fprintf (stderr, "usage: %s user [tty]\n", progname);
		exit (1);
	}

	hisname = argv[1];

	if (argc > 2)
		histty = argv[2];

	if ((myname = getlogin()) == NULL)
	{
		fprintf (stderr, "You don't exist. Go away.\n");
		exit (2);
	}
	
	signal (SIGINT, SIG_IGN);		/*  play safe  */
	signal (SIGHUP, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);

	screen_init ();			/*  set up windows  */

	signal (SIGINT, interrupt);	/*  gracefully trap signals  */
	signal (SIGHUP, interrupt);
	signal (SIGQUIT, interrupt);
	signal (SIGALRM, clock_tick);	/* increment clock counter */

	doconnect ();			/*  tell'm I want'o talk with'm */
	talk ();			/*  let your fingers do the walking */

	/*NOTREACHED*/
}


SHAR_EOF
if test 3121 -ne "`wc -c < 'main.c'`"
then
	echo shar: "error transmitting 'main.c'" '(should have been 3121 characters)'
fi
chmod 644 'main.c'
fi
echo shar: "extracting 'misc.c'" '(1974 characters)'
if test -f 'misc.c'
then
	echo shar: "will not over-write existing file 'misc.c'"
else
cat << \SHAR_EOF > 'misc.c'
#include "defs.h"

/*
 *	Print error message and disconnect.
 */

void
error (message, arg1, arg2)
char *message, *arg1, *arg2;
{
		static char beenhere = FALSE;
		
		if (beenhere++)
			return;

		wmove (hiswin, HIS_WINLINES - 2, 0);
		wprintw (hiswin, "%s: ", progname);
		wprintw (hiswin, message, arg1, arg2);
		wrefresh (hiswin);
		disconnect (PROGRAM_ERROR);
}


/*
 *  Read from message queue, any message dedicated to this process.
 *
 *  The returned message does also include information about which
 *  process it was sent from.
 *
 *  <LH:881125:v2.0>
 */


MSG *
getmsg (pid)
int pid;
{
	static MSG msg;
	register MSG *msgp = &msg;
	
	if (msgrcv (msgid, msgp, MSGSIZE, (long)pid, 0) == -1)
	{
		switch (errno)
		{
			case EINTR:		/* interrupted */
				return (NULL);
		
			case EIDRM:		/* msgid removed */
				disconnect (HE_DISCONNECTED);
				return (NULL);
	
			default:		/* other problems */
				error ("msgrcv");
				return (NULL);
		}
	}
	return (msgp);
}


/*
 * Send a character or command to a specific process.
 *
 *  <LH:881125:v2.0>
 */

void
sendmsg (pid, c)
int pid;
int c;
{
	static MSG msg;
	register MSG *msgp = &msg;

	msgp->to_pid = pid;
	msgp->from_pid = mypid;
	msgp->c = c;
	
	while (msgsnd (msgid, msgp, MSGSIZE, 0) == -1)
	{
		switch (errno)
		{
			case EINTR:		/* interrupted */
				continue;
		
			case EIDRM:		/* msgid removed */
			default:
				disconnect (HE_DISCONNECTED);
				return;
		}
	}
}

/*
 *  Determine whether file is not in use.
 */

out_of_date(fname)
char	*fname;
{
	struct stat stat_buf;

	if (stat (fname, &stat_buf) == -1)	/*  assume doesn't exist  */
		return (FALSE);

	if ((time ((long *) 0) - stat_buf.st_mtime) > (MAXTRY * TRY_TIME))
		return (TRUE);
		
	return (FALSE);
}

/*
 *  Get full name of user from the gecos field of the passwd file.
 *
 *  <LH:881125:v2.0>
 */

char *
fullname (logname)
char	*logname;
{
	register struct passwd *p;
	
	return ((p = getpwnam (logname)) ?  p->pw_gecos : "no user");
}

SHAR_EOF
if test 1974 -ne "`wc -c < 'misc.c'`"
then
	echo shar: "error transmitting 'misc.c'" '(should have been 1974 characters)'
fi
chmod 644 'misc.c'
fi
echo shar: "extracting 'notify.c'" '(888 characters)'
if test -f 'notify.c'
then
	echo shar: "will not over-write existing file 'notify.c'"
else
cat << \SHAR_EOF > 'notify.c'
#include "defs.h"

/*
 *  Notify user you wish to talk to.
 *
 *  <LH:881125:v2.0>
 */

char *
notify (how)
int	how;
{
	register struct	utmp *user;
	FILE	*fp;

	setutent ();

	while ((user = getutent ()) != NULL)	/*  search for user  */
	{
		if (!eq (user->ut_user, hisname))
			continue;

		if (!histty || eq (histty, user->ut_line))
			break;
	}

	if (!user)
		disconnect (NOT_LOGGED_IN);

	sprintf (tmp, "/dev/%s", user->ut_line);

	if ((fp = fopen (tmp, "w")) == NULL)
		disconnect (TTY_DISABLED);

	switch (how)
	{
		long curtime, time();

		case CONNECT:
			fprintf (fp, "\r\7%s (%s) is phoning - respond with 'talk %s'    \n",
					myname, fullname (myname), myname);
			break;

		case DISCONNECT:
			time (&curtime);
			fprintf (fp, "\r\7%s (%s) has stopped phoning [%s]\n",
				myname, fullname (myname), ctime (&curtime));
			break;
	}
	fclose (fp);
	return (user->ut_line);
}

SHAR_EOF
if test 888 -ne "`wc -c < 'notify.c'`"
then
	echo shar: "error transmitting 'notify.c'" '(should have been 888 characters)'
fi
chmod 644 'notify.c'
fi
echo shar: "extracting 'talk.1'" '(2210 characters)'
if test -f 'talk.1'
then
	echo shar: "will not over-write existing file 'talk.1'"
else
cat << \SHAR_EOF > 'talk.1'
.TH Talk 1
.SH NAME
talk \- talk to another user
.SH SYNOPSIS
.B talk user [ tty ]
.SH DESCRIPTION
.PP
Talk is a utility that enables two users to interactively
communicate on a character basis on split screens.
It is intended to supersede the write utility for interactive
use by providing a more useful service.

Talk is invoked with a user name and an optional tty name thus :-

        talk user [ tty8 ]

.PP
If user happens to be logged in more than once and no tty name is
supplied, talk will use the first entry in /etc/utmp (as used by who).
.PP
Talk will then attempt to notify the requested user that you are
trying to talk with him.
Trying to talk to a user may fail for two reasons :-

        The requested user is not logged in.

        The requested user has disabled messages (via mesg)

.PP
If the user doesn't answer, talk will keep notifying him.
After a reasonable number of retries, talk will give up and exit.
.PP
To reply to a user trying to talk to you, you should type
talk user (as shown in the request message).
.PP
When your party has connected you may both begin to talk (this
will be indicated by the bell ringing on your terminal).
The name of the person you are talking to and an elapsed time
indicator will appear in the centre of the screen.
You will notice that there are bursts of input and
output - this is due to the way they are handled within talk.
In order to avoid busy waiting, a sleep of one
second occurs whenever there is no activity.
.bp
.PP
Certain characters when typed have special meaning :-


ctrl-l    -    Refresh the screen.  Useful if the
               screen gets corrupted.

ctrl-d    -    Disconnect - finish talking.

ctrl-g    -    Ring the bell on other users terminal.

delete    -    Delete the character before the cursor
               (works backwards over lines as well).
               Uses your normal delete key.

ctrl-c    -    Forced exit.  This has the same effect
               as disconnect, although it may be
               used at any stage of the proceedings
               (ie. before connection has occured).


.SH FILES
/etc/utmp               to find recipient's tty
/tmp/ta_xxxxxxxx        tmp file to get a uniq msgkey.
SHAR_EOF
if test 2210 -ne "`wc -c < 'talk.1'`"
then
	echo shar: "error transmitting 'talk.1'" '(should have been 2210 characters)'
fi
chmod 644 'talk.1'
fi
echo shar: "extracting 'talk.c'" '(1317 characters)'
if test -f 'talk.c'
then
	echo shar: "will not over-write existing file 'talk.c'"
else
cat << \SHAR_EOF > 'talk.c'
#include "defs.h"

/*
 *  Talk() forks of a demon whose only task is to serve my own talk process
 *  (through the message queue) with characters read from the keyboard. Talk()
 *  interprets messages comming from my own PID as characters (or commands)
 *  to be displayed on my own window. If the character is accepteble it is 
 *  then sent over to the corresponding talk process.
 *
 *  <LH:881125:v2.0>
 */


void talk()				/*  talk to other user  */
{
	char c;
	register MSG *msgp;

	wmove (hiswin, 0, 0);		/*  start cursor positions  */
	wmove (mywin, 1, 0);

	delchar = erasechar();		/* get current erase char */

	if ((readpid = fork()) == 0)	/* fork of keyboard-read process */
	{
		setpgrp();

		while (1)
		{
			read (0, &c, 1);
			/* send char to my self */
			sendmsg (mypid, (c == delchar) ? DELETE : c); 
		}
	}
	else if (readpid == -1)
		error ("cannot fork");

	alarm (TICK_INTERVAL);		/*  trigg time counter */

	while (1)
	{
		wnoutrefresh (mywin);	/*  place cursor in my own window  */
		doupdate();		/*  update screen */

		if (msgp = getmsg (mypid)) /* returns NULL if interrupted */
		{
			if (msgp->from_pid == mypid)
				add_my_window (msgp->c);
			else if (msgp->from_pid == hispid)
				add_his_window (msgp->c);
		}

		if (time_changed)
			update_time();	/*  display elapsed time  */
	}
}

SHAR_EOF
if test 1317 -ne "`wc -c < 'talk.c'`"
then
	echo shar: "error transmitting 'talk.c'" '(should have been 1317 characters)'
fi
chmod 644 'talk.c'
fi
echo shar: "extracting 'time.c'" '(1240 characters)'
if test -f 'time.c'
then
	echo shar: "will not over-write existing file 'time.c'"
else
cat << \SHAR_EOF > 'time.c'
#include "defs.h"

static	int	elapsed_time = 0;
static	char	elapsed[] = "[%02d:%02d:%02d]  ";
static	int	x = 0;
static	int	y = 0;

/*
 *  Update the elapsed_time counter and set the global flag to indicate time
 *  should be updated
 *
 *  <LH:881125:v2.0>
 */

void
clock_tick()
{
	signal (SIGALRM, clock_tick);
	elapsed_time += TICK_INTERVAL;
	time_changed = TRUE;
	alarm (TICK_INTERVAL);
}
			

/*
 *  Update current elapsed time on the status line.
 *  (will show up on next doupdate())
 *
 *  <LH:881125:v2.0>
 */

void
update_time()
{
	register tm = elapsed_time;
	register WINDOW *win = dividewin;

	wmove (win, y, x);
	wstandout (win);
	wprintw (win, elapsed, tm/3600, tm/60, tm%60);
	wstandend (win);
        wnoutrefresh (win);

	time_changed = FALSE;
}

void update_time_line ()
{
	register WINDOW *win = dividewin;
	char time_temp [sizeof(elapsed)];

	sprintf (time_temp, elapsed, 0, 0, 0);
	sprintf (tmp, "  %s is talking to %s (%s) ", myname, hisname,
							fullname(hisname));
				
        wmove (win, 0, (COLS - strlen(tmp) - strlen(time_temp)) / 2);

	wstandout (win);
	waddstr (win, tmp);
	getyx (win, y, x);	/* remember where to put time next round */
	waddstr (win, time_temp);
	wstandend (win);
	wnoutrefresh (win);
}
SHAR_EOF
if test 1240 -ne "`wc -c < 'time.c'`"
then
	echo shar: "error transmitting 'time.c'" '(should have been 1240 characters)'
fi
chmod 644 'time.c'
fi
echo shar: "extracting 'window.c'" '(2252 characters)'
if test -f 'window.c'
then
	echo shar: "will not over-write existing file 'window.c'"
else
cat << \SHAR_EOF > 'window.c'
#include "defs.h"

/*
 *  Check for and act on any output to be displayed.
 *
 *  <LH:881125:v2.0>
 */

void
add_his_window (c)
register c;
{
	register WINDOW *win = hiswin;

	switch (c)
	{
		case  DISCONNECT:
			disconnect (HE_DISCONNECTED);
			/*NOTREACED*/

		case  DELETE  :
			wprevch (win);
			wdelch (win);
			break;

		case  BELL  :
			beep();
			break;

		default  :
			if (printable (c))
				waddch (win, c);
			break;
	}

        wnoutrefresh (win);

}


/*
 *  Check for and act on user input
 *
 *  <LH:881125:v2.0>
 */

void
add_my_window(c)
register c;
{
	register WINDOW *win = mywin;

	switch (c)
	{
		case  END_OF_FILE:
			disconnect (I_DISCONNECTED);
			/*NOTREACED*/

		case  REFRESH:
			clearok (curscr, TRUE);
			break;

		case  BELL:
			beep();
			break;

		case DELETE:
			wprevch (win);
			wdelch (win);
			break;

		default  :
			if (printable (c))
				waddch (win, c);
			else
			{
				beep();
				return;
			}
			break;
	}
        wnoutrefresh (win);
	sendmsg (hispid, c);	/* send character to other side */
}


/*
 *  Print message at head of window
 */

void
wheader (win, message)
register WINDOW	*win;
char	*message;
{
	wmove (win, 0, 0);
	wclrtoeol (win);
	waddstr (win, message);
}


/*
 *  Initialise talk windows
 */

void
screen_init()
{
        int	i;

        initscr();

        mywin = newwin (MY_WINLINES, COLS, 0, 0);
        hiswin = newwin (HIS_WINLINES, COLS, MY_WINLINES + 1, 0);
        dividewin = newwin (1, COLS, MY_WINLINES, 0);


	noecho ();
	cbreak ();
        scrollok (mywin, TRUE);
        scrollok (hiswin, TRUE);
	idlok (mywin, TRUE);
	idlok (hiswin, TRUE);


        wmove (dividewin, 0, 0);

        for (i = 0 ; i < COLS ; i++)
                waddch (dividewin, '-');

	/* faster screen update (a la curses manual) */

        wnoutrefresh (mywin);
        wnoutrefresh (hiswin);
        wnoutrefresh (dividewin);
}



/*
 *  Move cursor back to previous non-space char within window
 */

void
wprevch (win)
register WINDOW	*win;
{
	register x, y;

	getyx (win, y, x);

	if (--x < 0)   {
		if (--y < 0)   {
			wmove (win, 0, 0);
			return;
		}
		for (x = win->_maxx - 1 ; x >= 0 ; --x)
		{
			wmove(win, y, x);

			if (winch(win) != ' ')
				return;
		}
		return;
	}
	wmove (win, y, x);
}
SHAR_EOF
if test 2252 -ne "`wc -c < 'window.c'`"
then
	echo shar: "error transmitting 'window.c'" '(should have been 2252 characters)'
fi
chmod 644 'window.c'
fi
exit 0
#	End of shell archive