[comp.sources.games] v11i021: tinymud2 - user-extendible multi-user adventure

billr@saab.CNA.TEK.COM (Bill Randle) (08/10/90)

Submitted-by: Stu Heiss <stu@jpusa1.chi.il.us>
Posting-number: Volume 11, Issue 21
Archive-name: tinymud2/xenix_diffs
Patch-To: tinymud2: Volume 11, Issue 5-14

	[Not runnning Xenix, I haven't tried these patches.... -br]

[[Attached below is what's needed to bring up tinymud on xenix using
fifos instead of sockets for server/client communication.  The bulk
of this is fifoiface.c which replaces oldinterface.c.  There are
some minor diffs to game.c, Makefile, and a few missing routines.
A startup for the server is included as well as a client which is
a small shell script.  You are, of course, limmited to one machine,
but you can have a number of people playing.  Have fun.]]

#! /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:
#	xenix.diffs
#	rename.c
#	index.c
#	random.c
#	fifoiface.c
#	tinymud.sh
#	restart
# This archive created: Wed Aug  8 14:51:35 1990
# By:	stu (JPUSA - Chicago, IL)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'xenix.diffs'" '(7297 characters)'
if test -f 'xenix.diffs'
then
	echo shar: "will not over-write existing file 'xenix.diffs'"
else
sed 's/^X//' << \SHAR_EOFxenix.diffs > 'xenix.diffs'
XRCS file: RCS/Makefile,v
Xretrieving revision 1.1
Xdiff -c -r1.1 Makefile
X*** /tmp/,RCSt1a08638	Wed Aug  8 14:48:47 1990
X--- Makefile	Wed Aug  8 14:46:29 1990
X***************
X*** 8,15
X  #
X  
X  # GCC:
X! #CC=gcc
X! #OPTIM= -g -W -Wreturn-type -Wunused -Wcomment -Wwrite-strings
X  
X  # Systems with 'cc' built from GCC (IBM RT, NeXT):
X  CC=cc
X
X--- 8,16 -----
X  #
X  
X  # GCC:
X! CC=gcc
X! OPTIM= -g -W -Wreturn-type -Wunused -Wcomment -Wwrite-strings
X! OPTIM= -W -Wreturn-type -Wunused -Wcomment -Wwrite-strings
X  
X  # Systems with 'cc' built from GCC (IBM RT, NeXT):
X  #CC=cc
X***************
X*** 12,19
X  #OPTIM= -g -W -Wreturn-type -Wunused -Wcomment -Wwrite-strings
X  
X  # Systems with 'cc' built from GCC (IBM RT, NeXT):
X! CC=cc
X! OPTIM=-g
X  
X  # Dec 3100 C compiler
X  #CC=cc
X
X--- 13,20 -----
X  OPTIM= -W -Wreturn-type -Wunused -Wcomment -Wwrite-strings
X  
X  # Systems with 'cc' built from GCC (IBM RT, NeXT):
X! #CC=cc
X! #OPTIM=-g
X  
X  # Dec 3100 C compiler
X  #CC=cc
X***************
X*** 71,77
X  #      -DGOD_ONLY_PCREATE -DROBOT_MODE -DRECYCLE -DNOFAKES \
X  #      -DTINYHELL
X  
X! DEFS= -DGOD_PRIV -DCOMPRESS -DQUIET_WHISPER -DGENDER -DHOST_NAME \
X        -DCONNECT_MESSAGES -DPLAYER_LIST -DDETACH -DROBOT_MODE \
X        -DRECYCLE -DTINKER -DNOFAKES -DISLANDIA
X  
X
X--- 72,78 -----
X  #      -DGOD_ONLY_PCREATE -DROBOT_MODE -DRECYCLE -DNOFAKES \
X  #      -DTINYHELL
X  
X! DEFS= -DGOD_PRIV -DCOMPRESS -DQUIET_WHISPER -DGENDER -UHOST_NAME \
X        -DCONNECT_MESSAGES -DPLAYER_LIST -DDETACH -DROBOT_MODE \
X        -DRECYCLE -DTINKER -DNOFAKES -UISLANDIA -DDEBUG
X  
X***************
X*** 73,79
X  
X  DEFS= -DGOD_PRIV -DCOMPRESS -DQUIET_WHISPER -DGENDER -DHOST_NAME \
X        -DCONNECT_MESSAGES -DPLAYER_LIST -DDETACH -DROBOT_MODE \
X!       -DRECYCLE -DTINKER -DNOFAKES -DISLANDIA
X  
X  CFLAGS= $(OPTIM) $(DEFS)
X  
X
X--- 74,80 -----
X  
X  DEFS= -DGOD_PRIV -DCOMPRESS -DQUIET_WHISPER -DGENDER -UHOST_NAME \
X        -DCONNECT_MESSAGES -DPLAYER_LIST -DDETACH -DROBOT_MODE \
X!       -DRECYCLE -DTINKER -DNOFAKES -UISLANDIA -DDEBUG
X  
X  CFLAGS= $(OPTIM) $(DEFS)
X  
X***************
X*** 78,85
X  CFLAGS= $(OPTIM) $(DEFS)
X  
X  # Everything needed to use db.c
X! DBFILES= db.c compress.c player_list.c stringutil.c
X! DBOFILES= db.o compress.o player_list.o stringutil.o
X  
X  # Everything except interface.c --- allows for multiple interfaces
X  CFILES= create.c game.c help.c look.c match.c move.c player.c predicates.c \
X
X--- 79,86 -----
X  CFLAGS= $(OPTIM) $(DEFS)
X  
X  # Everything needed to use db.c
X! DBFILES= db.c compress.c player_list.c stringutil.c random.c
X! DBOFILES= db.o compress.o player_list.o stringutil.o random.o
X  
X  # Everything except interface.c --- allows for multiple interfaces
X  CFILES= create.c game.c help.c look.c match.c move.c player.c predicates.c \
X***************
X*** 84,90
X  # Everything except interface.c --- allows for multiple interfaces
X  CFILES= create.c game.c help.c look.c match.c move.c player.c predicates.c \
X  	rob.c set.c speech.c utils.c wiz.c game.c \
X! 	boolexp.c unparse.c conc.c oldinterface.c $(DBFILES)
X  
X  # .o versions of above
X  OFILES= create.o game.o help.o look.o match.o move.o player.o predicates.o \
X
X--- 85,91 -----
X  # Everything except interface.c --- allows for multiple interfaces
X  CFILES= create.c game.c help.c look.c match.c move.c player.c predicates.c \
X  	rob.c set.c speech.c utils.c wiz.c game.c \
X! 	boolexp.c unparse.c conc.c oldinterface.c $(DBFILES) index.c rename.c
X  
X  # .o versions of above
X  OFILES= create.o game.o help.o look.o match.o move.o player.o predicates.o \
X***************
X*** 89,95
X  # .o versions of above
X  OFILES= create.o game.o help.o look.o match.o move.o player.o predicates.o \
X  	rob.o set.o speech.o utils.o wiz.o boolexp.o \
X! 	unparse.o $(DBOFILES)
X  
X  # Files in the standard distribution
X  DISTFILES= $(CFILES) config.h db.h externs.h interface.h match.h \
X
X--- 90,96 -----
X  # .o versions of above
X  OFILES= create.o game.o help.o look.o match.o move.o player.o predicates.o \
X  	rob.o set.o speech.o utils.o wiz.o boolexp.o \
X! 	unparse.o $(DBOFILES) index.o rename.o
X  
X  # Files in the standard distribution
X  DISTFILES= $(CFILES) config.h db.h externs.h interface.h match.h \
X***************
X*** 108,114
X        $(BINDIR)/dump $(BINDIR)/decompress $(BINDIR)/concentrate
X  
X  # paths is likely to remain broken
X! all: extract sanity-check dump decompress netmud netmud.conc concentrate
X  
X  TAGS: *.c *.h
X  	etags *.c *.h
X
X--- 109,115 -----
X        $(BINDIR)/dump $(BINDIR)/decompress $(BINDIR)/concentrate
X  
X  # paths is likely to remain broken
X! all: extract sanity-check dump decompress fifonetmud # netmud netmud.conc concentrate
X  
X  TAGS: *.c *.h
X  	etags *.c *.h
X***************
X*** 117,122
X  	-mv -f netmud.conc netmud.conc~
X  	$(CC) $(CFLAGS) -o netmud.conc interface.o $(OFILES)
X  
X  netmud: $P oldinterface.o $(OFILES)
X  	-mv -f netmud netmud~
X  	$(CC) $(CFLAGS) -o netmud oldinterface.o $(OFILES)
X
X--- 118,126 -----
X  	-mv -f netmud.conc netmud.conc~
X  	$(CC) $(CFLAGS) -o netmud.conc interface.o $(OFILES)
X  
X+ fifonetmud: $P fifoiface.o $(OFILES)
X+ 	$(CC) $(CFLAGS) -o $@ fifoiface.o $(OFILES)
X+ match.h: copyright.h db.h
X  netmud: $P oldinterface.o $(OFILES)
X  	-mv -f netmud netmud~
X  	$(CC) $(CFLAGS) -o netmud oldinterface.o $(OFILES)
X===================================================================
XRCS file: RCS/game.c,v
Xretrieving revision 1.1
Xdiff -c -r1.1 game.c
X*** /tmp/,RCSt1a08638	Wed Aug  8 14:48:48 1990
X--- game.c	Tue Jul 31 13:07:17 1990
X***************
X*** 3,9
X  #include <stdio.h>
X  #include <ctype.h>
X  #include <signal.h>
X- #include <sys/wait.h>
X  #include <time.h>
X  #include <stdarg.h>
X      
X
X--- 3,8 -----
X  #include <stdio.h>
X  #include <ctype.h>
X  #include <signal.h>
X  #include <time.h>
X  #include <stdarg.h>
X      
X***************
X*** 46,51
X  /* should be void, but it's defined as int */
X  static int alarm_handler(void)
X  {
X      alarm_triggered = 1;
X      if(!alarm_block) {
X          fork_and_dump();
X
X--- 45,51 -----
X  /* should be void, but it's defined as int */
X  static int alarm_handler(void)
X  {
X+ signal(SIGALRM, SIG_IGN);
X      alarm_triggered = 1;
X      if(!alarm_block) {
X          fork_and_dump();
X***************
X*** 50,55
X      if(!alarm_block) {
X          fork_and_dump();
X      }
X      return 0;
X  }
X  
X
X--- 50,56 -----
X      if(!alarm_block) {
X          fork_and_dump();
X      }
X+ signal(SIGALRM, (void (*)) alarm_handler);
X      return 0;
X  }
X  
X***************
X*** 164,177
X      alarm(DUMP_INTERVAL);
X  }
X  
X- static int reaper(void)
X- {
X-     union wait stat;
X- 
X-     while(wait3(&stat, WNOHANG, 0) > 0);
X-     return 0;
X- }
X- 
X  int init_game(const char *infile, const char *outfile)
X  {
X     FILE *f;
X
X--- 165,170 -----
X      alarm(DUMP_INTERVAL);
X  }
X  
X  int init_game(const char *infile, const char *outfile)
X  {
X     FILE *f;
X***************
X*** 194,200
X     dumpfile = alloc_string(outfile);
X     signal(SIGALRM, (void (*)) alarm_handler);
X     signal(SIGHUP, (void (*)) alarm_handler);
X!    signal(SIGCHLD, (void (*)) reaper);
X     alarm_triggered = 0;
X     alarm(DUMP_INTERVAL);
X     
X
X--- 187,193 -----
X     dumpfile = alloc_string(outfile);
X     signal(SIGALRM, (void (*)) alarm_handler);
X     signal(SIGHUP, (void (*)) alarm_handler);
X!    signal(SIGCLD, SIG_IGN);
X     alarm_triggered = 0;
X     alarm(DUMP_INTERVAL);
X     
SHAR_EOFxenix.diffs
if test 5002 -ne "`sum < 'xenix.diffs' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'xenix.diffs'" '(sum should have been 5002)'
	echo shar: "trying 'sum -r'"
	if test 34058 -ne "`sum -r < 'xenix.diffs' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'xenix.diffs'" '(sum should have been 34058)'
	echo shar: "trying 'wc -c'"
		if test 7297 -ne "`wc -c < 'xenix.diffs'`"
		then
			echo shar: "error transmitting 'xenix.diffs'" '(should have been 7297 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 644 'xenix.diffs'
fi
echo shar: "extracting 'rename.c'" '(490 characters)'
if test -f 'rename.c'
then
	echo shar: "will not over-write existing file 'rename.c'"
else
sed 's/^X//' << \SHAR_EOFrename.c > 'rename.c'
X#include <sys/errno.h>
Xint
Xrename(old, new)
Xchar *old, *new;
X{
X	extern int errno;
X	int fd1,fd2;
X	char buf[1024];
X	int n;
X	(void)unlink(new);
X	if (link(old, new) == -1 && errno != EXDEV)
X		return(-1);
X	if((fd1=open(old,0)) == -1)
X		return(-1);
X	if((fd2=creat(new,0666)) == -1) {
X		close(fd1);
X		return(-1);
X	}
X	while((n=read(fd1,buf,sizeof buf)) > 0) {
X		if (write(fd2,buf,n) != n) {
X			close(fd1);
X			close(fd2);
X			unlink(new);
X			return(-1);
X		}
X	}
X	close(fd1);
X	close(fd2);
X	return 0;
X}
SHAR_EOFrename.c
if test 35337 -ne "`sum < 'rename.c' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'rename.c'" '(sum should have been 35337)'
	echo shar: "trying 'sum -r'"
	if test 33981 -ne "`sum -r < 'rename.c' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'rename.c'" '(sum should have been 33981)'
	echo shar: "trying 'wc -c'"
		if test 490 -ne "`wc -c < 'rename.c'`"
		then
			echo shar: "error transmitting 'rename.c'" '(should have been 490 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 644 'rename.c'
fi
echo shar: "extracting 'index.c'" '(80 characters)'
if test -f 'index.c'
then
	echo shar: "will not over-write existing file 'index.c'"
else
sed 's/^X//' << \SHAR_EOFindex.c > 'index.c'
Xextern char *strchr();
Xchar *index(s,c) char *s; int c; { return strchr(s,c); }
SHAR_EOFindex.c
if test 6722 -ne "`sum < 'index.c' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'index.c'" '(sum should have been 6722)'
	echo shar: "trying 'sum -r'"
	if test 37873 -ne "`sum -r < 'index.c' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'index.c'" '(sum should have been 37873)'
	echo shar: "trying 'wc -c'"
		if test 80 -ne "`wc -c < 'index.c'`"
		then
			echo shar: "error transmitting 'index.c'" '(should have been 80 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 644 'index.c'
fi
echo shar: "extracting 'random.c'" '(74 characters)'
if test -f 'random.c'
then
	echo shar: "will not over-write existing file 'random.c'"
else
sed 's/^X//' << \SHAR_EOFrandom.c > 'random.c'
Xint random() { return rand(); }
Xvoid srandom(n) unsigned n; { srand(n); }
SHAR_EOFrandom.c
if test 6351 -ne "`sum < 'random.c' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'random.c'" '(sum should have been 6351)'
	echo shar: "trying 'sum -r'"
	if test 19983 -ne "`sum -r < 'random.c' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'random.c'" '(sum should have been 19983)'
	echo shar: "trying 'wc -c'"
		if test 74 -ne "`wc -c < 'random.c'`"
		then
			echo shar: "error transmitting 'random.c'" '(should have been 74 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 644 'random.c'
fi
echo shar: "extracting 'fifoiface.c'" '(27328 characters)'
if test -f 'fifoiface.c'
then
	echo shar: "will not over-write existing file 'fifoiface.c'"
else
sed 's/^X//' << \SHAR_EOFfifoiface.c > 'fifoiface.c'
X/*
X * interface for xenix - uses fifos instead of sockets
X *
X * The server creates a fifo called 'fiforXXX' where XXX is replaced by the default port number.
X * It then listens for a request which should be an integer, typically the process id of the client,
X * and then creates two fifos named 'fiforYYY' and 'fifowYYY', where YYY is the number the client wrote
X * the default fifo, for i/o to the client.  The client writes to 'fiforYYY' and reads 'fifowYYY'.
X * Client code is supplied as the shell script 'tinymud.sh'.
X */
X#include "copyright.h"
X
X#undef NULL
X#include <stdio.h>
X#undef NULL
X#include <time.h>
X#include <sys/types.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <sys/select.h>
X#include <signal.h>
X#include <sys/ioctl.h>
X#include <fcntl.h>
X#include <sys/errno.h>
X#include <ctype.h>
X
X/* <netinet/in.h> */
X#define u_char unsigned char
X#define u_short unsigned short
X#define u_long unsigned long
Xstruct in_addr {
X	union {
X		struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
X		struct { u_short s_w1,s_w2; } S_un_w;
X		u_long S_addr;
X	} S_un;
X#define	s_addr	S_un.S_addr
X};
Xstruct sockaddr_in {
X	short	sin_family;
X	u_short	sin_port;
X	struct	in_addr sin_addr;
X	char	sin_zero[8];
X};
X#define	EWOULDBLOCK	35		/* Operation would block */
X
X#undef NULL
X#define NULL 0
X#define bzero(s,n) memset(s,'\0',n)
X#define getdtablesize() FD_SETSIZE
X
X#include "db.h"
X#include "interface.h"
X#include "config.h"
X
Xextern int	errno;
Xint	shutdown_flag = 0;
X
Xstatic const char *connect_fail = "Either that player does not exist, or has a different password.\n";
X#ifndef REGISTRATION
Xstatic const char *create_fail = "Either there is already a player with that name, or that name is illegal.\n";
X#endif REGISTRATION
Xstatic const char *flushed_message = "<Output Flushed>\n";
Xstatic const char *shutdown_message = "Going down - Bye\n";
X
Xstruct text_block {
X	int			nchars;
X	struct text_block	*nxt;
X	char			*start;
X	char			*buf;
X};
X
Xstruct text_queue {
X    struct text_block *head;
X    struct text_block **tail;
X};
X
Xstruct descriptor_data {
X        int rdescriptor;
X        int wdescriptor;
X	int connected;
X	dbref player;
X	char *output_prefix;
X	char *output_suffix;
X	int output_size;
X	struct text_queue output;
X	struct text_queue input;
X	char *raw_input;
X	char *raw_input_at;
X	long last_time;
X	long connected_at;
X	int quota;
X	struct sockaddr_in address;
X	const char *hostname;		/* 5/18/90 - Fuzzy */
X	struct descriptor_data *next;
X	struct descriptor_data *prev;
X} *descriptor_list = 0;
X
Xstatic int sock;
Xstatic int ndescriptors = 0;
X
Xvoid process_commands(void);
Xvoid shovechars(int port);
Xvoid shutdownsock(struct descriptor_data *d);
Xstruct descriptor_data *initializesock(int r, int w, struct sockaddr_in *a,
X				       const char *hostname);
Xvoid make_nonblocking(int s);
Xvoid freeqs(struct descriptor_data *d);
Xvoid welcome_user(struct descriptor_data *d);
Xvoid do_motd(dbref);
Xvoid check_connect(struct descriptor_data *d, const char *msg);
Xvoid close_sockets();
Xconst char *addrout (long);
Xvoid dump_users(struct descriptor_data *d, char *user);
Xvoid set_signals(void);
Xstruct descriptor_data *new_connection(int sock);
Xvoid parse_connect (const char *msg, char *command, char *user, char *pass);
Xvoid set_userstring (char **userstring, const char *command);
Xint do_command (struct descriptor_data *d, char *command);
Xchar *strsave (const char *s);
Xint make_socket(int);
Xint queue_string(struct descriptor_data *, const char *);
Xint queue_write(struct descriptor_data *, const char *, int);
Xint process_output(struct descriptor_data *d);
Xint process_input(struct descriptor_data *d);
X#ifdef CONNECT_MESSAGES
Xvoid announce_connect(dbref);
Xvoid announce_disconnect(dbref);
X#endif CONNECT_MESSAGES
Xchar *time_format_1(long);
Xchar *time_format_2(long);
X
X/* Signal handlers */
Xint bailout (int);
Xint sigshutdown (int);
X#ifdef DETACH
Xint logsynch (int);
X#endif DETACH
X
Xchar *logfile = (char *)LOG_FILE;
X
X#define MALLOC(result, type, number) do {			\
X	if (!((result) = (type *) malloc ((number) * sizeof (type))))	\
X		panic("Out of memory");				\
X	} while (0)
X
X#define FREE(x) (free((void *) x))
X
Xchar *fifonames[FD_SETSIZE] = {0};
X
Xint shutdown (s) int s;
X{
X	/* writelog("shutdown %d %s\n",s,fifonames[s]?fifonames[s]:"(nil)"); */
X	if (fifonames[s]) {
X		unlink(fifonames[s]);
X		free(fifonames[s]);
X		fifonames[s] = 0;
X	}
X	return 0;
X}
X#define htons(n) (n)
X#define bcopy(s,t,n) memcpy(t,s,n)
X
Xvoid gettimeofday(tv, tz)
Xstruct timeval *tv;
Xstruct timezone *tz;
X{
X	long time();
X	tv->tv_sec = time((int *)0);
X}
Xint select (maxd, rfds, wfds, xfds, timeout)
Xint maxd;
Xfd_set *rfds, *wfds, *xfds;
Xstruct timeval *timeout;
X{
X	int maxfd,fd;
X	struct stat sbuf;
X	int sec = 0;
X	fd_set r, w, x;
X
X	if (timeout) sec=timeout->tv_sec+(timeout->tv_usec+500)/1000;
X	r= *rfds; w= *wfds; x= *xfds;
X
X	while(1) {
X		*rfds=r; *wfds=w; *xfds=x;
X		for(maxfd=fd=0;fd<maxd;++fd) {
X			if(FD_ISSET(fd,rfds)) {
X				if (stat(fifonames[fd],&sbuf) != 0)
X					goto foo;
X				else if(sbuf.st_size>0)
X					++maxfd;
X				else
X					FD_CLR(fd,rfds);
X			}
X			if(FD_ISSET(fd,wfds)) {
X				if (stat(fifonames[fd],&sbuf) != 0)
X					goto foo;
X				else if(sbuf.st_size<5120)
X					++maxfd;
X				else
X					FD_CLR(fd,wfds);
X			}
X		}
X		if(maxfd > 0 || --sec < 0) break;
X		sleep(1);
X	}
X	return(maxfd);
Xfoo:
X	writelog("SELECT: %s has been unlinked!\n", fifonames[fd]);
X	shutdown_flag = 1;
X	return 0;
X}
Xchar *inet_ntoa(l) long l;
X{
X	static char buf[80];
X	sprintf(buf,"%d.%d.%d.%d",
X		(l/(256*256))%255,(l/(256*256))%255,(l/256)%255,l%255);
X	return buf;
X}
Xint fifo(port,mode)
Xint port,mode;
X{
X	char name[80];
X	int fd = -1;
X	int cmask;
X	extern char *strdup();
X
X	sprintf(name,"fifo%c%d",mode==0?'r':'w',port);
X
X	unlink(name);
X	cmask=umask(0);
X	fd = mknod(name,0666|S_IFIFO);
X	umask(cmask);
X	if (fd != 0) {
X		perror("mknod");
X		writelog("fifo: mknod %s fail\n",name);
X		fd = -1;
X	}
X	else if((fd = open(name,O_RDWR|O_NDELAY)) < 0) {
X		perror("open fifo");
X		writelog("fifo: open %s fail\n",name);
X	}
X	if (fd != -1) {
X		if (fifonames[fd]) free(fifonames[fd]);
X		fifonames[fd] = strdup(name);
X	}
X	return fd;
X}
X
X#ifndef BOOLEXP_DEBUGGING
Xvoid main(int argc, char **argv)
X{
X    if (argc < 3) {
X	fprintf(stderr, "Usage: %s infile dumpfile [port [logfile]]\n", *argv);
X	exit (1);
X    }
X
X    if (argc > 4) logfile = argv[4];
X
X    set_signals ();
X    if (init_game (argv[1], argv[2]) < 0) {
X	writelog("INIT: Couldn't load %s!\n", argv[1]);
X	exit (2);
X    }
X
X    /* go do it */
X    shovechars (argc >= 4 ? atoi (argv[3]) : TINYPORT);
X    close_sockets ();
X    dump_database ();
X    exit (0);
X}
X#endif /*BOOLEXP_DEBUGGING*/
X
Xvoid set_signals(void)
X{
X#ifdef DETACH
X    int i;
X
X    signal(SIGHUP, SIG_IGN);
X    if (fork() != 0) exit(0);
X
X    for (i=getdtablesize(); i >= 0; i--)
X	(void) close(i);
X
X    setpgrp();
X    i = open("/dev/tty", O_RDWR, 0);
X    if (i != -1) {
X	close(i);
X    }
X
X    freopen(logfile, "a", stderr);
X    setbuf(stderr, NULL);
X#endif DETACH    
X    
X    /* we don't care about SIGPIPE, we notice it in select() and write() */
X    signal (SIGPIPE, SIG_IGN);
X
X    /* standard termination signals */
X    signal (SIGINT, (void (*)) sigshutdown);
X    signal (SIGTERM, (void (*)) sigshutdown);
X
X#ifdef DETACH
X    /* SIGUSR2 synchronizes the log file */
X    signal (SIGUSR2, (void (*)) logsynch);
X#else DETACH    
X    signal (SIGUSR2, (void (*)) bailout);
X#endif DETACH    
X
X    /* catch these because we might as well */
X    signal (SIGQUIT, (void (*)) bailout);
X    signal (SIGILL, (void (*)) bailout);
X    signal (SIGTRAP, (void (*)) bailout);
X    signal (SIGIOT, (void (*)) bailout);
X    signal (SIGEMT, (void (*)) bailout);
X    signal (SIGFPE, (void (*)) bailout);
X    signal (SIGBUS, (void (*)) bailout);
X    signal (SIGSEGV, (void (*)) bailout);
X    signal (SIGSYS, (void (*)) bailout);
X    signal (SIGUSR1, (void (*)) bailout);
X}
X
Xint notify(dbref player, const char *msg)
X{
X    struct descriptor_data *d;
X    int retval = 0;
X#ifdef COMPRESS
X    extern const char *uncompress(const char *);
X
X    msg = uncompress(msg);
X#endif /* COMPRESS */
X
X    for(d = descriptor_list; d; d = d->next) {
X	if (d->connected && d->player == player) {
X	    queue_string(d, msg);
X	    queue_write(d, "\n", 1);
X	    retval = 1;
X	}
X    }
X    return(retval);
X}
X
Xstruct timeval timeval_sub(struct timeval now, struct timeval then)
X{
X    now.tv_sec -= then.tv_sec;
X    now.tv_usec -= then.tv_usec;
X    if (now.tv_usec < 0) {
X	now.tv_usec += 1000000;
X	now.tv_sec--;
X    }
X    return now;
X}
X
Xint msec_diff(struct timeval now, struct timeval then)
X{
X    return ((now.tv_sec - then.tv_sec) * 1000
X	    + (now.tv_usec - then.tv_usec) / 1000);
X}
X
Xstruct timeval msec_add(struct timeval t, int x)
X{
X    t.tv_sec += x / 1000;
X    t.tv_usec += (x % 1000) * 1000;
X    if (t.tv_usec >= 1000000) {
X	t.tv_sec += t.tv_usec / 1000000;
X	t.tv_usec = t.tv_usec % 1000000;
X    }
X    return t;
X}
X
Xstruct timeval update_quotas(struct timeval last, struct timeval current)
X{
X    int nslices;
X    struct descriptor_data *d;
X
X    nslices = msec_diff (current, last) / COMMAND_TIME_MSEC;
X
X    if (nslices > 0) {
X	for (d = descriptor_list; d; d = d -> next) {
X	    d -> quota += COMMANDS_PER_TIME * nslices;
X	    if (d -> quota > COMMAND_BURST_SIZE)
X		d -> quota = COMMAND_BURST_SIZE;
X	}
X    }
X    return msec_add (last, nslices * COMMAND_TIME_MSEC);
X}
X
Xvoid shovechars(int port)
X{
X    fd_set input_set, output_set;
X    long now;
X    struct timeval last_slice, current_time;
X    struct timeval next_slice;
X    struct timeval timeout, slice_timeout;
X    int maxd;
X    struct descriptor_data *d, *dnext;
X    struct descriptor_data *newd;
X    int avail_descriptors;
X
X    sock = make_socket (port);
X    maxd = sock+1;
X    gettimeofday(&last_slice, (struct timezone *) 0);
X
X    avail_descriptors = (getdtablesize() - 4)/2;
X    
X    while (shutdown_flag == 0) {
X	gettimeofday(&current_time, (struct timezone *) 0);
X	last_slice = update_quotas (last_slice, current_time);
X
X	process_commands();
X
X	if (shutdown_flag)
X	    break;
X	timeout.tv_sec = 1000;
X	timeout.tv_usec = 0;
X	next_slice = msec_add (last_slice, COMMAND_TIME_MSEC);
X	slice_timeout = timeval_sub (next_slice, current_time);
X	
X	FD_ZERO (&input_set);
X	FD_ZERO (&output_set);
X	if (ndescriptors < avail_descriptors)
X	    FD_SET (sock, &input_set);
X	for (d = descriptor_list; d; d=d->next) {
X	    if (d->input.head)
X		timeout = slice_timeout;
X	    else
X		FD_SET (d->rdescriptor, &input_set);
X	    if (d->output.head)
X		FD_SET (d->wdescriptor, &output_set);
X	}
X
X	if (select (maxd, &input_set, &output_set,
X		    (fd_set *) 0, &timeout) < 0) {
X	    if (errno != EINTR) {
X		perror ("select");
X		return;
X	    }
X	} else {
X	    (void) time (&now);
X	    if (FD_ISSET (sock, &input_set)) {
X		if (!(newd = new_connection (sock))) {
X		    if (errno
X			&& errno != EINTR
X			&& errno != EMFILE
X			&& errno != ENFILE) {
X			perror ("new_connection");
X			return;
X		    }
X		} else {
X		if (newd->rdescriptor >= maxd)
X		    maxd = newd->rdescriptor + 1;
X		if (newd->wdescriptor >= maxd)
X		    maxd = newd->wdescriptor + 1;
X		}
X	    }
X	    for (d = descriptor_list; d; d = dnext) {
X		dnext = d->next;
X		if (FD_ISSET (d->rdescriptor, &input_set)) {
X			d->last_time = now;
X			if (!process_input (d)) {
X			    shutdownsock (d);
X			    continue;
X			}
X		}
X		if (FD_ISSET (d->wdescriptor, &output_set)) {
X		    if (!process_output (d)) {
X			shutdownsock (d);
X		    }
X		}
X	    }
X	}
X    }
X}
X
Xstatic char hostname[128];
X
Xstruct descriptor_data *new_connection(int sock)
X{
X    int rnewsock, wnewsock;
X    struct sockaddr_in addr;
X    int addr_len;
X    char buf[80];
X    int n;
X
X    addr_len = read(sock,buf,sizeof(buf)-1);
X    if (addr_len>0) {
X	buf[addr_len] = '\0';
X	addr.sin_addr.s_addr = n = atoi(buf);
X    } else {
X	return 0;
X    }
X
X    rnewsock = fifo(n,0);
X    wnewsock = fifo(n,1);
X    if (rnewsock < 0 || wnewsock < 0) {
X	close(rnewsock); close(wnewsock);
X	return 0;
X#ifdef LOCKOUT
X    } else if(forbidden_site(ntohl(addr.sin_addr.s_addr))) {
X	writelog("REFUSED CONNECTION from %s(%d) on descriptor %d\n",
X		addrout(addr.sin_addr.s_addr),
X		ntohs(addr.sin_port), newsock);
X	shutdown(newsock, 2);
X	close(newsock);
X	errno = 0;
X	return 0;
X#endif /* LOCKOUT */
X    } else {
X	strcpy (hostname, addrout (addr.sin_addr.s_addr));
X#ifdef NOISY_LOG
X	writelog("ACCEPT from %s(%d) on descriptor %d\n",
X		 hostname,
X		 ntohs (addr.sin_port), newsock);
X#endif NOISY_LOG
X	return initializesock (rnewsock, wnewsock, &addr, hostname);
X    }
X}
X
Xconst char *addrout(long a)
X{
X    /* New version: returns host names, not octets.  Uses gethostbyaddr. */
X    extern char *inet_ntoa(long);
X    
X#ifdef HOST_NAME
X    struct hostent *he;
X
X    he = gethostbyaddr(&a,sizeof(a),AF_INET);
X    if (he) return he->h_name;
X    else return inet_ntoa(a);
X#else
X    return inet_ntoa(a);
X#endif HOST_NAME
X}
X
X
Xvoid clearstrings(struct descriptor_data *d)
X{
X    if (d->output_prefix) {
X	FREE(d->output_prefix);
X	d->output_prefix = 0;
X    }
X    if (d->output_suffix) {
X	FREE(d->output_suffix);
X	d->output_suffix = 0;
X    }
X}
X
Xvoid shutdownsock(struct descriptor_data *d)
X{
X    if (d->connected) {
X	writelog("DISCONNECT player %s(%d) %d %s\n",
X		db[d->player].name, d->player, d->rdescriptor, d->hostname);
X#ifdef CONNECT_MESSAGES
X	announce_disconnect(d->player);
X#endif CONNECT_MESSAGES
X    } else {
X	writelog("DISCONNECT descriptor %d never connected\n",
X		d->rdescriptor);
X    }
X    clearstrings (d);
X    shutdown (d->rdescriptor, 2);
X    close (d->rdescriptor);
X    shutdown (d->wdescriptor, 2);
X    close (d->wdescriptor);
X    freeqs (d);
X    if (d->prev) d->prev->next = d->next; else descriptor_list = d->next;
X    if (d->next) d->next->prev = d->prev;
X    FREE (d);
X    ndescriptors--;
X}
X
Xstruct descriptor_data *initializesock(int r, int w, struct sockaddr_in *a,
X				       const char *hostname)
X{
X    struct descriptor_data *d;
X
X    ndescriptors++;
X    MALLOC(d, struct descriptor_data, 1);
X    d->rdescriptor = r;
X    d->wdescriptor = w;
X    d->connected = 0;
X/*
X    make_nonblocking (r);
X    make_nonblocking (w);
X*/
X    d->output_prefix = 0;
X    d->output_suffix = 0;
X    d->output_size = 0;
X    d->output.head = 0;
X    d->output.tail = &d->output.head;
X    d->input.head = 0;
X    d->input.tail = &d->input.head;
X    d->raw_input = 0;
X    d->raw_input_at = 0;
X    d->quota = COMMAND_BURST_SIZE;
X    d->last_time = 0;
X    d->address = *a;			/* added 5/3/90 SCG */
X    d->hostname = alloc_string(hostname);
X    if (descriptor_list)
X        descriptor_list->prev = d;
X    d->next = descriptor_list;
X    d->prev = (struct descriptor_data *) NULL;
X    descriptor_list = d;
X    
X    welcome_user (d);
X    return d;
X}
X
Xint make_socket(int port)
X{
X    int s;
X
X    s = fifo(port,0);
X    if (s < 0) {
X	perror ("opening fifo");
X	exit (3);
X    }
X    return s;
X}
X
Xstruct text_block *make_text_block(const char *s, int n)
X{
X	struct text_block *p;
X
X	MALLOC(p, struct text_block, 1);
X	MALLOC(p->buf, char, n);
X	bcopy (s, p->buf, n);
X	p->nchars = n;
X	p->start = p->buf;
X	p->nxt = 0;
X	return p;
X}
X
Xvoid free_text_block (struct text_block *t)
X{
X	FREE (t->buf);
X	FREE ((char *) t);
X}
X
Xvoid add_to_queue(struct text_queue *q, const char *b, int n)
X{
X    struct text_block *p;
X
X    if (n == 0) return;
X
X    p = make_text_block (b, n);
X    p->nxt = 0;
X    *q->tail = p;
X    q->tail = &p->nxt;
X}
X
Xint flush_queue(struct text_queue *q, int n)
X{
X        struct text_block *p;
X	int really_flushed = 0;
X	
X	n += strlen(flushed_message);
X
X	while (n > 0 && (p = q->head)) {
X	    n -= p->nchars;
X	    really_flushed += p->nchars;
X	    q->head = p->nxt;
X	    free_text_block (p);
X	}
X	p = make_text_block(flushed_message, strlen(flushed_message));
X	p->nxt = q->head;
X	q->head = p;
X	if (!p->nxt)
X	    q->tail = &p->nxt;
X	really_flushed -= p->nchars;
X	return really_flushed;
X}
X
Xint queue_write(struct descriptor_data *d, const char *b, int n)
X{
X    int space;
X
X    space = MAX_OUTPUT - d->output_size - n;
X    if (space < 0)
X        d->output_size -= flush_queue(&d->output, -space);
X    add_to_queue (&d->output, b, n);
X    d->output_size += n;
X    return n;
X}
X
Xint queue_string(struct descriptor_data *d, const char *s)
X{
X    return queue_write (d, s, strlen (s));
X}
X
Xint process_output(struct descriptor_data *d)
X{
X    struct text_block **qp, *cur;
X    int cnt;
X
X    for (qp = &d->output.head; cur = *qp;) {
X	cnt = write (d->wdescriptor, cur -> start, cur -> nchars);
X	if (cnt < 0) {
X	    if (errno == EWOULDBLOCK)
X		return 1;
X	    return 0;
X	}
X	d->output_size -= cnt;
X	if (cnt == cur -> nchars) {
X	    if (!cur -> nxt)
X		d->output.tail = qp;
X	    *qp = cur -> nxt;
X	    free_text_block (cur);
X	    continue;		/* do not adv ptr */
X	}
X	cur -> nchars -= cnt;
X	cur -> start += cnt;
X	break;
X    }
X    return 1;
X}
X
X/*
Xvoid make_nonblocking(int s)
X{
X    if (fcntl (s, F_SETFL, FNDELAY) == -1) {
X	perror ("make_nonblocking: fcntl");
X	panic ("FNDELAY fcntl failed");
X    }
X}
X*/
X
Xvoid freeqs(struct descriptor_data *d)
X{
X    struct text_block *cur, *next;
X
X    cur = d->output.head;
X    while (cur) {
X	next = cur -> nxt;
X	free_text_block (cur);
X	cur = next;
X    }
X    d->output.head = 0;
X    d->output.tail = &d->output.head;
X
X    cur = d->input.head;
X    while (cur) {
X	next = cur -> nxt;
X	free_text_block (cur);
X	cur = next;
X    }
X    d->input.head = 0;
X    d->input.tail = &d->input.head;
X
X    if (d->raw_input)
X        FREE (d->raw_input);
X    d->raw_input = 0;
X    d->raw_input_at = 0;
X}
X
Xvoid welcome_user(struct descriptor_data *d)
X{ 
X    queue_string (d, WELCOME_MESSAGE);
X# ifdef CONNECT_FILE
X    do_connect_msg(d, CONNECT_FILE);
X# endif
X}
X
Xvoid goodbye_user(struct descriptor_data *d)
X{
X    write (d->wdescriptor, LEAVE_MESSAGE, strlen (LEAVE_MESSAGE));
X}
X
Xchar *strsave (const char *s)
X{
X    char *p;
X
X    MALLOC (p, char, strlen(s) + 1);
X
X    if (p)
X	strcpy (p, s);
X    return p;
X}
X
Xvoid save_command (struct descriptor_data *d, const char *command)
X{
X    add_to_queue (&d->input, command, strlen(command)+1);
X}
X
Xint process_input (struct descriptor_data *d)
X{
X    char buf[1024];
X    int got;
X    char *p, *pend, *q, *qend;
X
X    got = read (d->rdescriptor, buf, sizeof buf);
X    if (got <= 0)
X	return 0;
X    if (!d->raw_input) {
X	MALLOC(d->raw_input,char,MAX_COMMAND_LEN);
X	d->raw_input_at = d->raw_input;
X    }
X    p = d->raw_input_at;
X    pend = d->raw_input + MAX_COMMAND_LEN - 1;
X    for (q=buf, qend = buf + got; q < qend; q++) {
X	if (*q == '\n') {
X	    *p = '\0';
X	    if (p > d->raw_input)
X		save_command (d, d->raw_input);
X	    p = d->raw_input;
X	} else if (p < pend && isascii (*q) && isprint (*q)) {
X	    *p++ = *q;
X	}
X    }
X    if(p > d->raw_input) {
X	d->raw_input_at = p;
X    } else {
X	FREE(d->raw_input);
X	d->raw_input = 0;
X	d->raw_input_at = 0;
X    }
X    return 1;
X}
X
Xvoid set_userstring (char **userstring, const char *command)
X{
X    if (*userstring) {
X	FREE(*userstring);
X	*userstring = 0;
X    }
X    while (*command && isascii (*command) && isspace (*command))
X	command++;
X    if (*command)
X	*userstring = strsave (command);
X}
X
Xvoid process_commands(void)
X{
X    int nprocessed;
X    struct descriptor_data *d, *dnext;
X    struct text_block *t;
X
X    do {
X	nprocessed = 0;
X	for (d = descriptor_list; d; d = dnext) {
X	    dnext = d->next;
X	    if (d -> quota > 0 && (t = d -> input.head)) {
X		d -> quota--;
X		nprocessed++;
X		if (!do_command (d, t -> start)) {
X		    shutdownsock (d);
X		} else {
X		    d -> input.head = t -> nxt;
X		    if (!d -> input.head)
X			d -> input.tail = &d -> input.head;
X		    free_text_block (t);
X		}
X	    }
X	}
X    } while (nprocessed > 0);
X}
X
Xint do_command (struct descriptor_data *d, char *command)
X{
X    if (!strcmp (command, QUIT_COMMAND)) {
X	goodbye_user (d);
X	return 0;
X    } else if (!strncmp (command, WHO_COMMAND, strlen(WHO_COMMAND))) {
X	if (d->output_prefix) {
X	    queue_string (d, d->output_prefix);
X	    queue_write (d, "\n", 1);
X	}
X	dump_users (d, command + strlen(WHO_COMMAND));
X	if (d->output_suffix) {
X	    queue_string (d, d->output_suffix);
X	    queue_write (d, "\n", 1);
X	}
X    } else if (d->connected &&
X	       !strncmp (command, PREFIX_COMMAND, strlen (PREFIX_COMMAND))) {
X#ifdef ROBOT_MODE
X	if (!Robot(d->player)) { 
X#ifndef TINKER
X	    notify(d->player,
X		   "Only robots can use OUTPUTPREFIX; contact a Wizard.");
X#else TINKER
X	    notify(d->player,
X		   "Only robots can use OUTPUTPREFIX; contact a Tinker.");
X#endif TINKER
X	    return 1;
X	}
X	if (!d->connected) return 1;
X#endif ROBOT_MODE
X	set_userstring (&d->output_prefix, command+strlen(PREFIX_COMMAND));
X    } else if (d->connected &&
X	       !strncmp (command, SUFFIX_COMMAND, strlen (SUFFIX_COMMAND))) {
X#ifdef ROBOT_MODE
X	if (!Robot(d->player)) { 
X#ifndef TINKER
X	    notify(d->player,
X		   "Only robots can use OUTPUTSUFFIX; contact a Wizard.");
X#else TINKER
X	    notify(d->player,
X		   "Only robots can use OUTPUTSUFFIX; contact a Tinker.");
X#endif TINKER
X	    return 1;
X	}
X#endif ROBOT_MODE
X	set_userstring (&d->output_suffix, command+strlen(SUFFIX_COMMAND));
X    } else {
X	if (d->connected) {
X	    if (d->output_prefix) {
X		queue_string (d, d->output_prefix);
X		queue_write (d, "\n", 1);
X	    }
X	    process_command (d->player, command);
X	    if (d->output_suffix) {
X		queue_string (d, d->output_suffix);
X		queue_write (d, "\n", 1);
X	    }
X	} else {
X	    check_connect (d, command);
X	}
X    }
X    return 1;
X}
X
Xvoid check_connect (struct descriptor_data *d, const char *msg)
X{
X    char command[MAX_COMMAND_LEN];
X    char user[MAX_COMMAND_LEN];
X    char password[MAX_COMMAND_LEN];
X    dbref player;
X
X    parse_connect (msg, command, user, password);
X
X    if (!strncmp (command, "co", 2)) {
X	player = connect_player (user, password);
X	if (player == NOTHING) {
X	    queue_string (d, connect_fail);
X	    writelog("FAILED CONNECT %s on %d %s\n",
X		     user, d->rdescriptor, d->hostname);
X	} else {
X	    writelog("CONNECTED %s(%d) on %d %s\n",
X		     db[player].name, player, d->rdescriptor, d->hostname);
X	    d->connected = 1;
X	    d->connected_at = time(NULL);
X	    d->player = player;
X
X	    do_motd (player);
X	    do_look_around (player);
X#ifdef CONNECT_MESSAGES
X	    announce_connect(player);
X#endif CONNECT_MESSAGES
X	}
X    } else if (!strncmp (command, "cr", 2)) {
X#ifndef REGISTRATION	
X	player = create_player (user, password);
X	if (player == NOTHING) {
X	    queue_string (d, create_fail);
X	    writelog("FAILED CREATE %s on %d %s\n",
X		     user, d->rdescriptor, d->hostname);
X	} else {
X	    writelog("CREATED %s(%d) on descriptor %d %s\n",
X		     db[player].name, player, d->rdescriptor, d->hostname);
X	    d->connected = 1;
X	    d->connected_at = time(NULL);
X	    d->player = player;
X
X	    do_motd (player);
X	    do_look_around (player);
X#ifdef CONNECT_MESSAGES
X	    announce_connect(player);
X#endif CONNECT_MESSAGES
X	}
X#else
X	queue_string (d, REGISTER_MESSAGE);
X#endif REGISTRATION	
X    } else {
X	welcome_user (d);
X    }
X}
X
Xvoid parse_connect (const char *msg, char *command, char *user, char *pass)
X{
X    char *p;
X
X    while (*msg && isascii(*msg) && isspace (*msg))
X	msg++;
X    p = command;
X    while (*msg && isascii(*msg) && !isspace (*msg))
X	*p++ = *msg++;
X    *p = '\0';
X    while (*msg && isascii(*msg) && isspace (*msg))
X	msg++;
X    p = user;
X    while (*msg && isascii(*msg) && !isspace (*msg))
X	*p++ = *msg++;
X    *p = '\0';
X    while (*msg && isascii(*msg) && isspace (*msg))
X	msg++;
X    p = pass;
X    while (*msg && isascii(*msg) && !isspace (*msg))
X	*p++ = *msg++;
X    *p = '\0';
X}
X
Xvoid close_sockets(void)
X{
X    struct descriptor_data *d, *dnext;
X
X    for (d = descriptor_list; d; d = dnext) {
X	dnext = d->next;
X	write (d->wdescriptor, shutdown_message, strlen (shutdown_message));
X	if (shutdown (d->rdescriptor, 2) < 0)
X	    perror ("shutdown");
X	close (d->rdescriptor);
X	if (shutdown (d->wdescriptor, 2) < 0)
X	    perror ("shutdown");
X	close (d->wdescriptor);
X    }
X    close (sock);
X}
X
Xvoid emergency_shutdown(void)
X{
X	close_sockets();
X}
X
Xvoid boot_off(dbref player)
X{
X    struct descriptor_data *d, *dnext;
X    for (d = descriptor_list; d; d = dnext) {
X      dnext = d->next;
X      if (d->connected && d->player == player) {
X          process_output(d);
X	  shutdownsock(d);
X      }
X    }
X}
X
Xint bailout (int sig)
X{
X    writelog("BAILOUT: caught signal %d\n", sig);
X    panic("PANIC on spurious signal");
X    _exit(7);
X    return 0;
X}
X
Xint sigshutdown (int sig)
X{
X    writelog("SHUTDOWN: on signal %d\n", sig);
X    shutdown_flag = 1;
X    return 0;
X}
X
X#ifdef DETACH
Xint logsynch (int sig)
X{
X    freopen(logfile, "a", stderr);
X    setbuf(stderr, NULL);
X    writelog("log file reopened\n");
X    return 0;
X}
X#endif DETACH    
X
Xvoid dump_users(struct descriptor_data *e, char *user)
X{
X    struct descriptor_data *d;
X    long now;
X    char buf[1024];
X    int wizard;
X    int reversed, tabular;
X
X    while (*user && isspace(*user)) user++;
X    if (!*user) user = NULL;
X
X    reversed = e->connected && Flag(e->player,REVERSED_WHO);
X    tabular = e->connected && Flag(e->player,TABULAR_WHO);
X
X    (void) time (&now);
X    queue_string(e,
X		 tabular ? "Player Name          On For Idle\n" : "Current Players:\n");
X#ifdef GOD_MODE
X    wizard = e->connected && God(e->player);
X#else GOD_MODE    
X    wizard = e->connected && Wizard(e->player);
X#endif GOD_MODE
X
X    d = descriptor_list;
X    
X    if (reversed)
X	while (d && d->next) d = d->next;
X
X    while (d) {
X	if (d->connected &&
X	    (!user || string_prefix(db[d->player].name, user))) {
X	    if (tabular) {
X		sprintf(buf,"%-16s %10s %4s",
X			db[d->player].name,
X			time_format_1(now - d->connected_at),
X			time_format_2(now - d->last_time));
X		if (wizard) 
X		    sprintf(buf+strlen(buf),
X			    " %s", d->hostname);
X	    } else {
X		sprintf(buf,
X			"%s idle %d seconds",
X			db[d->player].name,
X			now - d->last_time);
X		if (wizard) 
X		    sprintf(buf+strlen(buf),
X			    " from host %s", d->hostname);
X	    }
X	    strcat(buf,"\n");
X	    queue_string (e, buf);
X	}
X	if (reversed) d = d->prev; else d = d->next;
X    }
X}
X
Xchar *time_format_1(long dt)
X{
X    register struct tm *delta;
X    static char buf[64];
X    
X    delta = gmtime(&dt);
X    if (delta->tm_yday > 0)
X	sprintf(buf, "%dd %02d:%02d",
X		delta->tm_yday, delta->tm_hour, delta->tm_min);
X    else
X	sprintf(buf, "%02d:%02d",
X		delta->tm_hour, delta->tm_min);
X    return buf;
X}
X
Xchar *time_format_2(long dt)
X{
X    register struct tm *delta;
X    static char buf[64];
X    
X    delta = gmtime(&dt);
X    if (delta->tm_yday > 0)
X	sprintf(buf, "%dd", delta->tm_yday);
X    else if (delta->tm_hour > 0)
X	sprintf(buf, "%dh", delta->tm_hour);
X    else if (delta->tm_min > 0)
X	sprintf(buf, "%dm", delta->tm_min);
X    else
X	sprintf(buf, "%ds", delta->tm_sec);
X    return buf;
X}
X
X#ifdef CONNECT_MESSAGES
Xvoid announce_connect(dbref player)
X{
X    dbref loc;
X    char buf[BUFFER_LEN];
X
X    if ((loc = getloc(player)) == NOTHING) return;
X    if (Dark(player) || Dark(loc)) return;
X
X    sprintf(buf, "%s has connected.", db[player].name);
X
X    notify_except(db[loc].contents, player, buf);
X}
X
Xvoid announce_disconnect(dbref player)
X{
X    dbref loc;
X    char buf[BUFFER_LEN];
X
X    if ((loc = getloc(player)) == NOTHING) return;
X    if (Dark(player) || Dark(loc)) return;
X
X    sprintf(buf, "%s has disconnected.", db[player].name);
X
X    notify_except(db[loc].contents, player, buf);
X}
X#endif CONNECT_MESSAGES
Xint do_connect_msg(struct descriptor_data * d, const char *filename)
X{
X  FILE           *f;
X  char            buf[BUFFER_LEN];
X
X  if ((f = fopen(filename, "r")) == (FILE *)NULL)
X  {
X    return (0);
X  } else
X  {
X    while (fgets(buf, sizeof buf, f))
X    {
X      queue_string(d, (char *)buf);
X
X    }
X    fclose(f);
X    return (1);
X  }
X}
SHAR_EOFfifoiface.c
if test 47081 -ne "`sum < 'fifoiface.c' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'fifoiface.c'" '(sum should have been 47081)'
	echo shar: "trying 'sum -r'"
	if test 31121 -ne "`sum -r < 'fifoiface.c' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'fifoiface.c'" '(sum should have been 31121)'
	echo shar: "trying 'wc -c'"
		if test 27328 -ne "`wc -c < 'fifoiface.c'`"
		then
			echo shar: "error transmitting 'fifoiface.c'" '(should have been 27328 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 644 'fifoiface.c'
fi
echo shar: "extracting 'tinymud.sh'" '(764 characters)'
if test -f 'tinymud.sh'
then
	echo shar: "will not over-write existing file 'tinymud.sh'"
else
sed 's/^X//' << \SHAR_EOFtinymud.sh > 'tinymud.sh'
X:
X# tinymud client
Xport=4201
Xcd /usr/local/games/lib/netmud
Xtest -p fifor$port || { echo "no server fifo";exit 1; }
Xpid=$$
Xecho $pid >> fifor$port
Xecho "connecting...\c"
Xtries=30
Xwhile test $tries -gt 0
Xdo
X	sleep 1
X	test -p fifor$pid -a -p fifow$pid && break
X	tries=`expr $tries - 1`
Xdone
Xecho
Xtest $tries -eq 0 && { echo "$0: timeout waiting for server";exit 1; }
Xtrap '' 0 1 2 3 15
Xcat -u fifow$pid &
Xchild=$!
Xcleanup() {
X	kill $child 2>/dev/null
X	test -p fifor$pid && echo QUIT >> fifor$pid
X	rm -f fifor$pid
X}
Xtrap 'cleanup; exit' 0
Xtrap 'exit' 1 2 3 15
Xwhile test -p fifor$pid -a -p fifow$pid && read x
Xdo
X	case "$x" in
X	+x) set +x;;
X	-x) set -x;;
X	!) sh -i;echo "\n$0";;
X	*) test -p fifor$pid || break
X	   echo "$x" >> fifor$pid
X	   sleep 1
X	   ;;
X	esac
Xdone
SHAR_EOFtinymud.sh
if test 59606 -ne "`sum < 'tinymud.sh' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'tinymud.sh'" '(sum should have been 59606)'
	echo shar: "trying 'sum -r'"
	if test 34991 -ne "`sum -r < 'tinymud.sh' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'tinymud.sh'" '(sum should have been 34991)'
	echo shar: "trying 'wc -c'"
		if test 764 -ne "`wc -c < 'tinymud.sh'`"
		then
			echo shar: "error transmitting 'tinymud.sh'" '(should have been 764 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 644 'tinymud.sh'
