[comp.sources.games] v11i015: tinyclients - three clients for tinyMUD, Part01/03

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

Submitted-by: James Aspnes <asp@cs.cmu.edu>
Posting-number: Volume 11, Issue 15
Archive-name: tinyclients/Part01

	[As mentioned in the previous posting of tinyuMUD, here is
	 a collection of three mud client programs (one can of
	 course use 'telnet').  -br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 3)."
# Contents:  README MANIFEST tint_shar.dcl tinytalk tinytalk/keyboard.c
#   tinytalk/tl.h
# Wrapped by billr@saab on Fri Jul 27 15:45:14 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(535 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is a selection of three of the more popular clients for the
XtinyMUD program.
X
X	tint - a VMS client
X	tinymud.el - a EMACS major mode client
X	tinytalk - a Unix/curses client
X
Xtint_shar.dcl is a VMS DCL archive file. It should be copied intact
Xto a VMS host and fed to DCL. The tinytalk subdirectory contains
Xthe Makefile and README files for tinytalk.
X
Xtinytalk came from derby.cs.wisc.edu
Xtinymud.el and tint came from lancelot.avalon.cs.cmu.edu
X
X	-Bill Randle
X	Moderator, comp.sources.games
X	billr@saab.CNA.TEK.COM
X	July 27, 1990
END_OF_FILE
if test 535 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(765 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X README                     1	
X tint_shar.dcl              1	
X tinymud.el                 2	
X tinytalk                   1	
X tinytalk/CHANGES           3	
X tinytalk/Makefile          3	
X tinytalk/README            3	
X tinytalk/command.c         2	
X tinytalk/config.c          3	
X tinytalk/fred.c            2	
X tinytalk/gag.c             3	
X tinytalk/keyboard.c        1	
X tinytalk/log.c             3	
X tinytalk/macro.c           3	
X tinytalk/special.c         3	
X tinytalk/stuff.c           3	
X tinytalk/tinytalk.doc      2	
X tinytalk/tl.h              1	
X tinytalk/util.c            3	
X tinytalk/wrap.c            3	
END_OF_FILE
if test 765 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'tint_shar.dcl' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tint_shar.dcl'\"
else
echo shar: Extracting \"'tint_shar.dcl'\" \(37328 characters\)
sed "s/^X//" >'tint_shar.dcl' <<'END_OF_FILE'
X$!
X$! This is a DCL shell archive
X$! to de-archive it, just run it as a DCL script
X$!
X$ write sys$output "MAKEFILE."
X$ copy sys$input MAKEFILE.
X$ deck /dollars="SHARC_EOF_MARK"
XCC	=	cc
XLINK	=	link
XLIBRARY	=	tint.opt/opt
XRM	=	delete/nolog
XPUR	=	purge/nolog
X
Xtint.exe: tint.obj tint_cmd.obj tint_filt.obj tint_netc.obj tint.opt 
X	$(LINK) tint,tint_cmd,tint_filt,tint_netc,$(LIBRARY)
X
Xtint.obj: tint.h tint.c
X	$(CC) tint
X
Xtint_cmd.obj: tint_cmd.c tint.h
X	$(CC) tint_cmd
X
Xtint_filt.obj: tint_filt.c tint.h
X	$(CC) tint_filt
X
Xtint_netc.obj: tint_netc.c tint.h
X    $(CC) tint_netc
X
Xclean:
X	$(RM) tint*.obj;*
X	$(PUR) tint.exe
XSHARC_EOF_MARK
X$ write sys$output "TINT.C"
X$ copy sys$input TINT.C
X$ deck /dollars="SHARC_EOF_MARK"
X/*
X
X	Tinymud INTerface program, by me. Feb 16-17 '90
X	I hereby release this code into the public domain, I make no guarantees
X	as to this software's reliability. If you make some neato mods, drop me
X	a note. Andrew Molitor aka bob.
X
X		AMOLITOR@EAGLE.WESLEYAN.EDU
X
X	If you find a bug, you're basically on your own. If??? Hah!
X
X */
X
X/*    Changelog:
XAndrew Molitor: (amolitor@eagle.wesleyan.edu):
X  Some cosmetic changes both to code and output, with parameters for macros.
X
XNick Castellano (ncastellano@eagle.wesleyan.edu):
X  Fixed a bug in \gag which caused a blank line to be printed every time a 
X   gagged person spoke.  Fixed using global variable 'gagged' to supress
X   the unwanted newline.
X  Fixed restoration of full scrolling region on program termination.
X  Fixed the mangled #define for RESTORE_CURSOR.  Not sure why this worked
X   at all as it was, all run together.
X  Cleaned up termination of \help and \dump.
X  Installed \version command.
X  Installed \spawn command.  This invlolved moving all the vt220 codes to
X   tint.h, so tint.h is now #included in tint.c.  Appropriate change was made
X   in the makefile.
X  Installed \save and \load commands to save/load macro and gag lists.
X  Installed support for startup file, fixed bugs in load routine.
X  Named the program TINT, in the spirit of another famous program from 
X   Wes.  I took this liberty since the program didn't seem to have a name.
X
XAndrew Molitor (amolitor@eagle.wesleyan.edu):
X  Change to expand_macro() in tint_filt.c to support embedding carriage 
X   returns in a macro definition -- a single % is expanded the a CR LF
X   pair, and a double %% expands to a single %.
X  Fixed bug in expand_macro() which caused the cooked line to be prematurely
X   terminated sometimes.
X  Modified the handling of the ``found a #, but its not a parameter'' case of
X   expand_macro().  The effect of this is to convert all pairs ## in a macro 
X   definition into a single # in the expanded macro. This allows you to put 
X   things like ##12591 into a macro. This expands to #12591. Without this mod,
X   the first # would have been passed direct, the second, being followed by a
X   digit (1) would be expanded as the first parameter to the macro (if any) 
X   or discarded (if no parameters given).
X  There's a printf("Done = TRUE") or something hidden in expand_macro() 
X   somewhere. This was debugging code and has been deleted.
X
XKevin Rainier (krainier@eagle.wesleyan.edu):
X
X  Added tint_netc.c -- allows input of host by either octets or name.  Allows
X   input of port by either number or service (ie: smtp, telnet, but NOT
X   "tinymud").
X*/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <descrip.h>
X#include <types.h>
X
X/* These are includes for the Multinet routines. They may be hidden
Xin some directory like ``MULTINET_ROOT:[MULTINET.INCLUDES]'' on your
Xsystem  */
X
X#include <netdb.h>
X#include <in.h>
X#include <iodef.h>
X#include <socket.h>
X#include "tint.h"
X
Xhandle_output();
X
X/* I/O status block for read requests */
X
Xunsigned short iosb[4];
X
X/* current character co-ordinates in the output window */
X
Xint out_x,out_y;
X
X/* a generic word buffer for word wrapping */
X
Xchar word[80];
X
X/* buffer for raw output from tinymud  */
X
Xchar buffer[5000];
X
X/* Channel number for my socket to TinyMud */
X
Xunsigned short tmud_chan;
X
X/* gag flag */
X
Xint gagged;
X
X/* load handler to load default.mud */
X
Xextern int load_handler();
X
X/********************************************
X
X	DO IT TO IT
X
X*********************************************/
X
Xmain(argc,argv)
X
Xint argc;
Xchar *argv[];
X
X	{
X	char *tst;
X	int flags;
X	long sts;
X	unsigned short tmud_port;
X	char *host_name;
X	struct sockaddr_in sin;
X	struct hostent *hp;
X	struct servent *sp;
X
X    /* Make connection...default is DAISY at 4201 */
X    switch (argc) {
X        case 1 : tmud_chan = netconnect("128.2.218.26","4201"); break;
X        case 2 : tmud_chan = netconnect(argv[1],"4201");        break;
X        case 3 : tmud_chan = netconnect(argv[1], argv[2]);      break;
X        default: printf("Usage: tint <host> <port>\n");         exit(1);
X    }
X    if (tmud_chan==0) exit(1);
X
X	/* initialize all stuff for i/o filters */
X
X	init_filters();
X        gagged = 0;
X
X	/* Initialize the screen. Do your own scrolling region. It's easier */
X
X	printf(HOME);printf(ERASE_EOS);
X	printf(SCROLLING_REGION);
X	out_x = out_y = 1;
X
X	/* set up asynchronous handling for input from TinyMud */
X
X	sys$qio(0,tmud_chan,IO$_READVBLK,&iosb,handle_output
X		,0,buffer,5000,0,0,0,0);
X                                    
X        load_handler("defaultmud","",0);
X
X	handle_input();
X
X	/* We get back here only when Herr User exits, so tidy up and quit */
X
X	printf(HOME);printf(ERASE_EOS);
X	printf(FULL_SCROLLING_REGION);
X	}
X
X/**********************************************************************
X
X	Get input from the user, cook it, and stuff it into the socket
Xconnected to TinyMud. Continue until the user quits.
X
X***********************************************************************/
X
Xhandle_input()
X
X	{
X	char raw_buff[256],cooked_buff[512];
X	int sts;
X	int siz;
X
X	for(;;)
X		{
X		/* Move the cursor to line 22, col 1 */
X
X		move_to(22,1);
X		printf(ERASE_EOS);
X		printf("TINT>");
X		gets(raw_buff);
X
X		/* Process commands and/or embedded macros. This returns one of
X		2 -- quit, 1 -- send cooked to TinyMud, or 0 -- do nothing */
X
X		sts = filter_input(raw_buff,cooked_buff,512);
X		if(sts == 2)
X			{
X			socket_close(tmud_chan);
X			return;
X			}
X		if(sts == 1)
X			{
X			siz = strlen(cooked_buff);
X			cooked_buff[siz] = (char) 13;
X			siz++;
X			cooked_buff[siz] = (char) 10;
X			siz++;
X			if( socket_write(tmud_chan,cooked_buff,siz) == -1)
X				{	
X		      		socket_perror("\nFailed send");
X				}
X			}
X		}
X	}
X
X/*********************************************************************
X
X	Called by AST every time TinyMud says something. Filter whatever
XTinyMud has to say, spew it out on the screen, and queue up
Xanother super-cool AST driven read request on the TinyMud socket, and quit.
X
X**********************************************************************/
X
Xhandle_output()
X
X	{
X	static char cooked_buffer[5000];
X
X	if( iosb[1] == 0)
X		return;
X
X	if( iosb[0] == 1)
X		{
X		if( filter_output(buffer,cooked_buffer,5000) == 1)
X			{
X			display_output(iosb[1],cooked_buffer);
X			}
X                else
X		        {
X                        gagged = 1;
X                        }
X		}
X
X	/* Set up another read request */
X
X	sys$qio(0,tmud_chan,IO$_READVBLK,&iosb,handle_output
X		,0,buffer,5000,0,0,0,0);
X
X	/* return */
X	}
X
X/*************************************************************************
X
XRoutine for displaying TinyMud output. Basically just does word wrapping, and
Xtracks the cursor position for output. Remember, output is done asychronously,
Xso the cursor may not be where you left it. Be careful.
X
X*************************************************************************/
X
Xdisplay_output(count,buff)
X
Xshort count;
Xchar *buff;
X
X	{
X	int curr_char,i,j;
X	int newln;
X	char ch;
X
X	printf(SAVE_CURSOR);
X
X	move_to(out_y,out_x);
X	/* copy the text out with word wrapping */
X
X	curr_char = 0;  /* point to 1st character in buffer */
X
X	for(;count > 0;)
X		{
X		/* skip spaces, and keep an eye out for carriage returns
X			and line feeds. */
X
X		newln = FALSE;
X		for(; (isspace(ch = buff[curr_char])) && (count > 0);)
X			{
X			if( (ch == '\015') || (ch == '\012') )
X				newln = TRUE;
X			count--;
X			curr_char++;
X			}
X
X		/* If skipped one or more CRs,LFs, then output a newline */
X		if( newln ) newline();
X
X		if(count <= 0) break;  /* If we fell off the end, quit */
X
X		/* assemble a word of output */
X
X		i = 0;
X		for(; (i < 80) && !(isspace(ch = buff[curr_char]))
X				&& (count > 0);)
X			{
X			word[i++] = buff[curr_char++];
X			count--;
X			}
X
X		/* dump it out, on this line if it fits, else newline
X			then dump it. Follow by a space */
X
X		if( (i + out_x) > 80)
X			{
X			newline();
X			}
X
X		out_x += (i+1);
X		for(j = 0; i > 0; i--)
X			{
X			putchar(word[j++]);
X			}
X		putchar(' ');
X		}
X	printf(RESTORE_CURSOR);
X	}
X
X/**********************************************************************
X
X	Couple teeny little routines for display_output()
X
X***********************************************************************/
X
X/* routine to move the cursor to a position */
X
Xmove_to(line,col)
X
Xint line,col;
X
X	{
X	printf("\033[%d;%dH",line,col);
X	}
X
X/* routine to do a new line in the output, and track all the garbage */
X
Xnewline()
X	{
X        if (gagged == 0)
X	        {
X            	out_x = 1;  
X         	out_y = (out_y >= 20 ? 20 : out_y+1);
X         	printf("\n");
X	        }
X        else
X	        {
X                gagged = 0;
X                }
X	}
X
XSHARC_EOF_MARK
X$ write sys$output "TINT.DOC"
X$ copy sys$input TINT.DOC
X$ deck /dollars="SHARC_EOF_MARK"
XTinymud INTerface (TINT) for VMS, Version 1.3
X=============================================
X
XInvoking the Interface
X----------------------
X
X	There are two optional parameters, the first is the host.  You may specify
Xit either as a numeric Internet address (e.g. 128.2.218.26) or as a "host
Xname" (e.g. DAISY.LEARNING.CS.CMU.EDU).  The second is the port which may also
Xappear as a number (e.g. 4201) or a recognized service (e.g. telnet, smtp,
Xfinger, etc.).  If one or both parameters does not appear, they default to
X128.2.218.26 (DAISY) and 4201, respectively.
X
XUsing the Interface
X-------------------
X
X	There are two windows. One runs from the top of the screen down
Xto about line 20 -- this is where TinyMud output goes. The other runs from line
X21 down to the bottom. You type input in lines 22 down, the interface puts
Xinformational messages on line 21. At the TINT> prompt, if you type text
X(without any \ characters in it), this text is sent unmodified to TinyMud.
X
X	Apart from this, the interface has a few interface commands, and
Xsupports very simple text macros.Both commands and macros begin with a \
Xcharacter.
X
X	The commands are: 
X
X\quit -- quit the program
X\gag -- gag a person (fairly effectively -- deletes ANY stuff from TinyMud
X	which starts with that person's name)
X\ungag -- ungag a person
X\help -- gives you help. Obviously.
X\def -- define a text macro. The macro name must start with a \.
X\version -- displays current version number.
X\spawn -- spawns a subprocess.
X\save -- save macro/gag list to a file.
X\load -- load macro/gag list from a file.
X
XText Macros
X-----------
X
X	When you define a text macro, every time the interface detects the
Xmacro name, it will expand it to the equivalent text before sending it off to
XTinyMud. You can have parameters in your macro. To do this, you put #n 
X(n = 1 single digit 1 though 9) in the equivalence string wherever you want a
Xparameter to appear. Then when you call the macro, you give it a parameter
Xlist of things to substitute in for the #n's.
X
X
XFor example:
X
XTINT>\def \hi=Hi There #1
XTINT>say hi(ralph)
X
XTINT>\def \hello=hi there #1, hows #2?
XTINT>say \hello(ralph,the wife)
X
X	If you have a parameter specified in the equivalence string, but do not
Xspecify it in the parameter list, it is deleted.
X
XNOTE: the space after \def is (currently) required.
XNOTE: NO space is allowed between the macro name and the parameter list.
XNOTE: in a \def, you MUST start the macro name with a \, and there must
Xbe a space after \def. I.E.
X
X\def \hi=stuff    -- this works
X
X\def\hi=stuff
X\def hi=stuff     - these don't
X\defhi=stuff
X
X
XConventions For the \ Character
X-------------------------------
X
X	(I apologize for this, it just sort of happened :-)
X
X	At the beginning of an input line:
X		a single \ indicates a command (\gag etc...)
X		a double \\ is interpreted as the beginning of a macro
X		a triple \\\ is a literal \ character
X
X	In the middle of a line
X		a single \ indiactes start of macro
X		a double \\ is a literal \ char.
X
XFor Example:
X
XTINT>\def \qu=QUIT  <--- single slash -- hence the def command
XTINT>\\qu           <--- this sends ``QUIT'' to TinyMud
X
Xbut:
X
XTINT>say I \qu   <--- sends ``say I QUIT'' to TinyMud
X
X
X
XCompatibility and Porting Issues
X--------------------------------
X
X	This program is designed for vt220 terminals. I think it ought to work
XOK on vt100's and anything compatible therewith. I have tried, somewhat
Xunsuccessfully, to keep the terminal dependent stuff segregated into #defines.
XBe warned that quite a bit of stuff is hard-coded.
X
X	The network software we use is Multinet. We have a socket library and a
X$QIO interface, which sort of work. This program uses them. There are a lot of
Xinclude files for this system, my compiler can find them on my system, dunno
Xabout yours.
X
X
XALL THE CODE FOR THIS PROGRAM IS HEREBY RELEASED INTO THE PUBLIC DOMAIN
X
X			Andrew Molitor aka bob
X
X
XLoading and Saving Macros and Gags
X----------------------------------
X
X	To save your current macros and gags to a file, enter "\save filename".
X"\load filename" will read in a file that was saved.  These files can be 
Xedited with a text editor such as TPU or EMACS as well.  
X
X	Upon startup, the program will look for a file called DEFAULTMUD.  It 
Xwill load all the definitions in the file if it finds it, or display an error 
Xmessage if it doesn't.  If you don't want a startup file, ignore the message.
XIf you want to call your startup file something other than DEFAULTMUD then just
Xenter "define defaultmud newname" at your DCL prompt (or in your login.com).
X
XSpawning a DCL Subprocess
X-------------------------
X
X	To spawn a new DCL process, simply enter the command "\spawn".
X
X
X
X                         ncastellano@eagle.wesleyan.edu
X                           a.k.a. entropy
X                           a.k.a. enthalpy
X
X
XSHARC_EOF_MARK
X$ write sys$output "TINT.H"
X$ copy sys$input TINT.H
X$ deck /dollars="SHARC_EOF_MARK"
X/*
X
X        Feb 17, 1990
X	Header file for Tinymud INTerface. by Andrew Molitor.
X	This is released into the Public Domain. Enjoy
X
X*/
X
X#define VERSION "TINT version 1.5"
X
X/* Escape sequences for vt220. These might work on a vt100 as well, but not on
Xmost anything else. */
X
X#define SCROLLING_REGION "\033[1;20r"   /* lines 0 through 20 will scroll */
X#define FULL_SCROLLING_REGION "\033[1;24r"   /* entire screen will scroll */
X#define ERASE_EOS "\033[J"
X#define HOME "\033[H"
X#define SAVE_CURSOR "\0337"
X#define RESTORE_CURSOR "\0338"
X#define CRLF "\015\012"
X
X/* structure for the list of gags. Back-linked for simplicity, and because I
X	like it that way */
X
Xstruct gag
X	{
X	char *name;
X	struct gag *back;
X	struct gag *fwd;
X	};
X
X/* structure for the list of text macros. Back-linked for simplicity, and
X	because I like it that way */
X
Xstruct txt_mac
X	{
X	char *name;
X	char *value;
X	struct txt_mac *back;
X	struct txt_mac *fwd;
X	};
X
X/* Structure for a built in command. We just have an array of these */
X
Xstruct cmd
X	{
X	char *name;     /* command name */
X	char *help_txt; /* a little help text for the command */
X	int (*handler)(); /* address of this command's handler routine */
X	};
X
X/* function declarations */
X
Xchar *skipspaces();
Xunsigned short netconnect(char *host, char *port);
XSHARC_EOF_MARK
X$ write sys$output "TINT.OPT"
X$ copy sys$input TINT.OPT
X$ deck /dollars="SHARC_EOF_MARK"
Xsys$share:vaxcrtl/share
Xmultinet:multinet_socket_library.exe/share
XSHARC_EOF_MARK
X$ write sys$output "TINT_CMD.C"
X$ copy sys$input TINT_CMD.C
X$ deck /dollars="SHARC_EOF_MARK"
X/*
X	Feb 17 1990
X	This software is released into the Public Domain. Enjoy.
X
X			Andrew Molitor
X
X	Tinymud INTerface command handlers
X*/
X
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include "tint.h"
X
X/*
X	Table of built in commands
X*/
X
Xint def_handler();
Xint gag_handler();
Xint help_handler();
Xint ungag_handler();
Xint quit_handler();
Xint dump_handler();
Xint version_handler();
Xint spawn_handler();
Xint save_handler();
Xint load_handler();
X
Xstruct cmd builtins[]=
X	{
X	{"def","\macro_name=macro_text defines a text macro",def_handler},
X	{"gag","person_name installs a gag",gag_handler},
X	{"help","gives you this help",help_handler},
X	{"ungag","person_name removes a gag",ungag_handler},
X	{"quit","quits this program",quit_handler},
X	{"dump","dumps gag and macro lists (debug cmd)",dump_handler},
X        {"version","displays current version number",version_handler},
X	{"spawn","spawns out a subprocess",spawn_handler},
X        {"save","filename saves gag and macro lists",save_handler},
X        {"load","filename loads gag and macro lists",load_handler},
X	{NULL,NULL,NULL}
X	};
X
Xextern struct gag *gag_list;
Xextern struct txt_mac *mac_list;
X
X/*************************************************************************
X
X	 The various command handlers
X
X***************************************************************************/
X
X/* define macro command:  \def name=string */
X
Xdef_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	struct txt_mac *new_mac, *tmp;
X	char *str,*name,*val;
X
X	/* skip spaces before the name */
X
X	for(;(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	if (*in_buff == '\0')
X		{
X		message("Macro name?");
X		return(0);
X		}
X	if (*in_buff != '\\')
X		{
X		message("Start Macro name with \\");
X		return(0);
X		}
X
X	in_buff++;  /* skip leading `/' */
X	str = in_buff;  /* macro name */
X
X	/* now terminate the name at the first white space or at the 
X		`=' sign */
X
X	for(;!(isspace(*in_buff)) && (*in_buff != '\0') && (*in_buff != '=');)
X		in_buff++;
X
X	if( *in_buff == '\0')
X		{
X		message("Invalid macro definition");
X		return(0);
X		}
X	else if( isspace(*in_buff))
X		{
X		/* terminate the macro name here */
X
X		*in_buff++ = '\0';
X
X		/* skip ahead to the `=' sign, and skip over it */
X
X		for(;(*in_buff != '=') && (*in_buff != '\0');) in_buff++;
X		if( *in_buff == '\0')
X			{
X			message("Invalid macro definition");
X			return(0);
X			}
X		in_buff++;
X		}
X	else  /* The macro name must end with the '=' */
X		{
X		*in_buff++ = '\0';
X		}
X
X	/* Do we already have a macro of this name? If so, redefine */
X
X	if( (tmp = mac_list_srch(str)) != NULL )
X		{
X		message("Macro exists, redefining");
X		val = malloc(strlen(in_buff)+1);
X		if( val == NULL)
X			{
X			message("Could not redefine macro");
X			}
X		else
X			{
X			free(tmp->value);
X			strcpy(val,in_buff);
X			tmp->value = val;
X			}
X		return(0);
X		}
X
X	/* otherwise make a new macro */
X
X	name = malloc(strlen(str)+1);
X	val = malloc(strlen(in_buff)+1);
X	new_mac = (struct txt_mac *)malloc(sizeof(struct txt_mac));
X
X	if((new_mac == NULL) || (name == NULL) || (val == NULL))
X		{
X		message("Could not define macro");
X		return(0);
X		}
X
X	strcpy(name,str);
X	strcpy(val,in_buff);
X	new_mac->name = name;
X	new_mac->value = val;
X
X	/* Now insert the new macro into the list */
X
X	if(mac_list == NULL)
X		{
X		mac_list = new_mac;
X		new_mac->fwd = new_mac->back = new_mac;
X		}
X	else
X		{
X		new_mac->fwd = mac_list->fwd;
X		new_mac->back = mac_list;
X		(mac_list->fwd)->back = new_mac;
X		mac_list->fwd = new_mac;
X		}
X
X
X	return(0);
X	}
X
X/* help handler. Format:  \help  */
X
Xhelp_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	int i;
X	char str[80];
X
X	for(i=0 ; builtins[i].name != NULL ;i++)
X		{
X		sprintf(str,"\\%s %s  (press CR)",
X			builtins[i].name , builtins[i].help_txt);
X		message(str);
X		getchar();
X		}
X	move_to(21,1);
X        printf("\033[K");
X	return(0);
X	}
X
X/* Add an item to the gag-list. Format: \gag person */
X
Xgag_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	struct gag *new_gag;
X	char *str,*name;
X
X	/* skip spaces in before the name */
X
X	for(;(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	if (*in_buff == '\0')
X		{
X		message("Gag who?");
X		return(0);
X		}
X
X	str = in_buff;
X
X	/* now terminate the name at the first space */
X
X	for(;!(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	*in_buff = '\0';
X	name = malloc(strlen(str)+1);
X	new_gag = (struct gag *)malloc(sizeof(struct gag));
X	if((new_gag == NULL) || (name == NULL) )
X		{
X		message("Could not install gag");
X		return(0);
X		}
X
X	strcpy(name,str);
X	new_gag->name = name;
X
X	/* Now insert the new gag into the list */
X
X	if(gag_list == NULL)
X		{
X		gag_list = new_gag;
X		new_gag->fwd = new_gag->back = new_gag;
X		}
X	else
X		{
X		new_gag->fwd = gag_list->fwd;
X		new_gag->back = gag_list;
X		(gag_list->fwd)->back = new_gag;
X		gag_list->fwd = new_gag;
X		}
X
X	return(0);
X	}
X
X/* Remove an item to the gag-list. Format \ungag person */
X
Xungag_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	char *str;
X	struct gag *tmp;
X
X	/* skip spaces in before the name */
X
X	for(;(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	if (*in_buff == '\0')
X		{
X		message("Ungag who?");
X		return(0);
X		}
X
X	str = in_buff;
X
X	/* now terminate the name at the first space */
X
X	for(;!(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	*in_buff = '\0';
X
X	/* Now str points at the name of the un-gagee */
X
X	if( (tmp = gag_list_srch(str)) == NULL )
X		{
X		message("Not Gagged");
X		}
X	else
X		{
X		/* delete the entry from the gag_list */
X
X		if( tmp->fwd == tmp )  /* this is the only thing */
X			{
X			gag_list = NULL;
X			}
X		else
X			{
X			(tmp->back)->fwd = tmp->fwd;
X			(tmp->fwd)->back = tmp->back;
X			if( tmp == gag_list )
X				gag_list = tmp->fwd;
X			}
X		free(tmp->name);
X		free(tmp);
X		}
X	return(0);
X	}
X
X/* Dump the macro list and gag list. Debugging code. Format: \dump */
X
Xdump_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	struct gag *tmp;
X	struct txt_mac *tmp2;
X
X	if(gag_list != NULL)
X		{
X		printf(HOME);
X		printf(ERASE_EOS);
X		tmp=gag_list;
X		printf("%s is gagged",tmp->name);
X		tmp = tmp->fwd;
X		for(;tmp != gag_list;)
X			{
X			printf("\n%s is gagged",tmp->name);
X			tmp = tmp->fwd;
X			}
X		}
X	message("For Macros -- press CR");
X	getchar();
X	if(mac_list != NULL)
X		{
X		printf(HOME);
X		printf(ERASE_EOS);
X		tmp2=mac_list;
X		printf("%s == %s",tmp2->name,tmp2->value);
X		tmp2 = tmp2->fwd;
X		for(;tmp2 != mac_list;)
X			{
X			printf("\n%s == %s",tmp2->name,tmp2->value);
X			tmp2 = tmp2->fwd;
X			}
X		}
X        move_to(21,1);
X        printf("\033[K");
X	return(0);
X	}
X/* Load a macro list and gag list. Format: \load filename */
X
Xload_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X        FILE *infile;
X        char line[256];
X        char work[256];
X	/* skip spaces in before the filename */
X
X	for(;(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	if (*in_buff == '\0')
X		{
X		message("Load filename?");
X		return(0);
X		}
X
X        if ((infile = fopen(in_buff,"r")) == NULL)
X                {
X                message("Error opening input file.");
X                return(0);
X	        }
X
X        while (!(feof(infile)) && !(ferror(infile)))
X	        {
X                fgets(line,255,infile);
X                if (line[strlen(line)-1] == '\n')
X		  {
X                  line[strlen(line)-1] = '\000';
X	          }
X                filter_input(line,work,strlen(line));
X	        }
X
X        if ((fclose(infile)) == EOF)
X                {
X                message("Error closing input file.");
X                return(0);
X	        }
X
X        message("Loaded.");
X	return(0);
X	}
X
X/* Save the macro list and gag list. Format: \save filename */
X
Xsave_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X        FILE *outfile;
X
X	struct gag *tmp;
X	struct txt_mac *tmp2;
X
X	/* skip spaces in before the filename */
X
X	for(;(isspace(*in_buff)) && (*in_buff != '\0');) in_buff++;
X
X	if (*in_buff == '\0')
X		{
X		message("Save filename?");
X		return(0);
X		}
X
X        if ((outfile = fopen(in_buff,"w")) == NULL)
X                {
X                message("Error opening output file.");
X                return(0);
X	        }
X
X	if(gag_list != NULL)
X		{
X		tmp=gag_list;
X		fprintf(outfile,"\\gag %s",tmp->name);
X		tmp = tmp->fwd;
X		for(;tmp != gag_list;)
X			{
X			fprintf(outfile,"\n\\gag %s",tmp->name);
X			tmp = tmp->fwd;
X			}
X		}
X        fprintf(outfile,"\n");
X	if(mac_list != NULL)
X		{
X		tmp2=mac_list;
X		fprintf(outfile,"\\def \\%s=%s",tmp2->name,tmp2->value);
X		tmp2 = tmp2->fwd;
X		for(;tmp2 != mac_list;)
X			{
X			fprintf(outfile,"\n\\def \\%s=%s",tmp2->name,tmp2->value);
X			tmp2 = tmp2->fwd;
X			}
X		}
X
X        if ((fclose(outfile)) == EOF)
X                {
X                message("Error closing output file.");
X                return(0);
X	        }
X
X        message("Saved.");
X	return(0);
X	}
X
X/* handle the quit command. Format: \quit */
X
Xquit_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	return(2);
X	}
X
X/* handle the version command. Format: \version */
X
Xversion_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X        {
X        message(VERSION);
X        return(0);
X        }
X
X/* handle the spawn command. Format: \spawn */
X
Xspawn_handler(in_buff,out_buff,size)
Xchar *in_buff,*out_buff;
Xint size;
X
X        {
X        extern int out_x, out_y;
X        printf(HOME);
X        printf(ERASE_EOS);
X        printf(FULL_SCROLLING_REGION);
X        lib$spawn();
X        printf(HOME);
X        printf(ERASE_EOS);
X        printf(SCROLLING_REGION);
X        out_x = out_y = 1;
X        return(0);
X        }
X
X/**********************************************************
X
X
X	Routine for giving the user messages on line 21
X
X***********************************************************/
X
X
Xmessage(str)
X
Xchar *str;
X
X	{
X	move_to(21,1);
X	printf("\033[K-->");
X	printf(str);
X	}
X
X
X
X
X
X
X
X
XSHARC_EOF_MARK
X$ write sys$output "TINT_FILT.C"
X$ copy sys$input TINT_FILT.C
X$ deck /dollars="SHARC_EOF_MARK"
X/*
X
X	Feb 17 1990
X	By me, Andrew Molitor. This is released into the Public Domain.
X		Enjoy.
X
X	TinyMud interface I/O filters. Includes some generic string handling
X	routines. One bolder than I might term them primitive parsing routines,
X	but I shan't.
X
X	The issue of '\' characters on comamnds works like this:
X
X		At the start of line:
X			a single \ must be followed by a command
X			a pair of slashes must be followed by a macro name
X			a trio of slashes is interpreted as a literal \ char
X		In the middle of a line:
X			a single \ must precede a macro name
X			a double \ is interpreted as a literal \ char
X				
X*/
X
X#define ERROR 1
X#define OK 0
X
X#include <stdio.h>
X#include <string.h>
X#include <ctype.h>
X#include "tint.h"
X
Xchar *expand_macro();
X
Xstruct gag *gag_list;
Xstruct gag *gag_list_srch();
X
Xstruct txt_mac *mac_list;
Xstruct txt_mac *mac_list_srch();
X
Xextern struct cmd builtins[];
X
X/*******************************************************************
X
X This routine initializes all the filters. Not real complex.
X
X********************************************************************/
X
Xinit_filters()
X
X	{
X	gag_list = NULL;
X	mac_list = NULL;
X	}
X
X/*************************************************************************
X
XThis filters the user's input. returns either 2 -- the interface program
Xshould quit, 1 -- cooked data should be sent to TinyMud or 0 -- ignore cooked
Xdata
X
X**************************************************************************/
X
Xfilter_input(in_buff,out_buff,size)
X
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	int i,done;
X	char *cmd;
X
X	/* skip spaces */
X
X	in_buff = skipspaces(in_buff);
X
X	/* Check for starting with \, but not \\ */
X
X	if( (in_buff[0] == '\\') && (in_buff[1] != '\\'))
X		{
X		/* Find the command */
X
X		cmd = in_buff+1;
X
X		/* command name may end with white space or end of string */
X
X		for(; !(isspace(*in_buff)) && (*in_buff != '\0');)
X			in_buff++;
X
X		if( *in_buff != '\0') /* if we didn't fall of the end */
X			*in_buff++ = '\0';
X
X		for(i=0 ; builtins[i].name != NULL ; i++)
X			{
X			if(strcmp(cmd,builtins[i].name) == 0)
X				break;
X			}
X
X		if(builtins[i].name != NULL)
X			{
X			return(builtins[i].handler(in_buff,out_buff,size));
X			}
X		}
X	else
X		{
X	/* This is not a command, so filter it into the output buffer */
X
X		/* If we started with a \\, then skip the first one */
X
X		if(in_buff[0] == '\\') in_buff++;
X
X		size--; /* leave one space for a terminator */
X		done = FALSE;
X		for(;!done;)
X			{
X			/* copy text until you hit a macro */
X
X			if(*in_buff != '\\')
X				{
X				*out_buff++ = *in_buff++;
X				size--;
X				done = (size <= 0) || (*in_buff == '\0');
X				}
X			else if((in_buff[0] == '\\') && (in_buff[1] == '\\'))
X				{
X				/* copy out a single / char */
X				in_buff++;
X				*out_buff++ = '\\';
X				size--;
X				done = (size <= 0);
X				}
X			else  /* attempt to expand the macro */
X				{
X				/* point to the macro's name */
X
X				in_buff++;
X
X				in_buff = expand_macro(in_buff,&out_buff,
X					&size);
X				done = (*in_buff == '\0') || (size <= 0);
X				}
X			}
X		*out_buff = '\0';
X		return(1);
X		}
X	}
X
X
X/*********************************************************************
X
X	Macro expansion routine. This receives a pointer to the macro name
X(in), a pointer to a pointer to an output buffer, and a pointer to an integer
Xindicating how much space there remains in the output buffer.
X
X	It expands the macro into the output buffer, adjusts the pointer to the
Xoutput buffer to point *after* the expanded macro, adjusts the integer to
Xreflect space taken up by the expanded macro, and returns a pointer to the
Xfirst character *after* the macro name (in the input buffer)
X
X*********************************************************************/
X
Xchar *expand_macro(in,out,siz)
X
Xchar *in;
Xchar **out;
Xint *siz;
X
X	{
X	char ch,*mac_name,*parm_list,*val,*parm;
X	int i,done,parm_num,sts;
X	struct txt_mac *tmp;
X
X	mac_name = in;
X
X	/* skip ahead to end of macro name, it will end with white space, the
X		end of the buffer or a '(' (start of parameter list) */
X
X	for(;!(isspace(*in)) && (*in != '\0') && (*in != '(');)
X		in++;
X
X	/* If we terminated at '(', we have a parameter list, deal with it */
X
X	if( *in == '(' )
X		{
X		/* terminate macro name */
X		*in++ = '\0';
X
X		parm_list = in;
X
X		/* go ahead to you hit the matching ')' */
X
X		for(; (*in != ')') && (*in != '\0') ;) in++;
X
X		/* If we fell of the end, error */
X
X		if( *in == '\0')
X			{
X			message("Parameter list error, macro flushed");
X			return(in);
X			}
X		else  /* otherwise, terminate the parameter list */
X			{
X			*in++ = '\0';
X			}
X		ch = ' ';
X		}
X	else if(*in != '\0')  /* if we didn't reach the end of input, do this */
X		{
X		/* in points to a white space char, which we want */
X		ch = *in;
X		*in++ = '\0';
X		}
X
X	/* mac_name points to the macro name. have we such a macro? */
X
X	if((tmp = mac_list_srch(mac_name)) != NULL)
X 		{
X		/* If we've got it, expand it */
X
X		done = FALSE;
X		val = tmp->value;
X		for(;!done;)
X			{
X			/* copy characters from val to *out */
X			for(; (*val != '#') && (*val != '\0') && (*val != '%');)
X				{
X				*(*out)++ = *val++;
X				(*siz)--;
X				if( *siz <= 0) return(in);
X				}
X
X			if( *val == '\0' )
X				{
X				done = TRUE;
X				}
X			else if (*val == '%')   /* Embedded CR ?*/
X				{
X				val++;
X				if( *val == '%' )   /* No, literal % */
X					{
X					val++;
X					if( *siz < 2 ) return(in);
X					*(*out)++ = '%';
X					(*siz)--;
X					}
X				else
X					{
X					if( *siz < 3 ) return(in);
X					*(*out)++ = '\015';   /* CR */
X					*(*out)++ = '\012';   /* LF */
X					(*siz) -= 2;
X					}
X				}
X			else	/* deal with a parameter */
X				{
X				val++;
X				if( isdigit(*val) )
X					{
X					parm_num = (*val) - '0';
X					val++; /* skip past digit */
X
X					/* get the parameter */
X
X					parm = parm_list;
X					sts = OK;
X					for(; (parm_num > 1) ; parm_num--)
X						{
X						for(;(*parm!=',')
X							&&(*parm != '\0');)
X							parm++;
X
X					/* If we fall off the end, error */
X
X						if(*parm == '\0')
X							{
X							sts=ERROR;
X							break;
X							}
X						else   /* skip the comma */
X							parm++; 
X						}
X					/* parm should point at parameter */
X					/* if so, copy it out */
X
X					if(sts != ERROR)
X						{
X						for(;(*parm != ',')
X							&& (*parm!= '\0');)
X							{
X							*(*out)++ = *parm++;
X							(*siz)--;
X							if( *siz <= 0)
X								return(in);
X							}
X						}
X					}
X				else		/* Not really a parameter */
X					{
X					/* kludge so you can enter ##n */
X					/* and get a #n. Convert all ##'s */
X					/* to a single #.		*/
X
X					if(*val == '#')
X						val++;
X					*(*out)++ = '#';
X					(*siz)--;
X					if(siz <= 0) return(in);
X					}
X				}
X			}
X		/* copy our last little character out */
X
X		(**out) = ch;
X		(*siz)--;
X		(*out)++;
X		}
X	else
X		{
X		message("Unknown macro flushed");
X		}
X
X	return(in);
X	}
X
X/*  routine to search the macro list */
X
Xstruct txt_mac *mac_list_srch(name)
X
Xchar *name;
X
X	{
X	struct txt_mac *tmp;
X	int found;
X
X	tmp = mac_list;
X	if( tmp == NULL ) return(NULL);
X
X	found = FALSE;
X	do
X		{
X		if( strcmp(tmp->name , name) == 0)
X			{
X			found = TRUE;
X			break;
X			}
X		tmp = tmp->fwd;
X		}
X	while(tmp != mac_list);
X	if(found)
X		return(tmp);
X	else
X		return(NULL);
X	}
X/* routine to search the gag list */
X
Xstruct gag *gag_list_srch(name)
X
Xchar *name;
X
X	{
X	struct gag *tmp;
X	int found;
X
X	tmp = gag_list;
X	if( tmp == NULL ) return(NULL);
X
X	found = FALSE;
X	do
X		{
X		if( strcmp(tmp->name , name) == 0)
X			{
X			found = TRUE;
X			break;
X			}
X		tmp = tmp->fwd;
X		}
X	while(tmp != gag_list);
X	if(found)
X		return(tmp);
X	else
X		return(NULL);
X	}
X
X/***********************************************************************
X
XThis filters the output from TinyMud, implementing gags and whatnot.
XI gues thats just gags, at the moment.
XIt returns either 1 (indicating ``display cooked data'') or 0 (indicating
Xthat this stuff should be ignored
X
X************************************************************************/
X
Xfilter_output(in_buff,out_buff,size)
X
Xchar *in_buff,*out_buff;
Xint size;
X
X	{
X	char *first_word;
X	char ch;
X
X	/* extract the first word in ``in_buff'' */
X
X	first_word = in_buff;
X	for(; (in_buff != '\0') && !(isspace(*in_buff)) ;) in_buff++;
X	ch = *in_buff;
X	*in_buff++ = '\0';
X
X	if( gag_list_srch(first_word) == NULL)
X		{
X		/* no gag in effect, so get this stuff into out_buff */
X		/* we know ``first_word'' will fit, since it fit in in_buff */
X
X		for(;*first_word != '\0';)
X			*out_buff++ = *first_word++;
X		*out_buff++ = ch;
X		if( ch == '\0' ) return(1);
X
X		/* the rest gets copied below */
X		}
X	else
X		{
X		/* gag in effect */
X		return(0);
X		}
X
X	for(; *in_buff != '\0' ;)
X		*out_buff++ = *in_buff++;
X	return(1);
X	}
X
X/********************************************************************
X
X	String routines. Only one so far.
X
X********************************************************************/
X
Xchar *skipspaces(str)
X
Xchar *str;
X
X	{
X	for(; isspace(*str) && (*str != '\0') ;) str++;
X	return(str);
X	}
X
XSHARC_EOF_MARK
X$ write sys$output "TINT_NETC.C"
X$ copy sys$input TINT_NETC.C
X$ deck /dollars="SHARC_EOF_MARK"
X#include <iodef.h>
X#include <stdio.h>
X
X#include "multinet_root:[multinet.include.sys]types.h"
X#include "multinet_root:[multinet.include.sys]socket.h"
X#include "multinet_root:[multinet.include.netinet]in.h"
X#include "multinet_root:[multinet.include]netdb.h"
X
X#include "tint.h"
X
X/* Given host and port, makes a connection */
X/*    Returns 0 on failure, else channel connected to */
Xunsigned short
Xnetconnect (host, port)
X    char *host, *port;
X{
X    struct sockaddr_in sin;
X    struct hostent *hp=0;
X    struct servent *sp=0;
X    char localhost[34];
X    unsigned short int chan;
X
X    printf("Trying...");
X
X    /* Zero the sin structure to initialize it */
X    bzero((char *) &sin, sizeof(sin));
X    sin.sin_family = AF_INET;
X
X    /* Lookup the host and initialize sin_addr */
X    /* If no host specified, use local address */
X    if ((host == 0) || (host[0] == '\0')) {
X        if (gethostname(localhost,sizeof(localhost))<0) {
X            fprintf(stderr,"Unable to find localhost.\n");
X            return(0);
X        }            
X        hp = gethostbyname(localhost);
X    } else {
X        hp = gethostbyname(host);
X    }
X    /* Host not name, try treating it as octets */
X    if (!hp) {      
X        if ((sin.sin_addr.s_addr = inet_addr(host)) == -1) {
X            fprintf(stderr,"Unknown internet host.\n");
X            return(0);
X        }
X    } else {
X        bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
X    }
X
X    /* Lookup the port */
X    if ((port == 0) || (port[0] == '\0')) {
X        sp = getservbyname("telnet", "tcp");        /* default */
X    } else {
X        sp = getservbyname(port, "tcp");            /* by name */
X    }       
X    if (sp == NULL) {                               /* maybe by number? */
X        unsigned short nport;
X        
X        nport = atol(port);
X        if (nport == 0) {
X            fprintf(stderr,"Unknown port.\n");
X            return(0);
X        }
X        sin.sin_port = htons(nport);
X    } else {   
X        sin.sin_port = sp->s_port;
X    }
X
X    /* Assign socket */
X    chan = socket(sin.sin_family, SOCK_STREAM, 0);
X    if (chan < 0) {
X        socket_perror("socket");
X        return(0);
X    }
X
X    /* Connect */
X    if (connect(chan, &sin, sizeof(sin),0) < 0) {
X        socket_perror("connect");
X        return(0);
X    }
X
X    printf("Connected\n");
X    
X/*    Report some information about the connection
X**  if (hp == NULL) hp = gethostbysockaddr(&sin, sizeof(sin), AF_INET);
X**  if (hp != NULL && hp->h_name != NULL) {
X**      printf("[%s, %s]\n",hp->h_name,inet_ntoa(sin.sin_addr));
X**  }
X*/
X
X    return(chan);
X}
XSHARC_EOF_MARK
END_OF_FILE
if test 37328 -ne `wc -c <'tint_shar.dcl'`; then
    echo shar: \"'tint_shar.dcl'\" unpacked with wrong size!
fi
# end of 'tint_shar.dcl'
fi
if test ! -d 'tinytalk' ; then
    echo shar: Creating directory \"'tinytalk'\"
    mkdir 'tinytalk'
fi
if test -f 'tinytalk/keyboard.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tinytalk/keyboard.c'\"
else
echo shar: Extracting \"'tinytalk/keyboard.c'\" \(11688 characters\)
sed "s/^X//" >'tinytalk/keyboard.c' <<'END_OF_FILE'
X/************************************************************************/
X/* TinyTalk keyboard handler.						*/
X/*									*/
X/*	Version 1.0 [ 1/24/90] : Initial implementation by ABR.		*/
X/*		1.1 [ 1/25/90] : Added minimal termcap support.		*/
X/*		1.2 [ 1/26/90] : Clean up terminal state on ^C.		*/
X/*		1.3 [ 2/ 2/90] : Allow interrupts to be ignored.	*/
X/*				 Optionally use stty information.	*/
X/*		1.4 [ 2/ 5/90] : Add minimal command recall.  Flush log	*/
X/*				 file on suspend.  Fix problem with gag	*/
X/*				 causing spurious input erasure.	*/
X/*		1.5 [ 2/16/90] : Integrated support for System V and	*/
X/*				 HP-UX, from Andy Norman and Kipp	*/
X/*				 Hickman.				*/
X/*		1.6 [ 6/25/90] : Fixed a bug in /logme; thanks to	*/
X/*				 Jean-Remy Facq for reporting it and	*/
X/*				 contributing a solution.  Also fixed a	*/
X/*				 bug in recall of lines starting with	*/
X/*				 //; thanks to "Insane Hermit" for	*/
X/*				 reporting this (a LONG time ago).	*/
X/*									*/
X/************************************************************************/
X
X#include "tl.h"
X#include <signal.h>
X
X#ifdef HPUX
X
X#include <sys/ioctl.h>
X#include <termio.h>
X#include <bsdtty.h>
X
X#else /* not HPUX */
X#ifdef SYSVTTY
X
X#include <termio.h>
X
X#else /* BSD */
X
X#include <sys/ioctl.h>
X#include <sgtty.h>
X
X#endif /* SYSVTTY */
X#endif /* HPUX */
X
X#include <errno.h>
X#include <stdio.h>
X
X  /* For some odd systems, which don't put this in errno.h. */
X
Xextern int errno;
X
X#ifdef TERMCAP
Xextern char *getenv();
Xstatic int have_clear;
Xstatic char start_of_line[16], clear_to_eol[16];  /* Warning, short! */
Xstatic char write_buffer[64];			  /* Also rather short.... */
Xstatic char *write_buffer_ptr;
X#endif
X
X#define DEFAULT_COLUMNS 80
X
Xstatic int keyboard_inited = FALSE;	/* Initialized yet? */
X
Xstatic int num_columns;			/* Number of columns on terminal. */
Xstatic int current_column;		/* Current input column (0->nc). */
Xstatic string keyboard_buffer;		/* Buffer for current input. */
Xstatic string recall_buffer;		/* Buffer for previous input. */
Xstatic int keyboard_pos;		/* Characters entered so far. */
Xstatic int recall_pos;			/* Characters on previous input. */
Xstatic int allow_intr;			/* Should we quit on INTR? */
Xstatic int recall_pending;		/* Flag: recall previous string. */
Xstatic int do_not_save_recall;		/* Flag: don't save recall buffer. */
X
Xstatic char k_delete, k_delete_2;	/* Backspace character(s). */
Xstatic char k_erase, k_erase_2;		/* Erase-line character(s). */
Xstatic char k_word;			/* Erase-word character. */
Xstatic char k_refresh;			/* Refresh-line character. */
X
Xsigint_handler()			/* Restore terminal state; cleanup. */
X{
X  if (allow_intr)			/* Don't exit if this is disabled. */
X    die("\n\nInterrupt, exiting.\n");	/* Exit through error handler. */
X}
X
Xsigtstp_handler()			/* Leave terminal alone! */
X{
X  flush_logfile();
X
X  cooked_echo_mode();
X  kill(getpid(),SIGSTOP);
X  cbreak_noecho_mode();
X
X  if (keyboard_pos != 0)		/* If there is input, we want to */
X    set_refresh_pending();		/* refresh when we start back up. */
X}
X
Xpreinit_keyboard()
X{
X  allow_interrupts();
X  no_use_stty();
X
X  do_not_save_recall = FALSE;
X  recall_pending = FALSE;
X  recall_pos = 0;
X}
X
Xallow_interrupts()
X{
X  allow_intr = TRUE;
X}
X
Xdisallow_interrupts()
X{
X  allow_intr = FALSE;
X}
X
X#ifdef HPUX
X
Xuse_stty()
X{
X  struct termio te_blob;
X  struct ltchars lt_blob;
X
X  if (ioctl(0, TCGETA, &te_blob) == -1)
X    perror("TCGETA ioctl");
X
X  if (ioctl(0, TIOCGLTC, &lt_blob) == -1)
X    perror("TIOCGLTC ioctl");
X
X  k_delete = k_delete_2 = te_blob.c_cc[VERASE];
X  k_erase = k_erase_2 = te_blob.c_cc[VKILL];
X
X  k_word = lt_blob.t_werasc;
X  k_refresh = lt_blob.t_rprntc;
X}
X
X#else /* not HPUX */
X#ifdef SYSVTTY
X
Xuse_stty()
X{
X  struct termio tcblob;
X
X  if (ioctl(0, TCGETA, &tcblob) == -1)
X    perror("TCGETA ioctl");
X
X  k_delete = k_delete_2 = tcblob.c_cc[VERASE];
X  k_erase = k_erase_2 = tcblob.c_cc[VKILL];
X
X#ifdef sgi
X
X  k_word = tcblob.c_cc[VWERASE];
X  k_refresh = tcblob.c_cc[VRPRNT];
X
X#else
X
X  k_word = '\027';			/* Control-W. */
X  k_refresh = '\022';			/* Control-R. */
X
X#endif /* sgi */
X
X}
X
X#else /* BSD */
X
Xuse_stty()
X{
X  struct sgttyb sg_blob;
X  struct ltchars lt_blob;
X
X  if (ioctl(0, TIOCGETP, &sg_blob) == -1)
X    perror("TIOCGETP ioctl");
X  if (ioctl(0, TIOCGLTC, &lt_blob) == -1)
X    perror("TIOCGLTC ioctl");
X
X  k_delete = k_delete_2 = sg_blob.sg_erase;
X  k_erase = k_erase_2 = sg_blob.sg_kill;
X
X  k_word = lt_blob.t_werasc;
X  k_refresh = lt_blob.t_rprntc;
X}
X
X#endif /* SYSVTTY */
X#endif /* HPUX */
X
Xno_use_stty()
X{
X  k_delete = '\010';			/* Backspace. */
X  k_delete_2 = '\177';			/* Delete. */
X
X  k_erase = '\025';			/* Control-U. */
X  k_erase_2 = '\030';			/* Control-X. */
X
X  k_word = '\027';			/* Control-W. */
X
X  k_refresh = '\022';			/* Control-R. */
X}
X
Xinit_keyboard()
X{
X#ifdef TIOCGWINSZ
X  struct winsize size;
X#endif
X#ifdef HPUX
X  struct sigvec hpvec;
X#endif
X
X  cbreak_noecho_mode();
X
X#ifdef TERMCAP
X  get_termcap_info();			/* Must precede TIOCGWINSZ code */
X#else
X  num_columns = DEFAULT_COLUMNS;	/* This is important! */
X#endif
X
X#ifdef TIOCGWINSZ
X
X  if (ioctl(0, TIOCGWINSZ, &size) == -1)
X    perror("TIOCGWINSZ ioctl");
X
X  num_columns = size.ws_col;
X  if (num_columns <= 20)		/* Suspicious, reset it. */
X    num_columns = DEFAULT_COLUMNS;
X
X#endif /* TIOCGWINSZ */
X
X  set_default_wrap(num_columns);
X
X  keyboard_pos = 0;			/* No input yet. */
X  current_column = 0;			/* Left side of screen. */
X
X#ifdef HPUX
X
X  hpvec.sv_handler = (void (*) ()) sigint_handler;
X  hpvec.sv_onstack = 0;
X  hpvec.sv_mask = 0;
X  sigvector(SIGINT,&hpvec,0);
X  hpvec.sv_handler = (void (*) ()) sigtstp_handler;
X  sigvector(SIGTSTP,&hpvec,0);
X
X#else /* BSD */
X
X  signal(SIGINT, sigint_handler);	/* Control-C. */
X  signal(SIGTSTP, sigtstp_handler);	/* Control-Z. */
X
X#endif
X
X  keyboard_inited = TRUE;
X}
X
X#ifdef TERMCAP
X
Xget_termcap_info()
X{
X  char blob[1024];			/* As per termcap man page */
X  char *terminal_name, *temp;
X
X  terminal_name = getenv("TERM");
X  if (terminal_name == NULL) {
X    have_clear = FALSE;
X    return;
X  }
X
X  if (tgetent(blob, terminal_name) == 1) {
X    num_columns = tgetnum("co");	/* Try to get # of columns. */
X    if (num_columns == -1)		/* Not specified? */
X      num_columns = DEFAULT_COLUMNS;
X
X    temp = start_of_line; 
X    if (tgetstr("cr",&temp) == NULL) {
X      start_of_line[0] = '\r';
X      start_of_line[1] = '\0';
X    }
X    temp = clear_to_eol;
X    if (tgetstr("ce",&temp) == NULL)
X      have_clear = FALSE;
X    else
X      have_clear = TRUE;
X  }
X  else					/* Couldn't read termcap entry */
X    have_clear = FALSE;
X}
X
X#endif
X
Xcleanup_keyboard()
X{
X  cooked_echo_mode();
X  signal(SIGTSTP, NULL);
X}
X
Xhandle_keyboard_input()			/* Read input, one char at a time. */
X{
X  char ch_noreg;
X  register char ch;
X  int count;
X
X  count = read(0, &ch_noreg, 1);	/* Read from stdin. */
X  if (count == 0)			/* Huh?  Ignore this. */
X    return;
X
X  if (count == -1) {
X    if (errno == EWOULDBLOCK)
X      return;
X    else
X      die("%% Couldn't read keyboard.\n");
X  }
X
X  ch = ch_noreg;			/* For stupid compilers. */
X
X  if (ch == '\n') {
X    write(1, &ch_noreg, 1);
X    process_buffer();
X    if (recall_pending) {
X      recall_pending = FALSE;
X
X      keyboard_pos = recall_pos;
X      bcopy(recall_buffer, keyboard_buffer, recall_pos); /* Retrieve buffer. */
X      current_column = keyboard_pos % num_columns;
X
X      refresh_entire_buffer();
X    }
X    else {
X      keyboard_pos = 0;
X      current_column = 0;
X    }
X  }
X  else {
X    if ((ch == k_delete) || (ch == k_delete_2)) /* Backspace. */
X      handle_backspace();
X    else if ((ch == k_erase) || (ch == k_erase_2)) { /* Cancel line. */
X      keyboard_pos = 0;
X
X      erase_keyboard_input(TRUE);
X      clear_refresh_pending();
X
X      current_column = 0;
X    }
X    else if (ch == k_word) {		/* Erase word (back to space). */
X					/* This is very, very, VERY ugly. */
X
X      handle_backspace();		/* Always delete at least one char. */
X
X      while ((keyboard_pos != 0) && (keyboard_buffer[keyboard_pos-1] != ' '))
X	handle_backspace();
X    }
X    else if (ch == k_refresh) {		/* Refresh entire input. */
X      refresh_entire_buffer();
X    }
X    else if ((ch < ' ') || (keyboard_pos == MAXSTRLEN - 1)) { /* Beep! */
X      write(1, "\007", 1);
X    }
X    else {
X      keyboard_buffer[keyboard_pos++] = ch;
X      current_column = (current_column + 1) % num_columns;
X      write(1, &ch_noreg, 1);
X    }
X  }
X}
X
Xhandle_backspace()			/* Do a backspace.... */
X{
X  if (keyboard_pos != 0) {
X    keyboard_pos--;
X
X    if (current_column != 0) {		/* Can do BS, SPC, BS. */
X      write(1, "\010 \010", 3);
X      current_column--;
X    }
X    else {
X      current_column = num_columns - 1;	/* Wrapped around. */
X      do_refresh();			/* Will redraw the right part. */
X    }
X  }
X}
X
Xprocess_buffer()
X{
X  register int kp = keyboard_pos;	/* Help the compiler optimize.	   */
X					/* keyboard_pos is static, and	   */
X					/* the compiler doesn't know if	   */
X					/* transmit() or log_input() might */
X					/* change it (they don't).	   */
X
X  if (kp == 0)				/* Nothing typed. */
X    return;
X
X  if (keyboard_buffer[0] == '/') {
X    if (keyboard_buffer[1] == '/') {	/* Two slashes, pass a single one. */
X      keyboard_buffer[kp] = '\n';
X      transmit(keyboard_buffer + 1, kp);
X      keyboard_buffer[kp] = '\0';
X      log_input(keyboard_buffer + 1);
X    }
X    else {				/* Single slash, it's a command. */
X      keyboard_buffer[kp] = '\0';
X      handle_command(keyboard_buffer, FALSE);
X      log_input(keyboard_buffer);	/* Should this be logged? */
X    }
X  }
X  else {
X    keyboard_buffer[kp] = '\n';
X    transmit(keyboard_buffer, kp + 1);
X    keyboard_buffer[kp] = '\0';
X    log_input(keyboard_buffer);
X  }
X
X  if (!do_not_save_recall) {		/* Don't destroy buffer on /RECALL! */
X    recall_pos = kp;
X    bcopy(keyboard_buffer, recall_buffer, kp); /* Save it away. */
X  }
X  else
X    do_not_save_recall = FALSE;
X}
X
Xrefresh_entire_buffer()			/* Refresh all lines of buffer. */
X{
X  register int i;
X
X  erase_keyboard_input(TRUE);		/* Start at beginning of line. */
X					/* Dump full lines. */
X  for (i = 0; i <= keyboard_pos - num_columns; i += num_columns) {
X    write(1, keyboard_buffer+i, num_columns);
X    write(1, "\n", 1);
X  }
X
X  do_refresh();			/* Handle possibly partial tail. */
X}
X
Xdo_refresh()
X{
X  int loc;				/* Place to start refresh from */
X  int modulo;
X
X  modulo = keyboard_pos % num_columns;	/* Number on last line */
X  loc = keyboard_pos - modulo;
X
X  if (modulo != 0)
X    write(1, &keyboard_buffer[loc], modulo);
X
X  clear_refresh_pending();
X}
X
X#ifdef TERMCAP
Xwrite_one(c)
X  char c;
X{
X  *(write_buffer_ptr++) = c;
X}
X#endif
X
Xerase_keyboard_input(forced)
X  int forced;				/* Forced output? */
X{
X  if ((!forced) && (current_column == 0))
X    return;
X
X#ifdef TERMCAP
X
X  if (have_clear) {			/* Can we use termcap now? */
X    write_buffer_ptr = write_buffer;
X    tputs(start_of_line, 1, write_one);
X    tputs(clear_to_eol, 1, write_one);
X    write(1, write_buffer, write_buffer_ptr - write_buffer);
X    goto end_termcap;
X  }
X
X  /* Drops through into non-TERMCAP code otherwise... */
X
X#endif
X
X  {
X    register int temp;
X    string buffer;			/* Should be long enough for 80*3. */
X    register char *ptr;
X
X    temp = current_column;
X    ptr = buffer;
X
X    while (temp != 0) {			/* Erase line, backwards. */
X      *(ptr++) = '\010';
X      *(ptr++) = ' ';
X      *(ptr++) = '\010';
X      temp--;
X    }
X    write(1, buffer, ptr - buffer);
X  }
X
Xend_termcap:
X
X  if (keyboard_pos != 0)		/* If there is input, schedule a */
X    set_refresh_pending();		/* refresh event. */
X
X}
X
Xavoid_recall()
X{
X  do_not_save_recall = TRUE;
X}
X
Xdo_keyboard_recall()
X{
X  if (!keyboard_inited)			/* Dummy, nothing to recall yet! */
X    return;
X
X  recall_pending = TRUE;
X  do_not_save_recall = TRUE;
X}
END_OF_FILE
if test 11688 -ne `wc -c <'tinytalk/keyboard.c'`; then
    echo shar: \"'tinytalk/keyboard.c'\" unpacked with wrong size!
fi
# end of 'tinytalk/keyboard.c'
fi
if test -f 'tinytalk/tl.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tinytalk/tl.h'\"
else
echo shar: Extracting \"'tinytalk/tl.h'\" \(975 characters\)
sed "s/^X//" >'tinytalk/tl.h' <<'END_OF_FILE'
X/************************************************************************/
X/* TinyTalk global types and variables.					*/
X/*									*/
X/*	Version 1.0 [ 1/24/90] : Initial implementation by ABR.		*/
X/*              1.1 [ 1/24/90] : Longer strings allowed.		*/
X/*		1.2 [ 2/16/90] : Integrated support for System V and    */
X/*				 HP-UX, from Andy Norman and Kipp       */
X/*				 Hickman.				*/
X/*									*/
X/************************************************************************/
X
X#define TRUE 1
X#define FALSE 0
X
X#define MAXSTRLEN 511
X#define HUGESTRLEN (2*MAXSTRLEN)
X#define SMALLSTR 31
X
Xtypedef char string[MAXSTRLEN+1];
Xtypedef char hugestr[HUGESTRLEN+1];
Xtypedef char smallstr[SMALLSTR+1];
X
X  /* A TinyMUD world/character record. */
X
Xtypedef struct world_rec {
X  struct world_rec *next;
X  smallstr world, character, pass, address, port;
X} world_rec;
X
X#ifdef HPUX
X
X#define rindex strrchr
X#define index strchr
X#define bcopy(a,b,c) memcpy((b),(a),(c))
X
X#endif /* HPUX */
END_OF_FILE
if test 975 -ne `wc -c <'tinytalk/tl.h'`; then
    echo shar: \"'tinytalk/tl.h'\" unpacked with wrong size!
fi
# end of 'tinytalk/tl.h'
fi
echo shar: End of archive 1 \(of 3\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0