[alt.sources] Multi user chat - Version 1.2

darcy@druid.uucp (D'Arcy J.M. Cain) (10/28/90)

-----------------------------------------------------------------------
NOTE: There was a n error in the first post this morning.  If you got
      it before the cancellation please ignore it.  This is the correct
      posting.  Sorry for any inconvenience.
-----------------------------------------------------------------------

Well here it is.  This time the header file is included.  I also dropped
the getopt.h and process.h include statements and just included the
relevant prototypes and extern declarations where required.  Hopefully
this will avoid some problems that people have been having.

You will need an ANSI compiler to compile this or else a filter such as
unprotoize in order to compile this.

This has been tested on ESIX 3.2 Rel D compiling with GNU C.  I
used the standard libraries but the header files are a modified
version of the ones that came with the development set.  I believe
that the result is a fairly compliant ANSI environment but if you
have an ANSI compiler that chokes on this I would certainly like
to hear from you.

I would also like to get your comments on the package itself.  If
you have any suggestions for added features or improvements please
send them along.  I hope to have a network (TLI) version soon.  I
would create a sockets version but I don't have any experience in
that area or a system to play with.  If anyone wants to create a
chc_sock.c and chs_sock.c file I will gladly include it in the
distribution.

Enjoy.

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 10/27/1990 20:20 UTC by darcy@druid
# Source directory /usr/darcy/work/chat
#
# existing files WILL be overwritten
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   9540 -rw-r--r-- chat.c
#   4071 -rw-r--r-- chatter.c
#   1586 -rw-r--r-- chc_msg.c
#   2340 -rw-r--r-- chs_msg.c
#   1164 -r--r--r-- chat.h
#    537 -r-xr--r-- lchat
#    682 -r-xr-xr-x chatyell
#   2516 -r--r--r-- chat.readme
#    520 -r--r--r-- chat.make
#
# ============= chat.c ==============
sed 's/^X//' << 'SHAR_EOF' > 'chat.c' &&
X /*
X
chat
Written by D'Arcy J.M. Cain
All rights reserved
X
This is the front end to the chatter program.  It is run by any number
of users who wish to communicate conference style with each other.  The
chatter program serves each user.
X
Permission is hereby granted to freely copy and redistribute this
software, provided that the author is clearly credited in all
copies and derivations.  This software is provided ``As Is'' and
without any express or implied warranties.
X
*/
X
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<curses.h>
#include	<signal.h>
#include	<ctype.h>
#include	<errno.h>
#include	"chat.h"
X
char chat_rcsid[] = "$Id: chat.c,v 1.2 90/10/26 19:55:16 darcy Exp Locker: darcy $\b ";
X
extern char	chc_rcsid[];
X
#ifndef	NO_CHG_NAME
static char	*options = "hn:f:";
#else
static char	*options = "hf:";
#endif
X
extern int		getopt(int argc, char **argv, const char *opts);
extern int		optind, opterr;
extern char		*optarg;
X
extern int		execlp(const char *path, const char *arg0, ...);
extern int		fork(void);
extern int		wait(int *stat_loc);
X
static FILE		*cap_file = NULL;
static WINDOW	*disp, *inp;
X
#ifdef	HALFDELAY_BROKEN
int		chat_suspended = 0;
#endif
X
void	cleanup(int sig)			/* come here on just about any signal */
{
X	while (chk_q())					/* clean up your own mess */
X		;
X
X	if (cap_file != NULL)
X		fclose(cap_file);
X
X	chc_send("x");
X
X	mvwprintw(inp, 1, 1, "%-77s", "    So long...   It's been nice chatting");
X	wrefresh(disp);					/* display any leftovers */
X	wrefresh(inp);					/* display polite message */
X
X	delwin(disp);
X	delwin(inp);
X	endwin();
X	ch_close();
X	exit(0);
}
X
void	disp_str(char *str)	/* add latest string to list just above box */
{
X	scroll(disp);				/* make room for it first */
X	mvwprintw(disp, LINES - 5, 0, "%s", str);
X
X	if (cap_file != NULL)		/* save to file if it is open */
X		fprintf(cap_file, "%s\n", str);
}
X
static int	need_to_refresh = 0;
X
int		chk_q(void)
{
X	char	msgbuf[256];
X
X	if (ch_get(msgbuf))
X	{
X		disp_str(msgbuf);			/*let user know what chatter has to say */
X
X		if (*msgbuf == 'e')			/* is chatter telling us to get lost? */
X		{
X			wrefresh(disp);			/* display anything pending */
X			cleanup(0);				/* take the Big Sleep */
X		}
X
X		need_to_refresh = -1;
X		return(1);					/* let caller know */
X	}
X
X	return(0);						/* nothing happened */
}
X
#ifndef	NO_SH_ESC
static void	run_command(char *s)
{
X	int		status, pid, w, tty;
X	void	(*istat)(), (*qstat)();
X
X	if ((tty = open("/dev/tty", 2)) == -1)
X	{
X		fprintf(stderr, "Can't open /dev/tty\n");
X		return;
X	}
X
X	if ((pid = fork()) == 0)
X	{
X		close(0); dup(tty);
X		close(1); dup(tty);
X		close(1); dup(tty);
X		close(tty);
X		execlp("sh", "sh", (*s ? "-c" : NULL), s, NULL);
X		exit(0);		/* note:  We don't care about failure here */
X	}
X
X	close(tty);
X	istat = signal(SIGINT, SIG_IGN);
X	qstat = signal(SIGQUIT, SIG_IGN);
X
X	while ((w = wait(&status)) != pid)
X		if ((w == -1) && (errno != EINTR))
X			break;
X
X	signal(SIGINT, istat);
X	signal(SIGQUIT, qstat);
}
#endif
X
void	help(void)
{
X	WINDOW	*w;
X	int		k;
X	static char *help_msg[] = {
X		"CHAT - multi user conference utility",
X		"Written by D'Arcy J.M. Cain (darcy@druid.uucp)",
X		"Copyright 1990 - All rights reserved",
X		"$Revision: 1.2 $\b " + 1,
X		"",
X		"chat commands consist of a slash (/) followed by a command",
X		"   ?     this Help screen",
#ifndef	NO_SH_ESC
X		"   !     shell Escape",
#endif
X		"   c     Change channel i.e: \"/c3\" - change to channel 3",
X		"         Note: \"/c*\" will change to first empty channel",
X		"   h     Same as ?",
#ifndef	NO_CHG_NAME
X		"   n     change Name to following string",
#endif
X		"   r     Redraw screen",
#ifndef	NO_SHOW_USERS
X		"   s     Show current users using chat",
#endif
X		"   x     eXit from chat",
X		"",
X		"                       Hit a key to continue ..."
X	};
#define	HELP_SZ	(sizeof(help_msg)/sizeof(char *))	
X
X	w = newwin(HELP_SZ + 3, 71, 1, 3);	/* get a new window */
X	box(w, 0, 0);						/* and box it */
X
X	for (k = 0; k < HELP_SZ; k++)		/*  display the help screen */
X		mvwprintw(w, k + 1, 3, "%s", help_msg[k]);
X
X	wrefresh(w);		/* show the screen */
X
X	while (wgetch(w) == ERR)
X		;				/* wait for keystroke */
X
X	delwin(w);			/* clear the window */
X	touchwin(disp);		/* update display window */
X	wrefresh(disp);		/* get everything back in sync */
X	wrefresh(inp);
}
X
int		main(int argc, char **argv)
{
X	int		c;
X	char	entry[512], *ptr = NULL, *ptr1, msgbuf[256];
X
X	if ((ptr1 = ch_init()) != NULL)
X	{
X		perror(ptr1);
X		return(1);
X	}
X
X	/* curses stuff */
X	initscr();
X	nonl();
X	noecho();
X	cbreak();
#ifndef	HALFDELAY_BROKEN
X	halfdelay(20);
#endif
X
X	/* make sure we always exit through cleanup */
X	signal(SIGINT, cleanup);
X	signal(SIGABRT, cleanup);
X	signal(SIGQUIT, cleanup);
X	signal(SIGKILL, cleanup);
X	signal(SIGTERM, cleanup);
X
X	disp = newwin(LINES - 4, COLS, 0, 0);	/* conversation window */
X	idlok(disp, TRUE);
X	scrollok(disp, TRUE);
X
X	inp = newwin(3, COLS, LINES - 4, 0);	/* input window */
X	box(inp, 0, 0);
X
X	mvwprintw(inp, 1, 1, "-> ");			/* prompt string */
X
X	while ((c = getopt(argc, argv, options)) != -1)
X	{
X		switch (c)
X		{
X			case '?':
X			default:
X				sprintf(entry, "%s: Invalid option %c", argv[0], c);
X				disp_str(entry);
X
X			case 'h':
X				sprintf(entry,
#ifndef	NO_CHG_NAME
X					"Usage: %s [-h] [-c channel] [-n name] [-f file]",
#else
X					"Usage: %s [-h] [-c channel] [-f file]",
#endif
X					argv[0]);
X				disp_str(entry);
X				cleanup(0);
X				break;
X
#ifndef	NO_CHG_NAME
X			case 'n':
X				ptr = optarg;
X				break;
#endif
X
X			case 'f':
X				if ((cap_file = fopen(optarg, "a")) == NULL)
X				{
X					sprintf(entry, "Can't open log file: %s", sys_errlist[errno]);
X					cleanup(0);
X				}
X				break;
X		}
X	}
X	
X	disp_str("chat - multi user conferencing utility");	/* signon string */
X	disp_str("Written by D'Arcy J.M. Cain");
X	disp_str("Copyright 1990 by D'Arcy J.M. Cain - All rights reserved");
X	disp_str("$Revision: 1.2 $\b " + 1);
X	disp_str("");
X
X	/* open chat link */
X	ch_init();
X	sprintf(entry, "o%s", (ptr == NULL) ? cuserid(NULL) : ptr);
X	chc_send(entry);
X	sleep(1);								/* messages are not FIFO     */
X
X	disp_str("Enter /h or /? for help");
X	disp_str("");
X	wrefresh(disp);
X
X	ptr = entry;
X	chc_send("s");							/* get list of current users */
X
X	for (;;)					/* loop till the cows come home */
X	{
X		while (chk_q())
X			;
X
X		if (need_to_refresh)
X			wrefresh(disp);		/* refresh display if necessary*/
X
X		wrefresh(inp);			/* refresh this to place cursor */
X
X		if ((c = wgetch(inp)) != ERR);	/* if got a character (not timed out) */
X		{
X			if (c == 4)
X				cleanup(0);	/* end of session ? */
X
X			if (c == '\r')	/* end of line ? */
X			{
X				*ptr = 0;					
X
X				if (*entry == '/')		/* command? */
X				{
X					switch (entry[1])
X					{
X						case 'v':		/* file versions.  undocumented */
X							disp_str(chat_rcsid + 1);
X							disp_str(chc_rcsid + 1);
X							chc_send("v");
X							break;
X
X						case '?':		/* request for help */
X						case 'h':
X							help();
X							break;
X
#ifndef	NO_SH_ESC
X						case '!':		/* shell escape */
X							ptr = entry + 2;
X							while (isspace(*ptr))
X								ptr++;
X
#ifdef	HALFDELAY_BROKEN
X							chat_suspended = 1;
#endif
X							endwin();
X							run_command(ptr);
X							printf("\n[Press 'Enter' to continue ...]");
X
X							while (gets(entry) == NULL)
X								;
X
X							doupdate();
#ifdef	HALFDELAY_BROKEN
X							chat_suspended = 0;
#endif
X							break;
#endif
X
X						case 'l':		/* redraw screen */
X						case 'r':
X							clearok(disp, TRUE);	/* update display window */
X							clearok(inp, TRUE);		/* update input window */
X							wrefresh(disp);			/* redraw both windows */
X							wrefresh(inp);
X							clearok(disp, FALSE);	/* reset display window */
X							clearok(inp, FALSE);	/* reset input window */
X							break;
X
#ifndef	NO_CHG_NAME
X						case 'n':		/* these are passed to chatter */
#endif
#ifndef	NO_SHOW_USERS
X						case 's':
#endif
X						case 'c':
X							ptr = entry + 2;
X							while (isspace(*ptr))
X								ptr++;
X							sprintf(msgbuf, "%c%s", entry[1], ptr);
X							chc_send(msgbuf);
X							break;
X
X						case 'x':		/* exit from chat */
X						case 'q':		/* for Dennis Breckenridge :-) */
X							cleanup(0);
X							break;
X
X						default:
X							beep();
X					}
X				}
X				else if (*entry)		/* else send it if not blank */
X				{
X					sprintf(msgbuf, "m%s", entry);
X					chc_send(msgbuf);
X				}
X
X				ptr = entry;	/* reset input box */
X				mvwprintw(inp, 1, 1, "%77s", "");
X				mvwprintw(inp, 1, 1, "-> ");
X			}
X			else if (c == '\b')	/* take care of backspaces */
X			{
X				if (ptr != entry)
X				{
X					wprintw(inp, "\b \b");
X					ptr--;
X				}
X			}
X			else if (c == '\t')	/* expand tabs */
X			{
X				ptr1 = ptr + 8 - ((ptr - entry) % 8);
X				while (ptr != ptr1)
X				{
X					*(ptr++) = ' ';
X					waddch(inp, ' ');
X				}
X			}
X			else if ((c >= ' ') && (c < 0x7f))	/* otherwise if printable */
X			{
X				if ((ptr - entry) > 62)			/* time for word wrap? */
X				{
X					*(ptr++) = c;				/* don't lose latest char */
X					*ptr = 0;					/* terminate string */
X
X					while (!isspace(*ptr))		/* find latest space */
X					{
X						if (--ptr == entry)		/* special case - no spaces */
X						{
X							ptr += strlen(entry);
X							*(ptr + 2) = 0;
X							break;
X						}
X					}
X
X					*(ptr++) = 0;				/* change space to zero */
X					sprintf(msgbuf, "m%s", entry);
X					chc_send(msgbuf);
X					strcpy(entry, ptr);			/* change string to leftover */
X					ptr = entry + strlen(entry);
X					mvwprintw(inp, 1, 1, "%77s", "");	/* clear input box */
X					mvwprintw(inp, 1, 1, "-> %s", entry);	/* print string */
X				}
X				else
X					waddch(inp, (*(ptr++) = c));		/* print character */
X			}
X		}
X	}
}
SHAR_EOF
chmod 0644 chat.c ||
echo 'restore of chat.c failed'
Wc_c="`wc -c < 'chat.c'`"
test 9540 -eq "$Wc_c" ||
	echo 'chat.c: original size 9540, current size' "$Wc_c"
# ============= chatter.c ==============
sed 's/^X//' << 'SHAR_EOF' > 'chatter.c' &&
/*
X
chatter
server process for multi-user chat
Written by D'Arcy J.M. Cain
West Hill, Ontario
darcy@druid.UUCP
All rights reserved
X
This is the server for the chat program.  It handles various
requests and sends messages to the users of chat.
X
Permission is hereby granted to freely copy and redistribute this
software, provided that the author is clearly credited in all
copies and derivations.  This software is provided ``As Is'' and
without any express or implied warranties.
X
*/
X
#include	<stdio.h>
#include	<signal.h>
#include	<string.h>
#include	<stdlib.h>
#include	<sys/types.h>
#include	<ctype.h>
#include	<errno.h>
#include	"chat.h"
X
char chatter_rcsid[] = "$Id: chatter.c,v 1.2 90/10/26 19:55:20 darcy Exp Locker: darcy $\b ";
X
extern char	chs_rcsid[];
X
#define		MAX_CHATTERS		32
CHATTER		u[MAX_CHATTERS];
X
char	progname[64];
X
void	cleanup(int sig)	/* exit through here always */
{
X	ch_close();
X	fprintf(stderr, "\nchatter exiting\n");
X	exit(sig);
}
X
/* open message queue */
void	init(void)
{
X	char	*p;
X
X	if ((p = ch_init()) != NULL)
X	{
X		fprintf(stderr, "%s: %s\n", progname, p);
X		exit(1);
X	}
}
X
void	main(int argc, char **argv)
{
X	int		k, u_id;
X	char	ch, msgbuf[512], message[256];
X
X	strcpy(progname, argv[0]);
X
X	/* all trap everything to go through cleanup */
X	signal(SIGINT, cleanup);
X	signal(SIGABRT, cleanup);
X	signal(SIGKILL, cleanup);
X	signal(SIGTERM, cleanup);
X
X	init();
X
X	/* reset all channels */
X	for (k = 0; k < MAX_CHATTERS; k++)
X		u[k].channel = 0;
X
X	for (;;)		/* till the stars fall from the sky */
X	{
X		while ((u_id = ch_get(msgbuf)) == -1)
X			;
X
X		if (u[u_id].channel == 0)
X			u[u_id].channel = '0';
X
X		ch = u[u_id].channel;
X
X		switch (*msgbuf)
X		{
X			case 'o':	/* open a chat channel */
X				strcpy(u[u_id].logname, msgbuf + 1);
X				sprintf(msgbuf, "[%-12.12s] Joining Chat", u[u_id].logname);
X
X				/* advise the chat world of newcomer */
X				for (k = 0; k < MAX_CHATTERS; k++)
X					if (u[k].channel)
X						ch_send(msgbuf, k);
X
X				break;
X
X
X			case 'x':		/* user exiting from chat */
X				sprintf(msgbuf, "[%-12.12s] Leaving Chat", u[u_id].logname);
X
X				/* check each user */
X				for (k = 0; k < MAX_CHATTERS; k++)
X				{
X					if (k == u_id)				/* this user */
X						u[k].channel = 0;
X					else if (u[k].channel)		/* other user */
X						if (u[k].channel == ch)	/* same channel */
X							ch_send(msgbuf, k);
X				}
X
X				ch_send(NULL, u_id);			/* remove user from list */
X				break;
X
X			case 'c':			/* change channel */
X				if (isascii(msgbuf[1]))			/* only allow ASCII */
X				{
X					if (msgbuf[1] == '*')
X					{
X						int c = '0';
X
X						while (isascii(c))
X						{
X							if (c == '*')
X								continue;
X
X							for (k = 0; k < MAX_CHATTERS; k++)
X								if (u[k].channel == c)
X									k = MAX_CHATTERS + 1;
X
X							if (k == MAX_CHATTERS)
X							{
X								u[u_id].channel = c;
X								sprintf(msgbuf,
X										"[%-12.12s] switched to channel %c",
X										u[u_id].logname, c);
X
X								for (k = 0; k < MAX_CHATTERS; k++)
X									if (u[k].channel)
X										ch_send(msgbuf, k);
X
X								break;
X							}
X
X							c++;
X						}
X
X						break;
X					}
X							
X					u[u_id].channel = msgbuf[1];
X					sprintf(msgbuf, "[%-12.12s] switched to channel %c",
X						u[u_id].logname, u[u_id].channel);
X
X					for (k = 0; k < MAX_CHATTERS; k++)
X						if (u[k].channel)
X							ch_send(msgbuf, k);
X				}
X
X				break;
X
X			case 's':			/* show users */
X				for (k = 0; k < MAX_CHATTERS; k++)
X				{
X					if (u[k].channel)
X					{
X						sprintf(msgbuf, "[%-12.12s] is on channel %c",
X								u[k].logname, u[k].channel);
X						ch_send(msgbuf, u_id);
X					}
X				}
X
X				break;
X
X			case 'n':			/* change user's display name */
X				if (msgbuf[1])
X					strcpy(u[u_id].logname, msgbuf + 1);
X				break;
X				
X			case 'm':			/* send a message */
X				msgbuf[64] = 0;
X				sprintf(message, "[%-12.12s] %s",
X						u[u_id].logname, msgbuf + 1);
X
X				for (k = 0; k < MAX_CHATTERS; k++)
X					if (u[k].channel == ch)
X						ch_send(message, k);
X
X				break;
X
X			case 'v':			/* send a message */
X				ch_send(chatter_rcsid + 1, u_id);
X				ch_send(chs_rcsid + 1, u_id);
X				break;
X		}
X	}
}
SHAR_EOF
chmod 0644 chatter.c ||
echo 'restore of chatter.c failed'
Wc_c="`wc -c < 'chatter.c'`"
test 4071 -eq "$Wc_c" ||
	echo 'chatter.c: original size 4071, current size' "$Wc_c"
# ============= chc_msg.c ==============
sed 's/^X//' << 'SHAR_EOF' > 'chc_msg.c' &&
/*
chc_msg
X
support routines for chat client - message IPC version
Written by D'Arcy J.M. Cain
West Hill, Ontario
darcy@druid.UUCP
All rights reserved
X
This module is linked with chat to implement a message based
version of chat.
X
Permission is hereby granted to freely copy and redistribute this
software, provided that the author is clearly credited in all
copies and derivations.  This software is provided ``As Is'' and
without any express or implied warranties.
X
*/
X
#include	<signal.h>
#include	<sys/ipc.h>
#include	<sys/msg.h>
#include	<string.h>
#include	"chat.h"
X
char chc_rcsid[] = "$Id: chc_msg.c,v 1.2 90/10/26 19:55:28 darcy Exp Locker: darcy $\b ";
X
#define		KEY		0x63
X
extern int	getpid(void);
X
static struct msgbuf	sndbuf, rcvbuf;
static int	q;
static long	my_pid;
X
#ifdef	HALFDELAY_BROKEN
static void	checker(int sig)
{
X	extern int chat_suspended;
X
X	if (!chat_suspended)
X		while (chk_q())
X			;
X
X	sigset(SIGALRM, checker);
X	alarm(2);
}
#endif
X
/* open message queue */
char	*ch_init(void)
{
X	if ((q = msgget(KEY, 0666)) == -1)
X		return("Can't open chat queue");
X
#ifdef	HALFDELAY_BROKEN
X	checker(0);			/* set up timer */
#endif
X	my_pid = getpid();
X	return(NULL);
}
X
int		ch_close(void)	/* exit through here always */
{
X	/* nothing to do in message IPC version */
X	return(0);
}
X
void	chc_send(char *s)			/* send message to chatter */
{
X	sndbuf.mtype = 1L;
X	strcpy(sndbuf.mtext, s);
X	msgsnd(q, &sndbuf, 256, 0);
}
X
/* don't block */
int		ch_get(char *s)
{
X	if (msgrcv(q, &rcvbuf, 256, my_pid, IPC_NOWAIT) > 0)
X	{
X		strcpy(s, rcvbuf.mtext);
X		return(1);
X	}
X
X	return(0);
}
X
SHAR_EOF
chmod 0644 chc_msg.c ||
echo 'restore of chc_msg.c failed'
Wc_c="`wc -c < 'chc_msg.c'`"
test 1586 -eq "$Wc_c" ||
	echo 'chc_msg.c: original size 1586, current size' "$Wc_c"
# ============= chs_msg.c ==============
sed 's/^X//' << 'SHAR_EOF' > 'chs_msg.c' &&
/*
chs_msg
X
support routines for chatter - message IPC version
Written by D'Arcy J.M. Cain
West Hill, Ontario
darcy@druid.UUCP
All rights reserved
X
This module is linked with chatter to implement a message IPC based
version of chatter.
X
Permission is hereby granted to freely copy and redistribute this
software, provided that the author is clearly credited in all
copies and derivations.  This software is provided ``As Is'' and
without any express or implied warranties.
X
*/
X
#include	<signal.h>
#include	<sys/ipc.h>
#include	<sys/msg.h>
#include	<string.h>
#include	"chat.h"
X
char chs_rcsid[] = "$Id: chs_msg.c,v 1.2 90/10/26 19:55:31 darcy Exp Locker: darcy $\b ";
X
#define		KEY		0x63
X
static struct msgbuf	sndbuf, rcvbuf;
static struct msqid_ds	msqid;
static int	q;
X
static long	u_pid[MAX_CHATTERS];
X
static int	get_u(long i)
{
X	int		k;
X
X	for (k = 0; k < MAX_CHATTERS; k++)
X		if (u_pid[k] == i)
X			return(k);
X
X	return(-1);
}
X
/* open message queue */
char	*ch_init(void)
{
X	if ((q = msgget(KEY, 0666 | IPC_CREAT)) == -1)
X		return("Chat queue in use");
X
X	return(NULL);
}
X
int		ch_close(void)	/* exit through here always */
{
X	/* remove chat message queue */
X	return(msgctl(q, IPC_RMID, NULL));
}
X
void	ch_send(char *s, int id)			/* send message to user */
{
X	if (s == NULL)
X		u_pid[id] = 0;
X	else if (u_pid[id])
X	{
X		sndbuf.mtype = u_pid[id];
X		strcpy(sndbuf.mtext, s);
X		msgsnd(q, &sndbuf, 256, 0);
X	}
}
X
/* block until a message is received */
int		ch_get(char *s)
{
X	int		u;
X
X	/* check for dead process */
X	for (u = 0; u < MAX_CHATTERS; u++)
X	{
X		if (u_pid[u])
X		{
X			if ((kill(u_pid[u], 0)) /* && (errno == ESRCH) */ )
X			/* We shouldn't have to test */
X			/* for ESRCH since that is   */
X			/* only theoretical return   */
X			{
X				while (msgrcv(q, &rcvbuf, 256, u_pid[u], IPC_NOWAIT) != -1)
X					;
X
X				/* simulate a close command */	
X				strcpy(s, "x");
X				u_pid[u] = 0;
X				return(u);
X			}
X		}
X	}
X
X	if (msgrcv(q, &rcvbuf, 256, 1L, 0) == -1)
X		return(-1);
X
X	/* set up various variables from message */
X	strcpy(s, rcvbuf.mtext);
X	msgctl(q, IPC_STAT, &msqid);
X
X	if ((u = get_u(msqid.msg_lspid)) == -1)
X	{
X		if ((u = get_u(0L)) == -1)
X		{
X			strcpy(sndbuf.mtext, "e2 Chatter table full");
X			sndbuf.mtype = msqid.msg_lspid;
X			msgsnd(q, &sndbuf, 256, 0);
X			return(-1);
X		}
X		else
X			u_pid[u] = msqid.msg_lspid;
X	}
X
X	return(u);
}
X
SHAR_EOF
chmod 0644 chs_msg.c ||
echo 'restore of chs_msg.c failed'
Wc_c="`wc -c < 'chs_msg.c'`"
test 2340 -eq "$Wc_c" ||
	echo 'chs_msg.c: original size 2340, current size' "$Wc_c"
# ============= chat.h ==============
sed 's/^X//' << 'SHAR_EOF' > 'chat.h' &&
/*
X
chat.h
Written by D'Arcy J.M. Cain
West Hill, Ontario
darcy@cain
All rights reserved
X
This is the header file for chat programs
X
If NO_SH_ESC is defined, shell escapes are disabled.
X
If NO_CHG_NAME is defined, users cannot change their display name from
the default which is the login name.
X
If NO_SHOW_USERS is defined, The /s command to show other users on the
system is disabled.
X
Some curses libraries in SV3.2 (and perhaps others) have a bug in that
halfdelay does not work as advertised.  To get around this define the
constant HALFDELAY_BROKEN.  This will cause code to be substituted which
uses a 2 second alarm to check the queue for incoming messages.  If you
don't know if this is necessary on your system then compile without it
and see if you can receive messages from others without hitting a key.
If not then recompile with the constant defined.
X
*/
X
#define		MAX_CHATTERS	32
X
typedef struct {
X	short	channel;		/* for multiple chats on one system */
X	char	logname[32];	/* for display purposes */
} CHATTER;
X
void	ch_send(char *s, int id);
void	chc_send(char *s);
int		ch_close(void);
char	*ch_init(void);
int		ch_get(char *s);
int		chk_q(void);
SHAR_EOF
chmod 0444 chat.h ||
echo 'restore of chat.h failed'
Wc_c="`wc -c < 'chat.h'`"
test 1164 -eq "$Wc_c" ||
	echo 'chat.h: original size 1164, current size' "$Wc_c"
# ============= lchat ==============
sed 's/^X//' << 'SHAR_EOF' > 'lchat' &&
:
# $Id: lchat,v 1.2 90/10/26 22:53:50 darcy Exp $
# This script calls chat with some arguments.  Set LOGFILE equal to 
# a log file to capture your session to.  Set REALNAME to the name
# to display on the output.
X
# If a user's name is entered, chat is called via chatyell which 
# sends a message to the named user to run chat before running chat.
X
LOGFILE=/usr/darcy/chat.log
REALNAME="D'Arcy"
X
echo "\n`date`" >> $LOGFILE
X
if [ $# = 0 ]
then
X	chat -n $REALNAME -f $LOGFILE
else
X	chatyell $1 -n $REALNAME -f $LOGFILE $2 $3 $4 $5
fi
X
SHAR_EOF
chmod 0544 lchat ||
echo 'restore of lchat failed'
Wc_c="`wc -c < 'lchat'`"
test 537 -eq "$Wc_c" ||
	echo 'lchat: original size 537, current size' "$Wc_c"
# ============= chatyell ==============
sed 's/^X//' << 'SHAR_EOF' > 'chatyell' &&
:
# chatyell tells someone to join the chat.
# Written by D'Arcy J.M. Cain
# $Id: chatyell,v 1.2 90/10/26 22:53:46 darcy Exp $
X
if [ $# -lt 1 ]
then
X	echo "Usage: chatyell user"
else
# The following test isn't perfect.  If you want to chat
# with user bill and he is not logged in but billy is then
# the test shows bill as logged in.  I'm sure there is a
# really easy way to find out if someone is logged in but
# I just don't happen to know it.
X	if [ `who | awk "/$1/ {print \$1}" | wc -l` != 0 ]
X	then
X		write $1 <<- END_ECHO
X		$LOGNAME wants to chat with you.
X		Type chat at the prompt
X		END_ECHO
X		chat $2 $3 $4 $5 $6 $7 $8 $9
X	else
X		echo "$1 not currently logged on"
X	fi
fi
SHAR_EOF
chmod 0555 chatyell ||
echo 'restore of chatyell failed'
Wc_c="`wc -c < 'chatyell'`"
test 682 -eq "$Wc_c" ||
	echo 'chatyell: original size 682, current size' "$Wc_c"
# ============= chat.readme ==============
sed 's/^X//' << 'SHAR_EOF' > 'chat.readme' &&
chat
====
X
Written by D'Arcy J.M. Cain 1990
(darcy@cain)
All rights reserved
X
Permission is hereby granted to freely copy and redistribute this
software, provided that the author is clearly credited in all
copies and derivations.  This software is provided ``As Is'' and
without any express or implied warranties.
X
Chat allows more than one user on a system to be involved
in a conference via their terminal.  It consists of a foreground
part and a background part.
X
chatter is the background process.  It looks for messages from 
other processes and sends messages to them.  It should be put into
inittab so that it always restarts.
X
chat is the front end and is the program run by users wishing to
join or start a chat.  In chat, lines starting with / are commands.
enter /? or /h for a list of commands
X
To call chat simply enter chat from the command line.  Various 
options are available and are accessed by adding '-x' to the command
where x is one of the following letters:
X
h   Displays a usage message instead of running the program.
n   Takes the following name and uses it instead of login name.
f   Takes the following file and keeps a log of the conversation in it.
X
Example:
X
chat -n "D'Arcy" -f chat.log
X
This starts chat, sets up the name as D'Arcy, opens the file chat.log
and keeps a record of the chat in it.  Note that the quotes around
D'Arcy are only neccessary because of the apostrophe.
X
The n option can be set from within the program as well.
X
Move chat.make to Makefile and run make to create the programs.
X
X
Chat Wish List:
X
These are a few things that I hope to add to chat later.  If you can
think of other enhancements please let me know.
X
- A TLI version.  I have made a small start on this but if anyone wants
X  to give it a try, go ahead.  Use the ???_msg.c files as models.  You
X  should be able to write ???_tli.c files that just link into the other
X  files without changing them.
X
- A monitor program.  This is mostly a matter of deciding what it should
X  do.  One idea would be to print the name of the user any time there was
X  any activity from the chatter.  This could be monitored in any way one
X  chooses.  A simplistic method would be to run the monitor program in
X  background and allow it to interupt the user each time there was any
X  activity.  Of course there are other possibilities.  On my system, for
X  example, I would put it in background and pipe it to a program that
X  displays the name on the LED's on my front panel.
X
X
Enjoy.
D'Arcy J.M. Cain
(darcy@druid)
X
SHAR_EOF
chmod 0444 chat.readme ||
echo 'restore of chat.readme failed'
Wc_c="`wc -c < 'chat.readme'`"
test 2516 -eq "$Wc_c" ||
	echo 'chat.readme: original size 2516, current size' "$Wc_c"
# ============= chat.make ==============
sed 's/^X//' << 'SHAR_EOF' > 'chat.make' &&
# Makefile for chat
# Written by D'Arcy J.M. Cain
# $Id: chat.make,v 1.2 90/10/26 22:53:40 darcy Exp $
#
X
# So far only message based method supported.
METHOD=msg
X
all:	chat chatter
X
chatter:	chatter.o chs_$(METHOD).o
X	$(CC) $(CFLAGS) chatter.o chs_$(METHOD).o $(LDFLAGS) -o chatter
X
chat:	chat.o chc_$(METHOD).o
X	$(CC) $(CFLAGS) chat.o chc_$(METHOD).o $(LDFLAGS) -lcurses -o chat
X
chat.o:				chat.c chat.h
X
chatter.o:			chatter.c chat.h
X
chc_$(METHOD).o:	chc_$(METHOD).c chat.h
X
chs_$(METHOD).o:	chs_$(METHOD).c chat.h
SHAR_EOF
chmod 0444 chat.make ||
echo 'restore of chat.make failed'
Wc_c="`wc -c < 'chat.make'`"
test 520 -eq "$Wc_c" ||
	echo 'chat.make: original size 520, current size' "$Wc_c"
exit 0

-- 
D'Arcy J.M. Cain (darcy@druid)     |
D'Arcy Cain Consulting             |   I support gun control.
West Hill, Ontario, Canada         |   Let's start with the government!
+ 416 281 6094                     |