fi
echo shar: "extracting 'restart'" '(381 characters)'
if test -f 'restart'
then
	echo shar: "will not over-write existing file 'restart'"
else
sed 's/^X//' << \SHAR_EOFrestart > 'restart'
X:
Xumask 002
Xport=4201
XS=/u2/src/games/tinymud/src
XL=/usr/local/games/lib/netmud
Xnetmud=$S/fifonetmud
Xcd $L
X
Xmv tinymud.db tinymud.db.old 2>/dev/null
Xif test -f tinymud.db.new; then
X	mv tinymud.db.new tinymud.db
Xelse
X	cp tinymud.db.old tinymud.db
Xfi
Xrm -f fifo*
Xecho RESTARTED AT `date` >> tinymud.log
X# ignore hup
Xtrap '' 1
Xexec $netmud tinymud.db tinymud.db.new $port tinymud.log
SHAR_EOFrestart
if test 32991 -ne "`sum < 'restart' | sed 's/ .*//'`"
then
	echo shar: "possible error transmitting 'restart'" '(sum should have been 32991)'
	echo shar: "trying 'sum -r'"
	if test 22561 -ne "`sum -r < 'restart' | sed 's/ .*//'`"
	then
		echo shar: "probable error transmitting 'restart'" '(sum should have been 22561)'
	echo shar: "trying 'wc -c'"
		if test 381 -ne "`wc -c < 'restart'`"
		then
			echo shar: "error transmitting 'restart'" '(should have been 381 characters)'
		else
			echo shar: "wc was ok"
		fi
	fi
fi
chmod 755 'restart'
fi
exit 0
#	End of shell archive