[net.sources] pwrite - a multi-user "talk" program.

rosenbk@dalcs.UUCP (Rob P. and Stan L.) (04/29/85)

Gentlepeople:

	After completing a software design course, which involved a 
	software project using sockets, I decided to do a little spare
	time hacking myself in order to produce this multi-user "talk"
	program. I thought it would be an interesting project because
	I could learn the use of 4.2BSD sockets and because talk(1)
	only handles two people and requires a driver.

	Do whatever you want with this piece of software, but please leave
	the comments in main.c undisturbed.

	A few notes:

	1)	In evals.c, in e_ring(), change the command for pwrite
		from ~puchyr/com/pwrite to an appropriate path.

	2)	The following is a shell archive file. make a directory
		and save this file in it. Edit this file and remove
		the top portion of this file up to and including the
		----cut here---- line. Then use the command:

		sh this_file

		Do not run this file through csh. It must be fed to the
		Bourne shell.

	3)	You should end up a number of C programs, a Makefile, and
		a pwrite.man manual entry. use the command:

		make

		to compile the programs. An 'a.out' will result.


	If anyone comes with bug fixes please post them to the net.

			.__	  ___.	 .__		 ..
			|__\	 /___|	 |__\		 ||
			|| >>	<<__	 || >>.. ..  __. ||__ ..  .. .. __.
			||//	 \__\	 ||// || || /__| |._ \ \\ || |'/__|
			||\\	.___>>	 ||   ||_||<<__. || ||	\\|| | /
			|| \\	|___/	 ||    \__| \__| || ||	 \`| ||
								  \|
			._________________________________________/|
			|_________________________________________/

			Robert Scott Puchyr;   ...dartvax!dalcs!dalcsug!puchyr
			Dalhousie University,  Halifax,  Nova Scotia,  Canada.


-----cut here-----cut here-----cut here-----cut here-----
#!/bin/sh
# shar:  Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile
#	evals.c
#	goodbye.c
#	header.h
#	hello.c
#	main.c
#	pwrite.man
#	socket_lib.c
#	users.c
#	utils.c
#	windows.c
cat - << \SHAR_EOF > Makefile

a.out:		main.o evals.o goodbye.o hello.o socket_lib.o users.o utils.o windows.o
		cc -g main.o evals.o goodbye.o hello.o socket_lib.o users.o utils.o windows.o -lcurses -ltermcap


main.o:		header.h main.c
evals.o:	header.h evals.c
goodbye.o:	header.h goodbye.c
hello.o:	header.h hello.c
socket_lib.o:	header.h socket_lib.c
users.o:	header.h users.c
utils.o:	header.h utils.c
windows.o:	header.h windows.c

SHAR_EOF
cat - << \SHAR_EOF > evals.c

#include "header.h"

eval_key(c)
char	c;
{
	int	a, k;

	switch(c)
	{
		case   4:
				goodbye();
				break;
		case  12:
				wrefresh(curscr);
				break;
		case  18:
				w_redraw(0);
				break;
		case  23:
				if (SNOOPER >= 0)
					p_sendch(c);
				break;
		case ESC:
				eval_esc();
				break;
		case 127:
		case   8:
				w_writech(0,'\b');
				p_sendch('\b');
				break;
		default:
				if (c >= ' ' || c == RET)
				{
					w_writech(0,c);
					p_sendch(c);
				}
				break;
	}
}

eval_esc()
{
	int	c;

	overwrite(curscr,SCREEN);
	w_setcorners(CURRENT_WINDOW);
	sprintf(STRING,"%c\n\"%s\" --> COMM-MODE.%c",BEGIN_SECRET,T[0].__tty,END_SECRET);
	p_sendstr(STRING);
	w_corners(1);
	do_line("Command Mode. Press ESC to return to Talk Mode.");

	while ((c = getchar()) != ESC)
	{
		w_corners(0);
		switch(c)
		{
			case  12:
					wrefresh(curscr);
					break;
			case  18:
					w_redraw(0);
					break;
			case 'w':
					w_who();
					break;
			case 'a':
					e_call();
					break;
			case 'd':
					e_delete();
					break;
			case 'r':
					e_ring();
					break;
			case '?':
					w_help();
					break;
			case 'h':
					w_mvcorners( 0,-2, 0,-2);
					break;
			case 'j':
					w_mvcorners( 1, 0, 1, 0);
					break;
			case 'k':
					w_mvcorners(-1, 0,-1, 0);
					break;
			case 'l':
					w_mvcorners( 0, 2, 0, 2);
					break;
			case 'H':
					w_mvcorners( 0, 0, 0,-2);
					break;
			case 'J':
					w_mvcorners( 0, 0, 1, 0);
					break;
			case 'K':
					w_mvcorners( 0, 0,-1, 0);
					break;
			case 'L':
					w_mvcorners( 0, 0, 0, 2);
					break;
			case SPC:
					w_nextcorners();
					break;
			case '@':
					w_change();
					break;
		}
		w_corners(1);
		do_line("Command Mode. Press ESC to return to Talk Mode.");
	}

	w_corners(0);
	w_redraw(1);
	sprintf(STRING,"%c\n\"%s\" <-- COMM-MODE.%c",BEGIN_SECRET,T[0].__tty,END_SECRET);
	p_sendstr(STRING);
	sock_io();
}

e_call()
{
	int	a;

	w_who();
	strcpy(STRING,enter_line("Which user do you want? "));
	if (*STRING)
	{
		if (!strcmp(STRING,"**"))
		{
			if (p_check(STRING,0) < 0)
			{
				SNOOPER = p_add("**","Super Snooper",-1,0);
				w_new_user(SNOOPER);
				sprintf(STRING,"%c\n\"%s\" ADD \"**\" %c",BEGIN_SECRET,T[0].__tty,END_SECRET);
				p_sendstr(STRING);
			}
			else
			{
				panic("How many Super Snoopers do you want?!?!?",2);
			}
		}
		else if ((a = p_check(STRING,1)) < 0)
		{
			panic("That user is not on the system.",2);
		}
		else
		{
			if (p_check(STRING,0) < 0)
			{
				a = p_add(W[a].W_tty,W[a].W_username,-1,0);
				w_new_user(a);
				sprintf(STRING,"%c\n\"%s\" ADD \"%s\".%c",BEGIN_SECRET,T[0].__tty,T[a].__tty,END_SECRET);
				p_sendstr(STRING);
			}
			else
			{
				panic("That user is already on pwrite.",2);
			}
		}
	}
	w_redraw(0);
}

e_delete()
{
	int	a;

	w_who();
	strcpy(STRING,enter_line("Which user do you want to delete? "));
	if (*STRING)
	{
		if ((a = p_check(STRING,0)) < 0)
		{
			panic("That user is not on pwrite.",2);
		}
		else
		{
			sprintf(STRING,"%c\n\"%s\" DEL \"%s\".%c",BEGIN_SECRET,T[0].__tty,T[a].__tty,END_SECRET);
			p_delete(a);
			p_sendstr(STRING);
		}
	}

	w_redraw(0);
}

e_ring()
{
	int  a;

	for (a=1; a < NUM_T; a++)
	{
		if (!T[a].__dead && !T[a].__connected)
		{
			sprintf(STRING,"/dev/tty%s",T[a].__tty);
			if ((fp = fopen(STRING,"w")) != NULL)
			{
				fprintf(fp,"\n\n\n%c",7);
				fprintf(fp,"pwrite: User '%s' on 'tty%s' wishes to talk to you using 'pwrite'.\n",T[0].__username,T[0].__tty);
				fprintf(fp,"pwrite: Please respond using: '~puchyr/com/pwrite' and wait 10 seconds.\n");
				fprintf(fp,"\n\n%c",7);
				fflush(fp);
				fclose(fp);
				w_writestr(a,"[RING]  ");
			}
			else
			{
				w_writestr(a,"[NO RING]  ");
			}
		}
	}
}

SHAR_EOF
cat - << \SHAR_EOF > goodbye.c

#include "header.h"

goodbye()
{
	int	a;

	for (a=0; a < NUM_T; a++)
	{
		if (T[a].__sock >= 0 && !T[a].__dead)
			s_delete(T[a].__sock);
	}

	unlink(T[0].__file);
	do_line("");
	nocrmode(); echo();
	endwin();

	exit(1);
}

SHAR_EOF
cat - << \SHAR_EOF > header.h

#include <curses.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>

#define	MAX_USERS		12
#define ESC			27
#define RET			'\n'
#define SPC			' '
#define BEGIN_SECRET		19
#define END_SECRET		20

WINDOW	*SCREEN;		/* a full-size work area	*/
WINDOW	*LINE;			/* bottom line of screen	*/

FILE	*fp, *fopen();

struct	pw__user		/* pwrite user structure	*/
{
	WINDOW	*__win1;	/* user's border window		*/
	WINDOW	*__win2;	/* user's inner window		*/
	char	__file[32];	/* user's socket file name	*/
	char	__tty[8];	/* user's tty number (tty??)	*/
	char	__username[32];	/* user's username		*/
	int	__sock;		/* user's socket descriptor	*/
	int	__connected;	/* is this user connected?	*/
	int	__dead;		/* is this connection dead?	*/
	int	__secret;	/* is currently sending secret  */
};
struct	pw__user T[MAX_USERS];	/* I am T[0]			*/

struct	corners
{
	int	C_y;		/* y coordinate or this corner	*/
	int	C_x;		/* x coordinate or this corner	*/
	int	C_under;	/* what was under this corner	*/
};
struct	corners C[4];
int	CURRENT_WINDOW;		/* The current window		*/

struct	def__loc		/* default locations of wins	*/
{
	int	D_y;		/* default row of this window	*/
	int	D_x;		/* default col			*/
	int	D_ysize;	/* default y size		*/
	int	D_xsize;	/* default x size		*/
};
struct	def__loc D[MAX_USERS];	/* I am D[0]			*/

struct	who__list		/* who_list structure		*/
{
	char	W_tty[8];	/* tty				*/
	char	W_username[32];	/* username			*/
};
struct	who__list W[64];	/* The who_list			*/
int	NUM_W;			/* # of users on the system	*/

char	STRING[BUFSIZ];		/* General purpose string	*/
int	NUM_T;			/* # of users you have		*/
int	NUM_C;			/* # of users connectd to you	*/
int	ACTIVE_MASK;		/* mask of active descriptors	*/
int	SMART;			/* TRUE if terminal is smart	*/
int	SNOOPER;		/* T element for the Snooper	*/

int	errno;			/* standard error noumber	*/
int	sock_io();		/* a place to go on SIGURG	*/
int	goodbye();		/* a place to go and die	*/
int	_pipe_();		/* "The Universe is defective"	*/
int	_death_();		/* program bombed badley!	*/

struct	timeval TIMEOUT;	/* timeout structure		*/

extern	char *enter_line();	/* input routine		*/

SHAR_EOF
cat - << \SHAR_EOF > hello.c

#include "header.h"

int	DEF_LENGTH, DEF_WIDTH;
int	NUM_DOWN, NUM_ACROSS;

hello()
{
	int	a;
	int	y, x;

	/*
	printf("# of lines: "); LINES = atoi(gets(STRING));
	printf("# of cols : "); COLS  = atoi(gets(STRING));
	*/

	initscr();
	crmode(); noecho();

	signal(SIGQUIT,SIG_IGN);
	signal(SIGINT, goodbye);
	/* signal(SIGPIPE, _pipe_); */
	signal(SIGSEGV,_death_);
	signal(SIGBUS, _death_);

	if (*CM && LINES >= 8 && COLS >= 32)
		SMART = 1;
	else
		SMART = 0;

	TIMEOUT.tv_sec =  5;	/* 5 seconds			*/
	TIMEOUT.tv_usec = 0;	/* 0 microseconds		*/

	SCREEN = newwin(LINES,COLS,0,0);
	clearok(SCREEN,0);
	LINE = newwin(1,COLS,LINES-1,0);

	DEF_LENGTH = 8;
	DEF_WIDTH  = (COLS - 8) / 2;
	if (DEF_WIDTH > 32)
		DEF_WIDTH = 32;
	NUM_DOWN   = LINES / DEF_LENGTH;
	NUM_ACROSS = (COLS - 8) / DEF_WIDTH;

	for (a=0; a < MAX_USERS; a++)
	{
		D[a].D_ysize = DEF_LENGTH;
		D[a].D_xsize = DEF_WIDTH;
		D[a].D_y = LINES - DEF_LENGTH;
		D[a].D_x = COLS  - DEF_WIDTH;
	}

	for (x=a=0; x < NUM_ACROSS; x++)
	for (y=0; y < NUM_DOWN; y++,a++)
	{
		D[a].D_y = y * DEF_LENGTH;
		D[a].D_x = x * DEF_WIDTH + 8;
	}

	p_setup_me();
}

SHAR_EOF
cat - << \SHAR_EOF > main.c

#include "header.h"

#define	VERSION			"2.0"

/***********************************************************************
 *
 *	____
 *	|   \               o   |	     A multi-user communication
 *	|___/  _  _  _ __  _.  _|_    ___    program by:
 *	|      |  |  |/     |   |    /___>
 *	|      |/\|  |     _|_  |_/  \___	Robert Scott Puchyr,
 *						Dalhousie University,
 *						Halifax, Nova Scotia,
 *						Canada,
 *						April 9, 1985
 *
 ***********************************************************************/

main(argc,argv)
int	argc;
char	**argv;
{
	printf("Pwrite. version %s.\nPress ESC ? for help.\n\n",VERSION);
	hello();
	do_pwrite();
	goodbye();
}

do_pwrite()
{
	while (1)
	{
		sock_io();
		if (NUM_C < NUM_T)
			p_beg();
	}
}

sock_io()
{
	char	S[BUFSIZ];
	int a, i, m;

	m = s_select(ACTIVE_MASK);

	if (m & 1)
	{
		read(0,&a,1);
		eval_key(a);
	}

	if (m <= 1)
		return(0);

	if (m & (1 << T[0].__sock))
	{
		p_answer();
	}

	for (a=1; a < NUM_T; a++)
	{
		if (m & (1 << T[a].__sock) && T[a].__connected)
		{
			i = read(T[a].__sock,S,BUFSIZ);
			if (i <= 0)
			{
				sprintf(STRING,"%c\n\"%s\" LOS \"%s\".%c",BEGIN_SECRET,T[0].__tty,T[a].__tty,END_SECRET);
				p_delete(a);
				p_sendstr(STRING);
			}
			else
			{
				S[i] = NULL;
				w_writestr(a,S);
			}
		}
	}
}

SHAR_EOF
cat - << \SHAR_EOF > pwrite.man



PWRITE(1)           UNIX Programmer's Manual            PWRITE(1)



NAME
     pwrite - multi-user ``talk'' program

SYNOPSIS
     pwrite

DESCRIPTION
     _P_w_r_i_t_e allows a user to communicate with a number of other
     users who are logged into the computer. There are no command
     line arguments for _p_w_r_i_t_e.  After entering _p_w_r_i_t_e, a box or
     window labeled with your username and terminal tty will
     appear in the upper left portion of your screen. There are
     two modes in _p_w_r_i_t_e, talk mode, and command mode. The ESCAPE
     button functions as a toggle switch between talk mode and
     command mode. The following commands are available in com-
     mand mode.

     a         - Add a user. A ``ghost'' window will appear on
               the screen.  When adding a user, the user's name
               or the user's terminal tty number are valid
               inputs. Ghost windows, drawn with dotted lines,
               are used to indicate incomplete connections.

     d         - Delete a connection. Deleting yourself will exit
               you from the program.

     r         - Ring. All unanswered connections, ghost windows,
               are rung by sending a note to the expected users.
               [NO RING] appears if a user has his write permis-
               sion off. You are free to ring as frequently or
               infrequently as you wish.

     w         - See who is on the system.

     ?         - Help. A few help windows are displayed in the
               lower right area of the screen.

     _P_w_r_i_t_e allows a maximun of 12 windows on the screen. You are
     free to change the windows within command mode.  Included in
     command mode are four corner posts, [ [  ] ] , which are
     used as sights for changing windows. The window on which
     they are placed is called the _c_u_r_r_e_n_t _w_i_n_d_o_w.  The commands
     for customizing the windows within command mode are:

     h j k l   - Moves the sights left (h), down (j), up (k), or
               right (l) respectively.

     H J K L   - Changes size by moving the lower right sight
               left, down, up, or right while holding the upper
               left sight stationary.

     SPACE     - Select current window. With a number of windows



Printed 4/29/85           9 April 1985                          1






PWRITE(1)           UNIX Programmer's Manual            PWRITE(1)



               on your screen, pressing SPACE will allow you to
               select a different current window.

     @         - Actually move and resize the current window to
               fit within the four sights.  Moving the sights
               alone will not change the current window's loca-
               tion or size.  The @ command does.

FILES
     /tmp/.pw_*               - communication sockets

SEE ALSO
     talk(1), mesg(1)

BUGS
     _P_w_r_i_t_e is heavily dependent on sockets.

     The program tends to boldly state that the current Universe
     is defective. This is done when an unknown SIGPIPE error
     ocurrs with the sockets.



































Printed 4/29/85           9 April 1985                          2



SHAR_EOF
cat - << \SHAR_EOF > socket_lib.c

#include "header.h"


/*****************************************************************************
 *
 *	SCOKET_ID = make_a_socket();
 *
 *		make_a_socket() creates a socket local to the system and
 *		binds the name FILE_NAME to it. This function is called
 *		by new_server_socket() and new_client_socket(). A SOCKET_ID
 *		is returned on success, -1 if not.
 *
 *****************************************************************************/

make_a_socket()
{
	int	SOCKET_ID;

	errno = 0;
	if ((SOCKET_ID = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
	{
		return(-1);
	}

	return(SOCKET_ID);
}


/*****************************************************************************
 *
 *	SOCKET_ID = s_new_server(FILE_NAME);
 *
 *		s_new_server() creates a socket ready for connection
 *		by connect_server(). This function returns a SOCKET_ID
 *		if successful, otherwise -1.
 *	
 *****************************************************************************/

s_new_server(FILE_NAME)
char	*FILE_NAME;
{
	int	SOCKET_ID;
	struct	sockaddr SOCKET_INFO;

	errno = 0;
	if ((SOCKET_ID = make_a_socket()) < 0)
	{
		return(-1);
	}

	SOCKET_INFO.sa_family = AF_UNIX;
	strcpy(SOCKET_INFO.sa_data, FILE_NAME);
	unlink(SOCKET_INFO.sa_data);
	errno = 0;
	if (bind(SOCKET_ID, &SOCKET_INFO, sizeof(SOCKET_INFO)) < 0)
	{
		return(-1);
	}

	chmod(SOCKET_INFO.sa_data, 0666);
	listen(SOCKET_ID,5);
	return(SOCKET_ID);
}


/*****************************************************************************
 *	
 *	SOCKET_ID = s_new_client();
 *
 *		s_new_client() creates and returns a SOCKET_ID ready
 *		ready for connrction using connect_client(). A -1 is returned
 *		if something messed up.
 *	
 *****************************************************************************/

s_new_client()
{
	int	SOCKET_ID;

	errno = 0;
	if ((SOCKET_ID = make_a_socket()) < 0)
	{
		return(-1);
	}

	return(SOCKET_ID);
}


/*****************************************************************************
 *
 *	s_connect_server(SOCKET_ID, FILE_NAME);
 *
 *		s_connect_server() connects the already created SOCKET_ID
 *		with a UNIX file. -1 is returned if the connection cannot
 *		be made. Otherwise, the filename of the client's socket
 *		placed in FILE_NAME and a SOCKET_ID file descriptor is
 *		for the newly accpeted socket is returned.
 *	
 *****************************************************************************/

s_connect_server (SOCKET_ID, FILE_NAME)
int	SOCKET_ID;
char	*FILE_NAME;
{
	int	NEW_SOCKET;
	int	namelen;
	struct	sockaddr SOCKET_INFO;

	namelen = sizeof(SOCKET_INFO);
	errno = 0;
	if ((NEW_SOCKET = accept(SOCKET_ID, &SOCKET_INFO, &namelen)) < 0)
	{
		return(-1);
	}

	strcpy(FILE_NAME,SOCKET_INFO.sa_data);
	return(NEW_SOCKET);
}


/*****************************************************************************
 *	
 *	s_connect_client(SOCKET_ID, FILE_NAME);
 *
 *		s_connect_client() connects a SOCKET_ID already created with
 *		new_client_socket() and connects it (with the UNIX file
 *		FILE_NAME). Zero is returned upon success, -1 if you're not
 *		so fortunate.
 *	
 *****************************************************************************/

s_connect_client (SOCKET_ID, FILE_NAME)
int	SOCKET_ID;
char	*FILE_NAME;
{
	int	namelen;
	struct	sockaddr SOCKET_INFO;

	namelen = sizeof(SOCKET_INFO);
	SOCKET_INFO.sa_family = AF_UNIX;
	strcpy(SOCKET_INFO.sa_data, FILE_NAME);
	errno = 0;
	return(connect(SOCKET_ID, &SOCKET_INFO, namelen));
}


/*****************************************************************************
 *
 *	s_select(MASK);
 *
 *****************************************************************************/

s_select (MASK)
int	MASK;
{
	int	mask;

	mask = MASK;
	select(20, &mask, 0, 0, &TIMEOUT);
	return(mask);
}


/*****************************************************************************
 *
 *	s_read(SOCKET_ID, STRING, NUM_CHARS);
 *
 *		s_read() reads NUM_CHARS bytes from the socket SOCKET_ID
 *		and places them in STRING. This function returns -1 if
 *		reading is not possible from SOCKET_ID, this means that
 *		the socket has been closed down from the other end.
 *		A SOCKET_ID of 0 will read from standard input. If no
 *		problems occur, the number of characters actually read
 *		from the socket is returned.
 *
 *****************************************************************************/

s_read(SOCKET_ID,STRING,NUM_CHARS)
int	SOCKET_ID;
char	*STRING;
int	NUM_CHARS;
{
	int	num_read;

	if (s_select(SOCKET_ID))
	{
		if ((num_read = read(SOCKET_ID, STRING, NUM_CHARS)) <= 0)
		{
			return(-1);	/* Socket was closed from other side */
		}
		STRING[num_read] = NULL;
		return(num_read);
	}

	return(0);
}


/*****************************************************************************
 *
 *	s_write(SOCKET_ID, STRING);
 *
 *		s_write() writes a NULL terminated STRING to the socket
 *		specified by SOCKET_ID. The number of characters written to
 *		the socket is returned. -1 is returned something goes wrong.
 *
 *****************************************************************************/

s_write(SOCKET_ID,STRING)
int	SOCKET_ID;
char	*STRING;
{
	return(write(SOCKET_ID, STRING, strlen(STRING)));
}


/*****************************************************************************
 *
 *	s_delete(SOCKET_ID);
 *
 *		s_delete() closes down a socket, as specified by
 *		SOCKET_ID;
 *
 *****************************************************************************/

s_delete(SOCKET_ID)
int SOCKET_ID;
{
	errno = 0;
	shutdown(SOCKET_ID, 2);
	return(close(SOCKET_ID));
}

SHAR_EOF
cat - << \SHAR_EOF > users.c

#include "header.h"

FILE *fp;
struct utmp ubuf;

p_setup_me()
{
	CURRENT_WINDOW = NUM_T = NUM_C = 0;
	SNOOPER = -1;
	ACTIVE_MASK = 0 | (1 << 0);	/* 0 is stdin		*/

	strcpy(T[0].__tty,rindex(ttyname(0),'/') + 4);
	strcpy(T[0].__username,getenv("USER"));
	sprintf(T[0].__file,"/tmp/.pw_%s",T[0].__tty);
	if ((T[0].__sock = s_new_server(T[0].__file)) < 0)
		panic("Error creating your socket.",0);

	T[0].__connected = 1;
	w_new_user(0);
	NUM_T++; NUM_C++;
	ACTIVE_MASK |= (1 << T[0].__sock);
}

p_delete(n)
int	n;
{
	int	a;

	if (T[n].__connected)
		NUM_C--;

	delwin(T[n].__win1);
	delwin(T[n].__win2);

	if (SMART)
	{
		wclear(T[n].__win1);
		wclear(T[n].__win2);
		wrefresh(T[n].__win1);
		wrefresh(T[n].__win2);
	}
	else
	{
		printf("\nUser '%s' on 'tty%s' has disconnected%c\n",T[n].__username,T[n].__tty,7);
	}

	if (T[n].__sock >= 0)
	{
		ACTIVE_MASK &= ~(1 << T[n].__sock);
		s_delete(T[n].__sock);
	}
	strcpy(T[n].__tty,"");
	strcpy(T[n].__username,"");
	strcpy(T[n].__file,"");
	T[n].__sock = -1;
	T[n].__dead = 1;
	T[n].__connected = 0;

	if (NUM_C < 1 ||  n == 0)
		goodbye();

	if (n == SNOOPER)
		SNOOPER = -1;

	if (n == CURRENT_WINDOW)
		w_nextcorners();
}

p_answer()
{
	char	t[8], u[32];
	int	a, c, s;

	strcpy(STRING,"");
	s = s_connect_server(T[0].__sock,STRING);

	if (s < 0)
		return(-1);

	a = read(s,STRING,BUFSIZ);	/* "j6|puchyr|"	*/
	STRING[a] = NULL;
	STRING[2] = NULL;
	strcpy(t,STRING);
	aslstr(STRING,3);
	for (a=0; STRING[a] != '|'; u[a] = STRING[a++]);
	u[a] = NULL;
	a = p_add(t,u,s,1);
	w_new_user(a);
	sprintf(STRING,"%c\n\"%s\" ANS \"%s\".%c",BEGIN_SECRET,T[0].__tty,T[a].__tty,END_SECRET);
	p_sendstr(STRING);
	NUM_C++;
}

p_add(t,u,s,c)
char	*t, *u;
int	s, c;
{
	int	a;

	if (NUM_C >= MAX_USERS)
	{
		panic("You cannot add any more.",4);
		return(0);
	}

	for (a=1; a < NUM_T; a++)
	{
		if (T[a].__dead)
			break;
	}

	sprintf(T[a].__file,"/tmp/.pw_%s",t);
	strcpy(T[a].__tty,t);
	strcpy(T[a].__username,u);
	T[a].__dead = 0;
	T[a].__secret = 0;
	T[a].__sock = s;
	T[a].__connected = c;

	if (c)
		ACTIVE_MASK |= (1 << T[a].__sock);

	if (a >= NUM_T)
		NUM_T++;

	CURRENT_WINDOW = a;
	return(a);
}

p_beg()
{
	int	a;

	for (a=0; a < NUM_T; a++)
	{
		if (!T[a].__dead && !T[a].__connected)
		{
			if (T[a].__sock < 0)
				T[a].__sock = s_new_client();

			if (s_connect_client(T[a].__sock,T[a].__file) >= 0)
			{
				sprintf(STRING,"%s|%s|",T[0].__tty,T[0].__username);
				s_write(T[a].__sock,STRING);
				T[a].__connected = 1;
				delwin(T[a].__win1);
				delwin(T[a].__win2);
				w_new_user(a);
				sprintf(STRING,"%c\n\"%s\" GOT \"%s\".%c",BEGIN_SECRET,T[0].__tty,T[a].__tty,END_SECRET);
				p_sendstr(STRING);
				NUM_C++;
				ACTIVE_MASK |= (1 << T[a].__sock);
			}
		}
	}
}

p_who()
{
	if ((fp = fopen("/etc/utmp","r")) == NULL)
	{
		panic("Can't open /etc/utmp. (Major Bug!!)\n",0);
	}

	for (NUM_W=0; fread((char *) &ubuf, sizeof(ubuf), 1, fp) == 1; )
	{
		if (*ubuf.ut_name)
		{
			strcpy(W[NUM_W].W_username,ubuf.ut_name);
			W[NUM_W].W_tty[0] = ubuf.ut_line[3];
			W[NUM_W].W_tty[1] = ubuf.ut_line[4];
			W[NUM_W].W_tty[2] = NULL;
			NUM_W++;
		}
	}

	fclose(fp);
}

p_check(s,how)
char	*s;
int	how;
{
	int	a, l;

	if (how)
	{
		p_who();
		if ((l = strlen(s)) <= 2)
		{
			for (a=0; a < NUM_W; a++)
			{
				if (!strcmp(W[a].W_tty,s))
					break;
			}
		}
		else
		{
			for (a=0; a < NUM_W; a++)
			{
				if (!strcmp(W[a].W_username,s))
					break;
			}
		}

		if (a < NUM_W)
			return(a);
		else
			return(-1);
	}
	else
	{
		if ((l = strlen(s)) <= 2)
		{
			for (a=0; a < NUM_T; a++)
			{
				if (!strcmp(T[a].__tty,s))
					break;
			}
		}
		else
		{
			for (a=0; a < NUM_T; a++)
			{
				if (!strcmp(T[a].__username,s))
					break;
			}
		}

		if (a < NUM_T)
			return(a);
		else
			return(-1);
	}
}

p_sendch(c)
int	c;
{
	int	a;

	for (a=1; a < NUM_T; a++)
		if (T[a].__connected && !T[a].__dead && T[a].__sock >= 0)
			write(T[a].__sock,&c,1);
}

p_sendstr(s)
char	*s;
{
	int	a;

	for (a=1; a < NUM_T; a++)
		if (T[a].__connected && !T[a].__dead && T[a].__sock >= 0)
			write(T[a].__sock,s,strlen(s));
}

p_secretcommand(n,c)
int	n, c;
{
	char	temp[16];
	int	a;

	switch(c)
	{
		case  23:
				sprintf(STRING,"%c\n\"%s\" HAS",BEGIN_SECRET,T[0].__tty);
				for (a=0; a < NUM_T; a++)
				{
					if (!T[a].__dead)
					{
						strcat(STRING," \"");
						strcat(STRING,T[a].__tty);
						strcat(STRING,"\"");
					}
				}
				sprintf(temp,".%c",END_SECRET);
				strcat(STRING,temp);
				write(T[n].__sock,STRING,strlen(STRING));
				break;
	}
}

SHAR_EOF
cat - << \SHAR_EOF > utils.c

#include "header.h"

panic(s,n)
char *s;
int n;
{
	if (SMART)
	{
		do_line(s);
	}
	else
	{
		printf("\n%s\n",s);
	}
	fflush(stdout);

	if (n)
		{ sleep(n); }
	else
		{ sleep(5); goodbye(); }
}

do_line(s)
char *s;
{
	int a, l;

	if (SMART)
	{
		wclear(LINE);
		if ((l = strlen(s)) < COLS)
		{
			waddstr(LINE,s);
		}
		else
		{
			for (a=16; a > 0; a--)
				waddch(LINE,s[l-a]);
		}
		if (!*s)
			touchwin(LINE);
		wrefresh(LINE);
	}
	else
	{
		printf("\n%s\n",s);
	}
}

_pipe_()
{
	signal(SIGINT, SIG_IGN);
	if (SMART)
		mvcur(0,COLS-1,LINES-9,0);

	printf("\n");
	printf(".----------------------.\n");
	printf("| THIS UNIVERSE SEEMS  |\n");
	printf("| TO BE DEFECTIVE!     |\n");
	printf("|                      |\n");
	printf("| PLEASE TRY ANOTHER.  |\n");
	printf("|         Press RETURN |\n");
	printf("`----------------------'\n");
	gets(STRING);
	endwin(); exit(0);
}

