/*************************************************************************
   .------------------------------------------------------------------.
   | CNetMultiPort v1.0 COPYRIGHT © 1995 METAL SOFTWARE AKA MetalSoft |
   `------------------------------------------------------------------'
**************************************************************************/

#include <exec/ports.h>
#include <exec/nodes.h>
#include <exec/exec.h>
#include <ctype.h>
#include <time.h>
#include <devices/timer.h>
#include <libraries/easyrexx.h>
#include "CNMPConf.h"
#include "CNMPConfFuncs.h"
#include <cnmp/cnmp.h>
#include <clib/cnmp_protos.h>

/**************************************************************************
 * REQUIRED CNetMultiPort global variables
 **************************************************************************/
UBYTE *vers="$VER:v1.01 CNetMultiPort Test (26-MAY-95)";

struct	CnmpCluster		*MyCnmpCluster=NULL;		/* the meat of CNMP! - see
                                                   CNMP.H for structure
                                                   members */

BOOL							PORTPERMISSION=TRUE;		/* portpermission defaults to FALSE */
BOOL							IgnoreMsgs=FALSE;			/* set to force CNMP NOT to save messages */
//char							MyCNMPortName[30];		/* MUST NOT CHANGE ARRAY SIZE OF 30! */



/**************************************************************************
 * CNMP ARexx support! - SEE CNMP_AREXX.DOC for more info on
 * ALTERING/ADDING/REMOVING/HANDLING AREXX COMMANDS.
 **************************************************************************/
char							RexxPortName[40];			/* must be at least CNMP_BASEPORT + 10 */
struct	Library			*EasyRexxBase=NULL;
struct	ARexxContext	*RexxContext=NULL;

/* define a context number for each ARexx operation you need */
#define AREXX_QUIT      1	/* force the port to exit - does NOT hang up user */
#define AREXX_TEXTECHO	2	/* this forces a CNMP_TEXTECHO */
#define AREXX_VERSION   3  /* This forces the port to display it's version number */
#define AREXX_INC_TV    4  /* This forces the port to increment it's .testvar variable */
#define AREXX_SHOW_TV   5 	/* This forces the port to display .testvar */
#define AREXX_QUERY_TV  6 	/* This forces the port to return the value of .testvar to the calling arexx script */

struct ARexxCommandTable rxtbl[] =
	{
	AREXX_QUIT,			"CNMPQUIT",		NULL,			NULL,
	AREXX_TEXTECHO,	"CNMPECHO","STRING/F",	NULL,
	AREXX_VERSION,		"CNMPVER",	NULL,			NULL,
	AREXX_INC_TV,		"CNMPVARINC",	"AMOUNT/N",	NULL,
	AREXX_QUERY_TV,	"CNMPVARQUERY",	NULL,			NULL,
	AREXX_SHOW_TV,		"CNMPVARSHOW",	NULL,			NULL,
	TABLE_END,
	};
/**************************************************************************
 * END OF AREXX IMPLEMENTATION - SEE CNMP_AREXX.DOC for more info on
 * ALTERING/ADDING/REMOVING/HANDLING AREXX COMMANDS.
 **************************************************************************/




/**************************************************************************
 * variables added for this test program...
 **************************************************************************/
UBYTE							*oldDOING;
char							MyFgColor='6';				/* default MCI color number for actual conference text */
char							MyBgColor='0';				/* default MCI color number for actual conference text */
struct	ChannelList		*MyChannelList=NULL;

/************************************************************************
 *                         CNet Global Symbols                          *
 ************************************************************************/
struct	MsgPort  *replyp;
struct	CPort    *cport;
struct	CMessage  cmess;
struct	MainPort  *myp;
struct	PortData  *z;
char		**bm;
struct	Library *CNetBase = NULL;
struct	SignalSemaphore *SEM;


/************************************************************************
 *                         Entry/Start routine                          *
 ************************************************************************/