_death_()
{
	signal(SIGINT, SIG_IGN);
	if (SMART)
		mvcur(0,COLS-1,LINES-9,0);

	printf("\n");
	printf(".----------------------.\n");
	printf("|    .-----* A serious |\n");
	printf("|   _|_   system error |\n");
	printf("|  /   \\  has occurred |\n");
	printf("| | TNT |              |\n");
	printf("|  \\___/  Press RETURN |\n");
	printf("`----------------------'\n");
	gets(STRING);
	endwin(); exit(0);
}

char *enter_line(s)
char *s;
{
	char	e_line[BUFSIZ], c;
	int	a, no_echo;

	if (!SMART)
	{
		printf("\n%s",s);
		echo(); nocrmode();
		gets(e_line);
		noecho(); crmode();
		return(e_line);
	}

	no_echo = 0;
	if (*s == '^')
		{ s++; no_echo = 1; }

	do_line(s);

	a = 0;
	for (a=e_line[0]=0; (c=getchar()) != RET && c != ESC; )
	{
		switch(c)
		{
			case 127:
			case   8:	if (a > 0)
						e_line[--a] = NULL;
					break;
			case  21:	a = 0; e_line[a] = NULL;
					break;
			default:	if (a < BUFSIZ && c >= ' ')
					{
						e_line[a++] = c;
						e_line[a] = NULL;
					}
					break;
		}
		if (!no_echo)
			do_line(e_line);
	}

	if (c == ESC)
		strcpy(e_line,"");

	return(e_line);
}

aslstr(s,n)
char	*s;
int	n;
{
	int	a;

	for (a=0; s[a] = s[a+n]; a++);
}

SHAR_EOF
cat - << \SHAR_EOF > windows.c

#include "header.h"

WINDOW	*W_WINDOW;
WINDOW	*H_WINDOW;

w_new_user(n)
int	n;
{
	int	a, b, c, x, y;

	T[n].__win1 = newwin(D[n].D_ysize,D[n].D_xsize,D[n].D_y,D[n].D_x);
	T[n].__win2 = newwin(D[n].D_ysize-3,D[n].D_xsize-3,D[n].D_y+2,D[n].D_x+2);
	if (T[n].__connected)
	{
		box(T[n].__win1,'|','-');
	}
	else
	{
		for (y=0; y < D[n].D_ysize; y++)
		{
			mvwaddch(T[n].__win1,y,0,'.');
			mvwaddch(T[n].__win1,y,D[n].D_xsize-1,'.');
		}

		for (x=0; x < D[n].D_xsize; x+=2)
		{
			mvwaddch(T[n].__win1,D[n].D_ysize-1,x,'-');
			mvwaddch(T[n].__win1,0,x+1,'-');
		}
	}

	mvwaddch(T[n].__win1,0,0,'.');
	mvwaddch(T[n].__win1,D[n].D_ysize-1,0,'`');
	mvwaddch(T[n].__win1,0,D[n].D_xsize-1,'.');
	mvwaddch(T[n].__win1,D[n].D_ysize-1,D[n].D_xsize-1,'\'');

	sprintf(STRING,"%s  %s",T[n].__tty,T[n].__username);
	for (a=0; STRING[a] && a+3 < T[n].__win1->_maxx; a++)
		mvwaddch(T[n].__win1,0,a+2,STRING[a]);

	scrollok(T[n].__win2,1);
	w_setcorners(n);

	if (SMART)
	{
		wrefresh(T[n].__win1);
		wrefresh(T[n].__win2);
	}
	else
	{
		printf("\nUser '%s' on 'tty%s' has joined.%c\n",T[n].__username,T[n].__tty,7);
	}
}