void main( int argc, char **argv )
{
	BOOL done=FALSE;
	char PortRange[50];
	struct RemotePort *TempRemotePort=NULL;
	struct CNMPMsg *UserCNMPMsg=NULL;
//	UBYTE *MsgString=NULL;
	LONG MsgMask=0;
	char OtherPortName[20];


	/* ----- Cnet comm stuff ----- */
	if( argc<2 || !(cport = (struct CPort *)FindPort( argv[1] )) )
		{
		printf("This is a CNet C program.\n");
		exit(0);
		};
	if( !(replyp = CreatePort( 0,0 )))
		{
		exit(0);
		};
	cmess.cn_Message.mn_ReplyPort   = replyp;
	cmess.cn_Message.mn_Length      = sizeof( struct CMessage );
	cmess.cn_Message.mn_Node.ln_Name= "cstuff";
	if( cport->ack != 30 )
		{
		cport->ack = 1;
		LoadError();
		};
	cport->ack	=	0;
	z				=	cport->zp;
	myp			=	cport->myp;
	SEM			=	myp->SEM;
	bm				=	z->bm;
	if( !(CNetBase = OpenLibrary( "cnet.library",0 )) )
		GetOut("", 0);



	/**************************************************************************
	 * CNMP(tm) initialization begins here!                                   *
	 *     ***** DO NOT CHANGE ANYTHING IN THIS SECTION OF CODE!! *****       *
	 * See below for details!                                                 *
	 **************************************************************************/

	PutText("Starting CNMPg1..\n");

	if( ! (MyCnmpCluster=InitCNMP(z, "CNMPConf", sizeof(struct PortVars))) )
		{
		GetOut("", 0);
		}

	PutText("Point 1g1..\n");

	/* Create the EasyRexx port */
	if(EasyRexxBase=OpenLibrary(EASYREXXNAME, EASYREXXVERSION))
		{
		/* set the arexx port name to CNMPTESTREXXn where n=port number */
		sprintf(RexxPortName, "%sREXX%d", MyCnmpCluster->CNMP_BASEPORT, z->InPort);
		RexxContext = AllocARexxContext(ER_Portname, RexxPortName,
 	                                   ER_CommandTable, rxtbl,
 	                                   TAG_DONE) ;
		}
	else
		{
		PutText("ERROR 504: arexx port not opened!\n");
		}

	PutText("Point 2g1..\n");

/**************************************************************************
 *** end of CNMP initalization. DO NOT CHANGE ANY CODE ABOVE THIS POINT ***
 **************************************************************************/


	/* User code begins here... */

	/* Set the user's "Where" field to whatever you want. */
	/* CNMPTest sets it to "CNMPTESTn" where n=user's port number */
	oldDOING = z->Doing;
//	SetDoing(MyCNMPortName);

	/* Print a title/intro header - change this to whatever you need */
	PutText("f1\nMMC v1.0 (Metal Mini-Conference) - A CNetMultiPort(tm) Test\n");

	/* Bragline.  DO NOT REMOVE! See "Legal Stuff" for info */
	PutText("Coded using CNetMultiPort v1.0 - COPYRIGHT © 1995 MetalSoft\n\n");



	/* create channel list */
//	if(!(MyChannelList = MakeChannelList()) )
//		{
//		GetOut("ERROR 510: Could not create channel list\n", 0);
//		}


	/* Main event loop */
	/* flush keyboard buffer */
	KBFlush(z);
	while(!done)
		{
		MsgMask = HandleIO(ER_SIGNAL(RexxContext), MyCnmpCluster );
		CheckFlowControl();

		if(!CarrierCheck(z))
			{
			GetOut("", 0);
			}

		/*
		 * Check message port events first - they have priority because
		 * they must be processed ASAP!  (ie. the remote port may not be
       * present/available later.  This is of no consequence code-wise
       * but the port may not receive your reply message (if you send
       * one) later.
		 *
		 * ADDPORT events are user notifiable/permissible and
       * if you wish to be notified of a port's intent to
       * enter and have control over it set PORTPERMISSION=1 or
       * PORTPERMISSION=TRUE.
       * This define causes CNMP(tm) to send a CNMP_ENTRYCONFIRM to
       * all available CNMP(tm) ports on entry.
       *
       * Sending a CNMP_ENTRYPERMIT in reply will allow the remote port to
       * converse with the current port and share data between them.
		 * the port will only be allowed to converse with ports that have
       * sent CNMP_ENTRYPERMIT in reply.  All other ports will act as
       * if that port does not exist and those ports will not be seen
       * by the port that requested permission.
		 *
		 * It is possible to alter the CNMP_ADDPORT message check to function
       * in this manner also..  All you need do is do your permission check
       * before you call CNMPortToList().
		 *
		 * When the you are finished with a message, you
		 * should call DisposeCNMPMessage(Copy_of_Msg) to let
       * us take care of freeing and disposal of it.
		 * messages that are not "disposed" will pile up and
       * eat memory.  ALL messages are cleaned up on exit
		 * as a precautionary measure but PLEASE do not rely
       * on this feature as it is ignorant of systems that
       * may have very little RAM to spare
		 * also note that messages WILL be lost if the
       * runs low on memory.  There is no way around this
       * except to send the message CNMP_FREEALL to all
		 * ports.  CNMP_FREEALL causes all ports to flush
		 * their message lists.  Please use this message type
       * sparingly as any queued messages are
       * deleted/removed before they are user processed thus causing
       * themn to be purged without the interpreter processing them.
		 *
		 * types CNMP_ADDPORT, CNMP_REMOVEPORT, CNMP_VARSINIT,
		 * and CNMP_VARSREMOVE are not affected by this at the
		 * HandleIO() level.  In other words, CNMP_FLUSHALL
		 * affects messages that would have been returned to you
		 * via GetCNMPMsg().
		 * it should be noted that a port can be "removed"
		 * at any time so ports should ALWAYS be checked
       * for validity before sending messages or accessing
		 * remote port data.  This can be done using the
       * PortFromMsg() or PortFromNum() or CheckCNMPExists()
       * functions.  I recommend using CheckCNMPExists() as it
       * checks to see if the actual Exec MsgPort exists whereas
		 * PortFromMsg() and PortFromNum() check only at the level
		 * of the current port's linked list which may not be valid
		 * 100% of the time.  The validity of these two functions
		 * is directly related to the amount of time that has passed
		 * between your calls to GetCNMPMsg().   If you wait long
       * periods of time between GetCNMPMsg() calls, then you are
		 * sacrificing  the validity of your port lists.  The ports may
       * go away before all messages have been handled.
		 */

		/* MUST CHECK UserMsg_Events first!  It frees RAM and also speeds */
		/* up the message server by shortening the message list            */
		if(MsgMask & ReplyMsg_Event)
			{
			/* get NT_REPLYMSG from any and all ports and handle them */
			while((UserCNMPMsg=GetNTReplyMsg( -1, MyCnmpCluster  )))
				{
				HandleNT_REPLYMSG(UserCNMPMsg);
				DisposeCNMPMsg(UserCNMPMsg, MyCnmpCluster);
				}
			}
		if(MsgMask & Rexx_Event)	/* An arexx event/msg was sent to us.. Handle it.. */
			{
			HandleRexx();
			}
		if(MsgMask & UserBreak_Event)
			{
			GetOut("^C", 0);
			}
		if(MsgMask & UserMsg_Event)
			{
			/*
			 * There is a message waiting at our message port
			 * use GetCNMPMsg() to get them one at a time
			 * use DisposeCNMPMsg() to clean the msg up after used
			 */

			while((UserCNMPMsg=GetCNMPMsg( -1, MyCnmpCluster )))
				{
				switch(UserCNMPMsg->CNMP_Type)
					{
					case CNMP_ADDPORT:
						/* Check if this message holds a NEW port to be added */
						/* We could also do checking on the port/user to see if */
						/* entry is allowed. */
						/* if you want to refuse a user entry, simply do not */
						/* CNMPortToList().  If you call CNMPortToList(), his port */
						/* is added to your list of ports and your port is added to his */

						sprintf(z->ABuffer, "c4<c7Statusc4>q1 %s (port %d) has entered\n", myp->PortZ[UserCNMPMsg->FromPort]->user1.Handle, UserCNMPMsg->FromPort);
						PutA();

						sprintf(OtherPortName, "%s%d", MyCnmpCluster->CNMP_BASEPORT, UserCNMPMsg->FromPort);
						CNMPortToList(MyCnmpCluster, UserCNMPMsg->FromPort, OtherPortName);
						break;
					case CNMP_REMOVEPORT:
						sprintf(z->ABuffer, "c4<c7Statusc4>q1 %s (port %d) has exited\n", myp->PortZ[UserCNMPMsg->FromPort]->user1.Handle, UserCNMPMsg->FromPort);
						PutA();
						break;
					case CNMP_KICKPORT:
						sprintf(z->ABuffer, "c4<c7Statusc4> q1You have been kicked by %s!\n", (char *)UserCNMPMsg->Data3);
						PutA();
						FreeUserMsgs(MyCnmpCluster);
						IgnoreMsgs=TRUE;
						done = TRUE;
						break;
					case CNMP_STATUS:
						sprintf(z->ABuffer, "c4<c7Statusc4>q1 %s", (char *)UserCNMPMsg->Data1);
						PutA();
						break;
					case CNMP_TEXTECHO:
						/*
						 * CNMP_TEXTECHO - printable message sent from another port!
						 * Allocate memory for this message and then CopyMem()
						 * it and add it to a linked list called CNMPUserMsg
						 */
						if( UserCNMPMsg->Var1 == TRUE )
							{
							/* PRIVATE MESSAGE to user - print handle and message */
							if(UserCNMPMsg->Data3)
								{
								/* handle recieved */
								sprintf(z->ABuffer, "c3(q1z4c6%sz0c3)q1 :2%s:0q1\n\0", (char *)UserCNMPMsg->Data3, (char *)UserCNMPMsg->Data1);
								PutA();
								}
							else
								{
								/* no handle received so print message only, no handle */
								sprintf(z->ABuffer, ":2c6%s:0q1\n", (char *)UserCNMPMsg->Data1);
								PutA();
								}
							}
						else
							{
							/* PUBLIC MESSAGE - print handle and message */
							if(UserCNMPMsg->Data3)
								{
								/* handle recieved */
								sprintf(z->ABuffer, " z4c6%s:z0 :2c6%s:0q1\n", (char *)UserCNMPMsg->Data3, (char *)UserCNMPMsg->Data1);
								PutA();
								}
							else
								{
								/* no handle received so print message only, no handle */
								sprintf(z->ABuffer, ":2c6%s:0q1\n", (char *)UserCNMPMsg->Data1);
								PutA();
								}
							}
						break;
					case CNMP_ENTRYCONFIRM:
						/*
	             	 * CNet port UserCNMPMsg->FromPort has requested confirmation
  	              	 * that it may enter.
					 	 *
					 	 * Call the function PermitEntry(UserCNMPMsg) to allow
					 	 * entry.
					 	 *
					 	 * Call the function RefuseEntry(UserCNMPMsg) to
                	 * refuse/disallow that port.
					 	 *
					 	 * This can be used to check a user's handle/account/access/etc..
					 	 * before letting him/her enter.
				  		 *
					 	 * This test program will ALWAYS allow users in by never
                	 * checking anything and by always sending a CNMP_ENTRYPERMIT
                	 * message in reply via PermitEntry().
              	 	 *
					 	 * The first argument to PermitEntry() and RefuseEntry()
                	 * MUST be a copy of the CNMP_ENTRYCONFIRM message that was
                	 * sent to request permission.
					 	 *
					 	 * The second argument to PermitEntry is a string that is
                	 * echo'd the remote user.  Simply pass an empty string ("")
                	 * if you don't want anything printed on the remote end
						 */

						PermitEntry(UserCNMPMsg, "", MyCnmpCluster);
						break;
					case CNMP_VARSINIT:
	 					/* remote port sending it's data here - don't forget to send ours in return */
	 					/* find the port */
				 		if(TempRemotePort=PortFromMsg(UserCNMPMsg, MyCnmpCluster))
			 				{
		 					if(!TempRemotePort->PortVars)
								{
								/* port found - set the remote port data pointer if we don't already have it */
								TempRemotePort->PortVars = UserCNMPMsg->Data3;

								/* give the remote port access to our declared variables also */
								sprintf(PortRange, "%d", TempRemotePort->PortNum);
								MyCnmpCluster->cc_cnmpmsg->FromPort = z->InPort;
								MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_VARSINIT;
								MyCnmpCluster->cc_cnmpmsg->Data3 = MyCnmpCluster->cc_portvars;
								MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;
								MyCnmpCluster->cc_cnmpmsg->Data1 = NULL;
								SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, FALSE, FALSE);
								}
							}
						break;
					case CNMP_ENTRYPERMIT:
						if(PORTPERMISSION == 1)
							{
							/* print text is existant */
							if(UserCNMPMsg->Data1)
								PutText((char *)UserCNMPMsg->Data1);

							/* Permission allowed - Notify remote port that we have entered! */
							sprintf(PortRange, "%d", UserCNMPMsg->FromPort);
							MyCnmpCluster->cc_cnmpmsg->FromPort = z->InPort;
							MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_ADDPORT;
							MyCnmpCluster->cc_cnmpmsg->Data1 = NULL;
							MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;
							MyCnmpCluster->cc_cnmpmsg->Data3 = NULL;
							SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, FALSE, FALSE);

							/* give the remote port(s) access to our declared variables */
							MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_VARSINIT;
							MyCnmpCluster->cc_cnmpmsg->Data3 = MyCnmpCluster->cc_portvars;
							SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, FALSE, FALSE);
							}
						break;
					case CNMP_DISPLAY_TV:
						/* we really don't need the PortFromMsg() function called below.
	                * It is for example's sake only.  We could have printed our
	                * testvar without it.  It was required in this case because
	                * we are printing the CNet port number that the message was
	                * received from and we don't know it until we find the port
	                * data.
						 */
						if(TempRemotePort=PortFromMsg(UserCNMPMsg, MyCnmpCluster))
							{
							sprintf(z->ABuffer, "Remote Port %d wants us to display \"testvar\".  testvar=%d\n", TempRemotePort->PortNum, MyCnmpCluster->cc_portvars->testvar);
							PutA();
							}
						else
							{
							PutText("ERROR 601: Could not get port from message\n");
							}
						break;
					}

				/* note - due to the way CNMP(tm) is set up, it is okay for
             * CNMPorts to ignore message types.  Important ones are handled
             * by HandleIO().  Message that you don't use will be disposed of
             * anyways, at this point, by DisposeCNMPMsg().  If you want to
             * trap/handle any message types that you have added or #defined
             * yourself, add them - here - before your DisposeCNMPMsg() call.
             */

				DisposeCNMPMsg(UserCNMPMsg, MyCnmpCluster);
				}
			}

		/* Check if there is a pending keyboard event */
		if( (MsgMask & UserKey_Event) || (z->KB1 != z->KB2) )
			{
			if( (z->KBuff[z->KB1] == 13) || (z->KBuff[z->KB1] == 8) )
				{
				/* flushes the keyboard if the first character is <ENTER> or <DEL> */
				KBFlush(z);
				}
			else
				{
				/* there are keys waiting in the keyboard buffer */
				/* Keyboard handling section - user has pressed a key.  Use  */
				/* EnterLine() or OneKey() to read it..                      */

				EnterLine(255, 2048, "c4>q1 ");

				if(strlen(z->InBuffer))
					{
					if(!stricmp(z->InBuffer, "help"))
						{
						DoHelp();
						}
					else if(z->InBuffer[0] == 92)
						{
						/* first character of user entered string is a backslash, therefore, a conference command */

						if(!stricmp(z->InBuffer, "\\Quit"))
							{
							FreeUserMsgs(MyCnmpCluster);
							IgnoreMsgs=TRUE;
							done=TRUE;	/* flag done=TRUE so that program will exit */
							}
						else if(!strnicmp(z->InBuffer, "\\fg", 3))
							{
							if(strlen(z->InBuffer+3) == 1)
								{
								switch(z->InBuffer[3])
									{
									case '0':
									case '1':
									case '2':
									case '3':
									case '4':
									case '5':
									case '6':
									case '7':
									case '8':
									case '9':
									case 'a':
									case 'b':
									case 'c':
									case 'd':
									case 'e':
									case 'f':
										MyFgColor = z->InBuffer[3];
										break;
									default:
										sprintf(z->ABuffer, "\nERROR 509: color value '%c' out of range.\n", z->InBuffer[3]);
										PutA();
										break;
									}
								}
							else
								{
								PutText("ERROR 506: You must specify an single MCI color value from 0-E\n           (ie. 0 for \c0, 1 for \c1)\n\n");
								}
							}
//						else if(!strnicmp(z->InBuffer, "\\join", 5))
//							{
//							if(strlen(z->InBuffer+5))
//								{
//								strcpy(z->ABuffer, z->InBuffer+5);
//								JoinChannel(z->ABuffer);
//								}
//							}
						else if(!stricmp(z->InBuffer, "\\list"))
							{
							DoHelp();
							}
						else if(!strnicmp(z->InBuffer, "\\kick", 5))
							{
							/* NOTE: I am using \KICK to force the user OUT of the
                      * P-File.  Normally, if you are using ROOMS/channels for
                      * a conference, you would kick the user from the CURRENT
                      * channel, not the while conference program.
                      */
							
							if(strlen(z->InBuffer+5))
								{
								strcpy(z->ABuffer, z->InBuffer+5);
								if(CheckDigits(z->ABuffer))
									{
									/* string is all digits */
									z->uz[0] = atoi(z->ABuffer);
									if(TempRemotePort = PortFromNum(z->uz[0], MyCnmpCluster))
										{
										KickUser(TempRemotePort);
										}
									}
								else
									{
									/* assume string is a handle or name since it's not all digits */
									if(TempRemotePort = PortFromName(z->ABuffer, MyCnmpCluster, myp))
										{
										/* Found the port */
										KickUser(TempRemotePort);
										}
									}
								}
							else
								{
								PutText("c1[c6ERROR 505c1]c4: c7You must specify a port or user name to kickq1\n\n");
								}
							}
						else if(!stricmp(z->InBuffer, "\\cnmports"))
							{
							if(MyCnmpCluster->cc_firstport)
								{
								PutText("\nc7## Handle                PortName\nc4== ===================== ========q1\n");
								TempRemotePort=MyCnmpCluster->cc_firstport;
								while(TempRemotePort)
									{
									sprintf(z->ABuffer, "%2d %-21.21s %s\n", TempRemotePort->PortNum, (TempRemotePort->PortVars) ? TempRemotePort->PortVars->z->user1.Handle : "Unknown", TempRemotePort->PortName);
									PutA();
									TempRemotePort=TempRemotePort->NextPort;
									}
								DoReturn();
								}
							}
						else if(!strnicmp(z->InBuffer, "\\var++", 6))
							{
							/* just a test to demonstrate remote port variable alteration */
							z->uz[0] = atoi(z->InBuffer+6);
							if(TempRemotePort = PortFromNum(z->uz[0], MyCnmpCluster))
								{
								TempRemotePort->PortVars->testvar++;
	
								/* here is an example of a user installed CNMP_Type
								 * this type simply tells the remote port to display the
	                      * contents of it's PortVar variable testvar
								 * To add this user-installed CNMP_Type, we added a
	                      * define.  Don't forget to add a handler for this message
								 * type to the routine that handles your CNMP_Types..
								 */
								sprintf(PortRange, "%d", TempRemotePort->PortNum);
								MyCnmpCluster->cc_cnmpmsg->FromPort = z->InPort;
								MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_DISPLAY_TV; /* TV = acronym for testvar */
								MyCnmpCluster->cc_cnmpmsg->Data1 = NULL;
								MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;
								MyCnmpCluster->cc_cnmpmsg->Data3 = NULL;
								SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, TRUE, FALSE);
								}
							}
						else if(!strnicmp(z->InBuffer, "\\VARSCHECK", 10))
							{
							/* check on the state of variables from port x */
							/* this is just an example                     */
	
							z->uz[0] = atoi(z->InBuffer+10);
							if(TempRemotePort = PortFromNum(z->uz[0], MyCnmpCluster))
								{
								sprintf(z->ABuffer, "z->uz[0]: %d\n", z->uz[0]);
								PutA();
								sprintf(z->ABuffer, "CNetPort: %d\n", TempRemotePort->PortVars->z->InPort);
								PutA();
								sprintf(z->ABuffer, "User    : %s\n", TempRemotePort->PortVars->z->user1.Handle);
								PutA();
								sprintf(z->ABuffer, "Account#: %d\n", TempRemotePort->PortVars->z->id);
	
								MakeDate(&TempRemotePort->PortVars->z->user1.ConnectDate, z->InBuffer);
								sprintf(z->ABuffer, "Logon   : %s\n", z->InBuffer);
								PutA();
								
								sprintf(z->ABuffer, "testvar : %d\n", TempRemotePort->PortVars->testvar);
								PutA();
								}
							else
								{
								sprintf(z->ABuffer, "Could not find port %d\n", z->uz[0]);
								PutA();
								}
							}
						else if(!stricmp(z->InBuffer, "\\CLS"))
							{
							/* this is an example of a command affecting the local screen only */
							PutText("f1");
							}
						else if(!stricmp(z->InBuffer, "\\CLSALL"))
							{
							/* this is an example of a command affecting all port screens */
							/* take care of our own screen first. */
							PutText("f1");
							/* send text to other port(s) here - *remember: YOU decide what the VarX and DataX are going to represent */
							MyCnmpCluster->cc_cnmpmsg->Var1 = FALSE;              /* I use Var1 as a "flag" and in the case of a CNMP_TEXTECHO message, it will always mean PRIVATE if TRUE, PUBLIC/BROADCAST if FALSE */
							MyCnmpCluster->cc_cnmpmsg->Data1 = "f1";					/* I use Data1 for the pointer to the remote user's MESSAGE in the case of CNMP_TEXTECHO messages */
							MyCnmpCluster->cc_cnmpmsg->Data1Len=4;						/* 4 bytes long */
							MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;  				/* I use Data2 as a pointer to the remote user's HANDLE in the case of CNMP_TEXTECHO messages */
							MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_TEXTECHO;	/* CNMP_Type sets the type of Message */
							SendCNMPMessage(MyCnmpCluster, "ALL", MyCnmpCluster->cc_cnmpmsg, TRUE, FALSE);		/* Send this message to ALL known ports! */
							}
						else if(!strnicmp(z->InBuffer, "\\msg", 4))
							{
							if(!strlen(z->InBuffer+4))
								{
								PutText("\nYou must specify a port number if you wish to send a private message\n(ie. \\priv0)\n\n");
								}
							else
								{
								strcpy(PortRange, z->InBuffer+4);
								RemoveLeadingSpace(PortRange);
								if(strlen(PortRange))
									{
									if(stricmp(PortRange, "ALL"))
										{
										/* user is not broadcasting..  Check for name or port number */
										if(CheckDigits(PortRange))
											{
											/* string is all digits */
											z->uz[0] = atoi(PortRange);
											if(TempRemotePort = PortFromNum(z->uz[0], MyCnmpCluster))
												{
												/* Found the port */
												sprintf(PortRange, "%d", TempRemotePort->PortNum);
												}
											else
												{
												sprintf(z->ABuffer, "ERROR 507: %s not found\n\n", PortRange);
												PutA();
												PortRange[0]=0;
												}
											}
										else
											{
											/* assume string is a handle or name since it's not all digits */
											if(TempRemotePort = PortFromName(PortRange, MyCnmpCluster, myp))
												{
												/* Found the port */
												sprintf(PortRange, "%d", TempRemotePort->PortNum);
												}
											else
												{
												sprintf(z->ABuffer, "ERROR 508: %s not found\n\n", PortRange);
												PutA();
												PortRange[0]=0;
												}
											}
										}
									if(strlen(PortRange))
										{
										sprintf(z->ABuffer, "msg %s> ", PortRange);
										EnterLine(70, 0, z->ABuffer);
										if(strlen(z->InBuffer))
											{
											/* send text to other port here - *remember: YOU decide what the VarX and DataX are going to represent */
											MyCnmpCluster->cc_cnmpmsg->Var1 = TRUE;
		
						               /* I use Var1 as a "flag" and in the case of a CNMP_TEXTECHO message, it will always mean PRIVATE if TRUE, PUBLIC/BROADCAST if FALSE */
											sprintf(z->ABuffer, "z%cc%c%s", MyBgColor, MyFgColor, z->InBuffer);
											MyCnmpCluster->cc_cnmpmsg->Data1Len = sizeof(UBYTE)*strlen(z->ABuffer)+1;
											MyCnmpCluster->cc_cnmpmsg->Data1 = z->ABuffer;	 	    /* I use Data1 for the pointer to the remote user's MESSAGE in the case of CNMP_TEXTECHO messages */
											MyCnmpCluster->cc_cnmpmsg->Data3 = &z->user1.Handle;   /* I use Data3 as a pointer to the remote user's HANDLE in the case of CNMP_TEXTECHO messages */
											MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_TEXTECHO;	 /* CNMP_Type sets the type of Message */
											SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, TRUE, FALSE); /* Send this message only to the user specified port! */
											}
										}
									}
								}
							}
						else
							{
							strcpy(z->InBuffer, z->InBuffer + 1);
							if(!CommonCommands())
								{
								PutText("\nEnter \"\LIST\" for a summary of commands\n\n");
								}
							else
								{
								PutText("\n");
								}
							}
						}
					else
						{
						/* send text to other port here - *remember: YOU decide what the VarX and DataX are going to represent */
						/* I chose them to represent what I needed them to be applied to for this mini conference set-up..     */
						/* p.s. the character 8 check below is a backspace trap. */
						if(strlen(z->InBuffer))
							{
							if(z->InBuffer[0] == 8)
								{
								DoReturn();
								}
							else
								{
								sprintf(z->ABuffer, "z%cc%c%s", MyBgColor, MyFgColor, z->InBuffer);
								MyCnmpCluster->cc_cnmpmsg->Data1Len = sizeof(UBYTE)*strlen(z->ABuffer)+1;
								MyCnmpCluster->cc_cnmpmsg->Var1 = FALSE;              /* I use Var1 as a "flag" and in the case of a CNMP_TEXTECHO message, it will always mean PRIVATE if TRUE, PUBLIC/BROADCAST if FALSE */
								MyCnmpCluster->cc_cnmpmsg->Data1 = z->ABuffer; 	    /* I use Data1 for the pointer to the remote user's MESSAGE in the case of CNMP_TEXTECHO messages */
								MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;
								MyCnmpCluster->cc_cnmpmsg->Data3 = &z->user1.Handle;  /* I use Data3 as a pointer to the remote user's HANDLE in the case of CNMP_TEXTECHO messages */
								MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_TEXTECHO;	/* CNMP_Type sets the type of Message */
								SendCNMPMessage(MyCnmpCluster, "ALL", MyCnmpCluster->cc_cnmpmsg, TRUE, FALSE);		/* Send this message to ALL known ports! */
								}
							}
						}
					}
				}
			}
		if(MsgMask & UserTimer_Event)
			{
			/* Do other processing here. Anything that requires periodic
            checking/processing can be done here */
			/* This timeout message occurs approximately every 3/8 seconds */

			/* Cnet seems to only check OLMs at PROMPTS (ie. EnterLine())
          * So you will have to check z->OLMWaiting to see if an OLM exists..
          * Also, the only place CNet allows an OLM to be displayed is at a
          * prompt.  ie.  During EnterLine().  OLMs will not appear unless
          * you call EnterLine().  Thus, OLMs will not be displayed until
          * you require input from the user.
          * OLMsOff() is a function that I have written to disable/save
          * olms.  Write me if you require such a function
			 */
			}

			CheckFlowControl();
			OlmCheck();
		} /* end of while(!done) loop */

	/* get back to CNet if done==TRUE */
	GetOut("", 0);
}



/**************************************************************************
 *       Routine called if Load error (wrong CNet version, etc,...        *
 **************************************************************************/
void LoadError( void )
{
	DeletePort( replyp );
	exit(0);
}


/************************************************************************
 *                          MetalSoft EXIT code                         *
 ************************************************************************/
void GetOut( char *text, short errornum )
{
	struct CNMPMsg *Msg = NULL;

	/* bring the rexx port down */
	if(RexxContext)
		FreeARexxContext(RexxContext);

	NotifyCNMPRemove(MyCnmpCluster);
	Forbid();
	while(Msg=(struct CNMPMsg *)GetMsg(MyCnmpCluster->cc_msgport))
		{
		if(Msg->Msg.mn_Node.ln_Type == NT_REPLYMSG)
			{
			FreeMem(Msg, sizeof(struct CNMPMsg));
			}
		else
			{
			ReplyMsg((struct Message *)Msg);
			}
		}

	/* free linked list of ports */
	FreeCNMPList(MyCnmpCluster);
	if(MyCnmpCluster->cc_msgport)
		{
		RemPort(MyCnmpCluster->cc_msgport);
		DeleteMsgPort(MyCnmpCluster->cc_msgport);		/* delete our message port */
		}
	MyCnmpCluster->cc_msgport=NULL;
	Permit();
	CleanupTimeout(MyCnmpCluster);
	if(MyCnmpCluster->cc_iotr->tr_node.io_Device)
		{
		CloseDevice((struct IORequest *)MyCnmpCluster->cc_iotr);
		MyCnmpCluster->cc_iotr->tr_node.io_Device = NULL;
		}
	if(MyCnmpCluster->cc_iosink)
		{
		DeleteMsgPort(MyCnmpCluster->cc_iosink);
		MyCnmpCluster->cc_iosink = NULL;
		}

	FreeUserMsgs(MyCnmpCluster);

	if( (errornum > 0) || (strlen(text)) )
		{
		PutText(text);
		}

	SetDoing(oldDOING);
	ShutDown( NULL );
	DeletePort( replyp );
	exit(errornum);
}