w_setcorners(n)
int	n;
{
	CURRENT_WINDOW = n;
	C[0].C_y = C[1].C_y = D[n].D_y;
	C[0].C_x = C[2].C_x = D[n].D_x;
	C[2].C_y = C[3].C_y = D[n].D_y + D[n].D_ysize-1;
	C[1].C_x = C[3].C_x = D[n].D_x + D[n].D_xsize-1;
}

w_addch(n,c)
int	n, c;
{
	switch(c)
	{
		case BEGIN_SECRET:
				T[n].__secret = 1;
				return(0);
				break;
		case END_SECRET:
				T[n].__secret = 0;
				return(0);
				break;
		case 23:
				p_secretcommand(n,c);
				return(0);
				break;
	}

	if (T[n].__secret)
	{
		if (SNOOPER < 0)
			return(0);
		else
			n = SNOOPER;
	}

	if (SMART)
	{
		if (c == 8)
		{
			w_backspace(n);
		}
		else if (T[n].__win2->_curx == T[n].__win2->_maxx-1)
		{
			if (c == RET || c == SPC)
				waddch(T[n].__win2,RET);
			else
			{
				w_wordwrap(n);
				waddch(T[n].__win2,c);
			}
		}
		else
		{
			waddch(T[n].__win2,c);
		}

		if (n == SNOOPER)
			wrefresh(T[SNOOPER].__win2);
	}
	else
	{
		putchar(c);
	}
}