void ShutDown( char *spawn )			/* another file to run? */
{
	if( spawn )
		strcpy( z->CSpawn, spawn );

	CallHost( 0 );
}



void CallHost( UBYTE c )
{
	cmess.command = c;
	PutMsg  ( (struct MsgPort *)cport, (struct Message *)&cmess );
	WaitPort( replyp );
	GetMsg  ( replyp );
}



void PutText( char *text )
{
	cmess.arg1 = (ULONG)text;	/* text to print		*/
	CallHost( 1 );
}



void PutA( void )
{
	PutText( z->ABuffer );
}




/**************************************************************************
 *		ENTERLINE FLAGS:                                                    *
 *		    1:	All capitalized                                            *
 *		    2:	FILENAME.  Don't allow =":;/ or asterisk                   *
 *		    4:	Begin with existing z.InBuffer                             *
 *		    8:	Chop leading spaces                                        *
 *		   16:	Force 1st letter of word caps                              *
 *		   32:	Force all others lower case                                *
 *		   64:	Numeric input only                                         *
 *		  128:	Print input box (.)                                        *
 *		  256:	DO allow MCI                                               *
 *		  512:	HANDLES/SPECIAL.  Don't allow ^_`{|}~@                     *
 *		 1024:	Exit for . or / at beginning of line                       *
 *		 2048:	Exit for backspace at beginning of line                    *
 *		 4096:	Do not allow OLM's to appear while editing                 *
 *		 8192:	Allow Chat break in at this prompt. COMMAND PROMPT.        *
 *		16384:	Don't allow SPACE, either                                  *
 *		32768:	DON'T ALLOW MOVEMENT                                       *
 **************************************************************************/
int EnterLine( UBYTE len, USHORT flags, char *prompt )
{
	cmess.arg1 = (ULONG)len;	/* how many chars max to input	*/
	cmess.arg2 = (ULONG)flags;	/* 1=UpperCase			*/
	cmess.arg3 = (ULONG)prompt;	/* text to print before input	*/
	CallHost( 2 );			/* result is in z->InBuffer	*/
	return( (int)strlen( z->InBuffer ));
}



char OneKey( void )			/* Stop until a key is pressed	*/
{
	CallHost( 3 );
	return( (char)cmess.result );
}



void EnterPassword( UBYTE len )
{
	cmess.arg1 = (ULONG)len;	/* max number of characters */
	CallHost( 4 );
}



long CommonCommands( void )		/* Check z->InBuffer for Chat, OLM, etc */
{
	CallHost( 5 );
	return( (long)cmess.result );
}



UBYTE ReadFile( char *path, UBYTE flags )
{
	cmess.arg1 = (ULONG)path;
	cmess.arg2 = (ULONG)flags;	/* 1 = print File Not Found	*/
	CallHost( 6 );
	return( (UBYTE)cmess.result );		/* FALSE if File Not Found	*/
}



void SetDoing( char *what )
{
	cmess.arg1 = (ULONG)what;
	CallHost( 7 );
}