w_writech(n,c)
int	n, c;
{
	w_addch(n,c);
	if (SMART)
		wrefresh(T[n].__win2);
	else
		fflush(stdout);
}

w_writestr(n,s)
int	n;
char	*s;
{
	for (; *s; w_addch(n,*s++));
	if (SMART)
		wrefresh(T[n].__win2);
	else
		fflush(stdout);
}

w_wordwrap(n)
int	n;
{
	int	c, x, y, s;

	waddch(T[n].__win2,RET);

	y = T[n].__win2->_cury;
	x = T[n].__win2->_maxx - 2;

	for ( ; x > 0 && T[n].__win2->_y[y-1][x] != SPC; x--);

	s = x + 1;

	if (x)
	{
		for (x=0; s < T[n].__win2->_maxx - 1; x++,s++)
		{
			wmove(T[n].__win2,y-1,s);
			c = winch(T[n].__win2);
			waddch(T[n].__win2,SPC);
			mvwaddch(T[n].__win2,y,x,c);
		}
	}
}

w_backspace(n)
int	n;
{
	int	y, x;

	if (!T[n].__win2->_curx && (y = T[n].__win2->_cury))
	{
		wmove(T[n].__win2,y-1,T[n].__win2->_maxx-1);
		for (x=T[n].__win2->_maxx-2; x >= 0; x--)
		{
			wmove(T[n].__win2,y-1,x);
			if (winch(T[n].__win2) != SPC)
				break;
		}
		wmove(T[n].__win2,y-1,x+1);
	}
	else
		waddch(T[n].__win2,8);
}