void CallEditor( short max, short inlines )
{
	cmess.arg1 = (ULONG)max;	/* Maximum number of lines (250)*/
	cmess.arg2 = (ULONG)inlines;	/* TRUE/FALSE use existing _edbuff? */
	CallHost( 8 );
}



UBYTE ReadGraphics( char *path, char flags )
{
	cmess.arg1 = (ULONG)path;
	cmess.arg2 = (ULONG)flags;	/* 1 = print File Not Found	*/
	CallHost( 9 );
	return( (UBYTE)cmess.result );		/* FALSE if File Not Found	*/
}



void MakeDate( struct IsDate *date, char *output )
{
	cmess.arg1 = (ULONG)date;
	cmess.arg2 = (ULONG)output;
	CallHost( 10 );
}



UBYTE ReadAccount( short id, struct UserData *user )
{
	cmess.arg1 = (ULONG)id;
	cmess.arg2 = (ULONG)user;
	CallHost( 11 );
	return( (UBYTE)cmess.result );
}



UBYTE SaveAccount( struct UserData *user, short id )
{
	cmess.arg1 = (ULONG)user;
	cmess.arg2 = (ULONG)id;
	CallHost( 12 );
	return( (UBYTE)cmess.result );
}



UBYTE AddCharge( short n, short a )
{
	cmess.arg1 = (ULONG)n;
	cmess.arg2 = (ULONG)a;
	CallHost( 13 );
	return( (UBYTE)cmess.result );
}



UBYTE CheckBalance( short n, short a )
{
	cmess.arg1 = (ULONG)n;
	cmess.arg2 = (ULONG)a;
	CallHost( 14 );
	return( (UBYTE)cmess.result );
}



int EnterText( char firstchar, short maxchars, short perline, short maxlines )
{
	cmess.arg1 = (ULONG)firstchar;
	cmess.arg2 = (ULONG)maxchars;
	cmess.arg3 = (ULONG)perline;
	cmess.arg4 = (ULONG)maxlines;
	CallHost( 15 );
	return( (int)cmess.result );
}



long ConferenceWait( short a )
{
	cmess.arg1 = (ULONG) a;
	CallHost( 16 );
	return( (long)cmess.result );
}



void CheckChanges( void )
{
	CallHost( 17 );
}



long ConvertAccess( char *s )
{
	cmess.arg1 = (ULONG)s;
	CallHost( 18 );
	return( (long)cmess.result );
}



long GetFree( char *s, UBYTE q )
{
	cmess.arg1 = (ULONG)s;
	cmess.arg2 = (ULONG)q;
	CallHost( 19 );
	return( (long)cmess.result );
}



short FindAccount( char *a, struct UserData *b )
{
	cmess.arg1 = (ULONG)a;
	cmess.arg2 = (ULONG)b;
	CallHost( 20 );
	return( (short)cmess.result );
}



/**************************************************************************
 * forces CNet to update it's keyboard buffers etc,..
 **************************************************************************/
void CheckFlowControl( void )
{
	CallHost( 21 );
}



long ListDir( UBYTE a, UBYTE b, struct IsDate *c )
{
	cmess.arg1 = (ULONG)a;
	cmess.arg2 = (ULONG)b;
	cmess.arg3 = (ULONG)c;
	CallHost( 22 );
	return( (int)cmess.result );
}



UBYTE FileOLM( short a, int b )
{
	cmess.arg1 = (ULONG)a;
	cmess.arg2 = (ULONG)b;
	CallHost( 23 );
	return( (UBYTE)cmess.result );
}



UBYTE Rnext( void )
{
	CallHost( 24 );
	return( (UBYTE)cmess.result );
}



void ParseCommandLine( UBYTE numargs )
{
	cmess.arg1 = (ULONG)numargs;
	CallHost( 25 );
}



short FindCommand( short num )
{
	cmess.arg1 = (ULONG) num;
	CallHost( 26 );
	return( (short)cmess.result );
}



void ReadMessagePoint( char *a, long b )
{
	cmess.arg1 = (ULONG) a;
	cmess.arg2 = (ULONG) b;
	CallHost( 27 );
}



void EditMessage( char *file )
{
	cmess.arg1 = (ULONG) file;
	CallHost( 28 );
}



void LoadText( BPTR fh )
{
	cmess.arg1 = (ULONG) fh;
	CallHost( 29 );
}



char WaitForInput( long mics )
{
	/* 1000000 mics = 1 second */

	cmess.arg1 = (ULONG) mics;
	CallHost( 31 );
	return( (char)cmess.result );
}



void AddCredits( UBYTE justadd, short m )
{
	cmess.arg1 = (ULONG)justadd;
	cmess.arg2 = (ULONG)m;
	CallHost( 33 );
}



UBYTE SelectAndDownload( char *file, UBYTE now )
{
	cmess.arg1 = (ULONG)file;
	cmess.arg2 = (ULONG)now;
	CallHost( 39 );
	return( (UBYTE)cmess.result );
}



/************************************************************************
 *	file: the ".vde" filename, without the ".vde"!                       *
 *	data: pointer to the structure you are going to edit                 *
 *	size: structure length in bytes                                      *
 *                                                                      *
 *	returns:	TRUE  if structure has been changed                         *
 *				FALSE otherwise                                             *
 ************************************************************************/
short VisualDataEditor( char *file, void *data, long size )
{
	cmess.arg1 = (ULONG)file;
	cmess.arg2 = (ULONG)data;
	cmess.arg3 = (ULONG)size;
	CallHost( 40 );
	return( (short)cmess.result );
}



void WriteLog( short n, char *text1, char *text2 )
{
	cmess.arg1 = (ULONG)n;
	cmess.arg2 = (ULONG)text1;
	cmess.arg3 = (ULONG)text2;
	CallHost( 41 );
}



/************************************************************************
 *	In preparation for an ExtUpload, this function                       *
 *	sets the minimum number of free bytes to maintain on the             *
 *	drive.                                                               *
 ************************************************************************/ 
void ExtSetMinFree( long free )
{
	cmess.arg1 = (ULONG)free;
	CallHost( 42 );
}



/************************************************************************
 *	In preparation for an ExtDownload or an ExtUpload, this function     *
 *	sets the protocol to be used.  If you send NULL, it will allow the   *
 *	user to choose his OWN protocol.                                     *
 *                                                                      *
 *	Otherwise, you may select 'a' to be the first letter of a valid      *
 *	system protocol (from BBSPROTO file), such as 'x', 'z', etc.         *
 *                                                                      *
 *	TRUE will be returned if a protocol is selected and ready, FALSE     *
 *	if there is a problem.                                               *
 ************************************************************************/
UBYTE ExtSetProtocol( char a )
{
	cmess.arg1 = (ULONG)a;
	CallHost( 43 );
	return( (UBYTE)cmess.result );
}



/* 
	This routine allows the user to download the SINGLE file specified
	by the FULL PATH 'args'.

	Currently, NULL is always returned.
*/
char *ExtDownload( char *args )
{
	cmess.arg1 = (ULONG)args;
	CallHost( 44 );
	return( (char *)cmess.result );
}



/*
	This routine allows the user to upload the file specified by
	'args'.  The path for uploading will be taken from the path
	in 'args'.  If you do NOT specify a path, the file(s) will
	appear in the user's HOME directory.

	Note that with batch protocols like ZMODEM, the filename(s) are
	taken from the header packet information, and may NOT be the
	same as what you have requested the user upload.  For this reason,
	you should have uploads occur in a TEMP directory, and search that
	directory yourself for new files.

	Currently, NULL is always returned.
*/
char *ExtUpload( char *args )
{
	cmess.arg1 = (ULONG)args;
	CallHost( 45 );
	return( (char *)cmess.result );
}




short compstra( char *s, char *t )
{
	for( ; tolower(*s) == tolower(*t); s++, t++)
		if( !*s ) return 0;

	return (short)( tolower(*s)-tolower(*t) );
}



UBYTE PutQ( char *a )
{
	PutText( a );
	return (UBYTE)(z->MCIcreg[0][0]=='1') ;
}



void DoReturn( void )
{
	PutText("\n");
}



void MakeEd( char *path )
{
	sprintf( path, "%s_edbuff%d", myp->gc.ZIPpath, z->InPort );
}



void DeleteEd( void )
{
	char	filename[80];

	MakeEd    ( filename ) ;
	DeleteFile( filename ) ;
}



BPTR OpenEd( long mode )
{
	char	filename[80];

	MakeEd( filename );

	return Open( filename, mode );
}



void PrepEditor( BPTR fp )
{
	BPTR	kp;
	char	buff[100];

	if( fp ) {
		if( kp = OpenEd( MODE_NEWFILE ) ) {
			while( FGets( fp, buff, 82 ) && buff[0]!=26 )
			       FPuts( kp, buff     ) ;

			Close( kp );
		}
	}
	else	DeleteEd();
}



void SaveEditor( BPTR fp, UBYTE eof )
{
	BPTR	kp;
	char	buff[100];

	if( kp = OpenEd( MODE_OLDFILE ) ) {
		while( FGets( kp, buff, 82 ) && buff[0]!=26 )
		       FPuts( fp, buff     ) ;

		Close( kp );

		DeleteEd();
	}

	if( eof ) FPuts( fp, "\032\n" );
}


/**************************************************************************
 * This function not part of CNMP - this is required for EasyRexx
 **************************************************************************/