w_who()
{
	int	a, i, j, x, y;

	p_who();
	y = LINES - 4;
	x = NUM_W / y;
	if (NUM_W % y)
		x++;
	if (NUM_W < y)
		y = NUM_W;
	x *= 16;

	if (SMART)
		W_WINDOW = newwin(y+3,x+4,LINES-(y+4),0);
	else
		W_WINDOW = newwin(y+3,x+4,0,0);

	box(W_WINDOW,'|','-');
	mvwaddch(W_WINDOW,0,0,'.');
	mvwaddch(W_WINDOW,y+2,0,'`');
	mvwaddch(W_WINDOW,0,x+3,'.');
	mvwaddch(W_WINDOW,y+2,x+3,'\'');
	mvwaddstr(W_WINDOW,0,2,"who");

	for (i=j=a=0; a < NUM_W; a++)
	{
		if (p_check(W[a].W_tty,0) != -1)
		{
			wstandout(W_WINDOW);
			mvwaddstr(W_WINDOW,j+2,(i*16)+2,"              <");
		}

		mvwaddstr(W_WINDOW,j+2,(i*16)+2,W[a].W_tty);
		mvwaddstr(W_WINDOW,j+2,(i*16)+6,W[a].W_username);

		wstandend(W_WINDOW);

		j++;
		if (j >= y)
		{
			j = 0;
			i++;
		}
	}

	wrefresh(W_WINDOW);
	delwin(W_WINDOW);
}