void HandleRexx( void )
{
	UBYTE ResultString[255];
	LONG ResultLong=0;

	ResultString[0]='\0';
	ResultLong=0;

	if(GetARexxMsg(RexxContext))
		{
		switch(RexxContext->id)
			{
			case AREXX_QUIT:
				/* received quit message from ARexx */
				ReplyARexxMsg(RexxContext, TAG_DONE);
				GetOut("", 0);
				break;
			case AREXX_TEXTECHO:
				/* Recieved a message from ARexx to echo/print some text */
				sprintf(z->ABuffer, "z4c6AREXX_MSG:q1 %s\n", ARGSTRING(RexxContext, 0));
				PutA();
				break;
			case AREXX_VERSION:
				/* received a message from ARexx to print the version number */
				sprintf(ResultString, "%s", vers+5);
				break;
			case AREXX_INC_TV:
				/* received a message from ARexx to increment this port's testvar variable */
				MyCnmpCluster->cc_portvars->testvar += ARGNUMBER(RexxContext, 0);
				break;
			case AREXX_SHOW_TV:
				/* received a message from ARexx to display this port's testvar variable */
				sprintf(z->ABuffer, "** Arexx asked us to display testvar:  testvar=%d\n", MyCnmpCluster->cc_portvars->testvar);
				PutA();
				break;
			case AREXX_QUERY_TV:
				/* received a message from ARexx to return this port's testvar variable */
				ResultLong=MyCnmpCluster->cc_portvars->testvar;
				break;
			}
		if(strlen(ResultString))
			ReplyARexxMsg(RexxContext, ER_ResultString, ResultString, TAG_DONE);
		else if(ResultLong)
			ReplyARexxMsg(RexxContext, ER_ResultLong, ResultLong, TAG_DONE);
		else
			ReplyARexxMsg(RexxContext, TAG_DONE);
		}
}



/**************************************************************************
 * Functions below added only to support the Mini-Conference but
 * not-necessarily needed by other CNMP applications.
 **************************************************************************/
void DoHelp( void )
{
	PutText("\nThis test program accepts the following commands:\n\n");
	PutText("	\\quit	\\list	\\var++	\\varscheck\n	\\cls	\\clsall	\\msg	\\cnmports\n	\\kick	\\fg\n\n");
	PutText("The \\ (backslash) is also an escape inducer to let you use CNet \"everywhere\" commands.  Try \\who now..\n\n");
}



/**************************************************************************
 * CheckDigits() - Returns true if string is ALL digits, false otherwise  *
 **************************************************************************/
BOOL CheckDigits( char *str )
{
	BOOL status=TRUE;

	while(*str != '\0')
		{		
		if(!isdigit(*str))
			{
			/* if you want to speed the routine up, "return FALSE" here rather. */
			/* than just setting a flag. For me, speed isn't a concern here.    */
			status=FALSE;
			};
		str++;
		}
	return status;
}


/**************************************************************************
 * Note that this routine does not do sanity checking on the length of
 * the string buffer passed to it.  It shouldn't matter though because we
 * are attempting to SHORTEN it, not lengthen it.
 **************************************************************************/
void RemoveLeadingSpace( char *str )
{
	char *retstr;
	char *tempstr;

	tempstr=retstr=str;

	/* advance past space */
	while( (isspace(*str)) && (*str != '\0') )
		str++;

	while(*str != '\0')
		{
		*retstr=*str;
		retstr++;
		str++;
		}
	*retstr='\0';
}



void KickUser(struct RemotePort *TempRemotePort)
{
	char PortRange[50];

	/* Found the port */
	sprintf(PortRange, "%d", TempRemotePort->PortNum);
	MyCnmpCluster->cc_cnmpmsg->FromPort = z->InPort;
	MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_KICKPORT; /* Kick this user the hell outta there! */
	MyCnmpCluster->cc_cnmpmsg->Data1 = NULL;
	MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;
	MyCnmpCluster->cc_cnmpmsg->Data3 = z->user1.Handle;
	SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, TRUE, FALSE);

	strcpy(PortRange, "all");
	MyCnmpCluster->cc_cnmpmsg->FromPort = z->InPort;
	MyCnmpCluster->cc_cnmpmsg->CNMP_Type = CNMP_STATUS; /* Notify other ports */
	
	/* set up the string to send */
	sprintf(z->ABuffer, "%s kicked by %s\n", myp->PortZ[TempRemotePort->PortNum]->user1.Handle, z->user1.Handle);
	
	/* allocate memory for the string - it will be free()'d when the message is replied to */
	MyCnmpCluster->cc_cnmpmsg->Data1Len = sizeof(UBYTE)*strlen(z->ABuffer)+1;
	MyCnmpCluster->cc_cnmpmsg->Data1=z->ABuffer;
	MyCnmpCluster->cc_cnmpmsg->Data2 = NULL;
	MyCnmpCluster->cc_cnmpmsg->Data3 = z->user1.Handle;
	SendCNMPMessage(MyCnmpCluster, PortRange, MyCnmpCluster->cc_cnmpmsg, TRUE, FALSE);
}


BOOL FileExists(char *Name)
{
   BPTR FileLock;

   if (FileLock = Lock(Name, SHARED_LOCK))
      {
         UnLock(FileLock);
         return TRUE;
      }

   return FALSE;
}

/**************************************************************************
 * Add keys to CNet keybuffer of current port
 **************************************************************************/
void AddKeyBuffer( char *c )
{
	while( *c )
		{
		z->KBuff[ z->KB2++ ] = *c;
		c++;
		}
}




/**************************************************************************
 *
 *
 *  ************* ROUTINES BELOW NOT YET IMPLEMENTED! **************
 *
 *
 **************************************************************************/



void OlmCheck( void )
{
	char OlmFilename[48];  /* 40 chars max OLMPath + 8 chars max OLMFilename */

	sprintf(OlmFilename, "%s_olm%d", myp->gc.OLMpath);
	if( (FileExists(OlmFilename)) && (z->OLMWaiting) )
		{
		/* yep.. OLM waiting - force enterline? */
		PutText("Olm Waiting..\n");
		}
}








/**************************************************************************
 * join channel "chan_name"
 **************************************************************************/
APTR JoinChannel(char *chan_name)
{
//	LockChannelList();	/* lock the channel list */
//	FindNode(chan_name); /* find the channel node if existent */

	return NULL;
}



/**************************************************************************
 * Create the list header if none are existent, etc,..
 **************************************************************************/
struct ChannelList *GetChannelList( void )
{
	struct ChannelList *clist=NULL;

	/* search all known ports for a channel list */
	/* if none found, create a new one. */

//	if( clist=AllocMem(sizeof(struct ChannelList), MEMF_PUBLIC|MEMF_CLEAR) )
//		{
//		NewList(&clist->cl_List);
//		clist->cl_Sem.ss_Link.ln_Pri = 0;
//		clist->cl_Sem.ss_Link.ln_Name = MyCNMPortName;
//		AddSemaphore() here
//		}

	return clist;	
}


/***************************************************************************
 * Checks if there is a CNMP message waiting - use this if you don't       *
 * want to Wait() for signals                                              *
 ***************************************************************************/
BOOL CheckCNMPMsg( void )
{
/* fix! */
	return FALSE;
}


/*
ALSO - WRITE the intuition messageport checking routine into a PROCESS
       so that WE don't have to Wait() for messages.. the process will
       store CNMP Messages as usual but use SEMAPHORES to lock/access
       the shared lists..
*/