w_redraw(n)
int	n;
{
	int	a;

	if (SMART)
	{
		werase(SCREEN);
		for (a=0; a < NUM_T; a++)
		{
			if (!T[a].__dead)
			{
				overwrite(T[a].__win1,SCREEN);
				overwrite(T[a].__win2,SCREEN);
			}
		}

		if (n)
			touchwin(SCREEN);

		wrefresh(SCREEN);
	}
}

w_help()
{
	if (SMART)
		H_WINDOW = newwin(14,40,LINES-15,COLS-40);
	else
		H_WINDOW = newwin(14,40,0,0);

	wprintw(H_WINDOW,".-help---------------------------------.");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"| Talk-mode commands:                  |");
	wprintw(H_WINDOW,"| ===================                  |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|   ^L   - redraw screen               |");
	wprintw(H_WINDOW,"|   ^D   - exit pwrite                 |");
	wprintw(H_WINDOW,"|   ESC  - enter Command Mode          |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"`-Press SPACE for more-----------------'");
	wrefresh(H_WINDOW);
	while (getchar() != SPC);

	wclear(H_WINDOW);
	wprintw(H_WINDOW,".-help---------------------------------.");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"| Command Mode commands:               |");
	wprintw(H_WINDOW,"| ======================               |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|   a    - add a user                  |");
	wprintw(H_WINDOW,"|   d    - delete a user               |");
	wprintw(H_WINDOW,"|   w    - see who is on the system    |");
	wprintw(H_WINDOW,"|   r    - ring all unanswered users   |");
	wprintw(H_WINDOW,"|   ^L   - redraw screen               |");
	wprintw(H_WINDOW,"|   ESC  - return to Talk Mode         |");
	wprintw(H_WINDOW,"|   ?    - show these help pages       |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"`-Press SPACE for more-----------------'");
	wrefresh(H_WINDOW);
	while (getchar() != SPC);

	wclear(H_WINDOW);
	wprintw(H_WINDOW,".-help---------------------------------.");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|   Moving windows around:             |");
	wprintw(H_WINDOW,"|   ----------------------             |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"|   h j k l - move the four border     |");
	wprintw(H_WINDOW,"|             corners left, down, up,  |");
	wprintw(H_WINDOW,"|             or right.                |");
	wprintw(H_WINDOW,"|   H J K L - change size.             |");
	wprintw(H_WINDOW,"|   SPACE   - select another window.   |");
	wprintw(H_WINDOW,"|   @       - move window to fit       |");
	wprintw(H_WINDOW,"|             within the four corners. |");
	wprintw(H_WINDOW,"|                                      |");
	wprintw(H_WINDOW,"`-Press SPACE--------------------------'");
	wrefresh(H_WINDOW);
	while (getchar() != SPC);
	delwin(H_WINDOW);
	w_redraw(1);
}

w_corners(n)
int	n;
{
	int	a;

	if (!SMART)
		return(0);

	if (n)
	{
		for (a=0; a < 4; a++)
		{
			wmove(curscr,C[a].C_y,C[a].C_x);
			C[a].C_under = winch(curscr);
			wmove(SCREEN,C[a].C_y,C[a].C_x);
			wstandout(SCREEN);
			if (a % 2)
				waddch(SCREEN,']');
			else
				waddch(SCREEN,'[');
			wstandend(SCREEN);
		}
	}
	else
	{
		for (a=0; a < 4; a++)
		{
			wmove(SCREEN,C[a].C_y,C[a].C_x);
			waddch(SCREEN,C[a].C_under);
		}
	}

	wrefresh(SCREEN);
}

w_mvcorners(y,x,j,i)
int	y, x, j, i;
{
	if (!SMART)
		return(0);

	C[0].C_y += y; C[0].C_x += x;
	C[1].C_y += y; C[1].C_x += i;
	C[2].C_y += j; C[2].C_x += x;
	C[3].C_y += j; C[3].C_x += i;

	if (C[0].C_y < 0 || C[0].C_y >= (LINES - (C[2].C_y - C[0].C_y)))
	{
		C[0].C_y -= y; C[1].C_y -= y;
	}
	if (C[0].C_x < 0 || C[0].C_x >= (COLS  - (C[1].C_x - C[0].C_x)))
	{
		C[0].C_x -= x; C[2].C_x -= x;
	}
	if (C[3].C_y < (C[0].C_y+4) || C[3].C_y >= LINES)
	{
		C[2].C_y -= j; C[3].C_y -= j;
	}
	if (C[3].C_x < (C[0].C_x+7) || C[3].C_x >= COLS)
	{
		C[1].C_x -= i; C[3].C_x -= i;
	}
}

w_change()
{
	if (!SMART)
		return(0);

	delwin(T[CURRENT_WINDOW].__win1);
	delwin(T[CURRENT_WINDOW].__win2);
	D[CURRENT_WINDOW].D_ysize = C[2].C_y - C[0].C_y + 1;
	D[CURRENT_WINDOW].D_xsize = C[1].C_x - C[0].C_x + 1;
	D[CURRENT_WINDOW].D_y = C[0].C_y;
	D[CURRENT_WINDOW].D_x = C[0].C_x;
	w_new_user(CURRENT_WINDOW);
	w_redraw(1);
}

w_nextcorners()
{
	if (!SMART)
		return(0);

	do
	{
		if (++CURRENT_WINDOW >= NUM_T)
			CURRENT_WINDOW = 0;
	}
	while (T[CURRENT_WINDOW].__dead);
	w_setcorners(CURRENT_WINDOW);
}

SHAR_EOF
#That's all folks!
exit 0