/* * CTB (Communication Toolbox) plugin for RB 3.0 and later * * By Thomas Tempelmann, http://www.tempel.org/rb/ */#if TARGET_OS_WIN32	#include <MacTypes.h>	static void inline DebugStr(unsigned char* p) {}	#define log(x) OutputDebugString(x)#else	#include <MacMemory.h>	#include "dcon.h"	#define log(x) dprintf(x)#endif// Std C#include <string.h>// Mac OS#if TARGET_API_MAC_OS8	#include <CommResources.h>	#include <CTBUtilities.h>	#include <Connections.h>#else	typedef long ConnHandle;	typedef long CMBufferSizes[8];	typedef long CMStatFlags;	enum {		cmQuiet,		cmDataIn,		cmDataOut,	};#endif// RB#include "TT's Plugin Support.h"#include "rb_plugin.h"typedef long	RBinteger;typedef Boolean	RBboolVar;	// use this for Boolean variables stored in this plugintypedef Boolean	RBboolPar;	// use this to receive Boolean parameters (both by value and by reference)typedef long	RBboolRet;	// use this when returning a Boolean as a function result// -- CTB handling code --static Boolean ctb_inited = false;static long toTicks (long timeout){	if (timeout <= 0) return timeout;	return timeout * 60;}static OSErr ctb_Init (){	#if TARGET_API_MAC_OS8		if (not ctb_inited) {			OSErr err = InitCTBUtilities();	// this must come first due to a Q&A from Apple			if (!err) err = InitCRM();			if (!err) err = InitCM();			if (!err) ctb_inited = true;			return err;		}	#endif	return 0;}static OSErr ctb_selectTool (short *id, ConstStringPtr toolName){	OSErr err = -1;	#if TARGET_API_MAC_OS8		*id = -1;	// -1 is invalid ProcID		err = ctb_Init ();		if (!err) {			*id = CMGetProcID (toolName);			if (*id == -1) err = -1;		}	#endif	return err;}static OSErr ctb_newConn (ConnHandle *connHdl, short id, long options = cmQuiet){	#if TARGET_API_MAC_OS8		if (id != -1) {			CMBufferSizes bufSizes = {0, 0};			*connHdl = CMNew (id, cmData | cmNoMenus | options, bufSizes, 0, 0);			if (!*connHdl) return -1;			return 0;		}	#endif	return -1;}static OSErr ctb_setConfigStr (ConnHandle connHdl, const char* configStr){	#if TARGET_API_MAC_OS8		if (connHdl) {			CMIdle (connHdl);			return CMSetConfig (connHdl, configStr);		}	#endif	return paramErr;}static void ctb_idle (ConnHandle connHdl){	#if TARGET_API_MAC_OS8		if (connHdl) CMIdle (connHdl);	#endif}static OSErr ctb_listen (ConnHandle connHdl){	#if TARGET_API_MAC_OS8		if (connHdl) {			return CMListen (connHdl, true, nil, -1);		}	#endif	return paramErr;}static OSErr ctb_accept (ConnHandle connHdl, Boolean accepted){	#if TARGET_API_MAC_OS8		if (connHdl) {			return CMAccept (connHdl, accepted);		}	#endif	return paramErr;}static void ctb_connClose (ConnHandle *connHdl){	#if TARGET_API_MAC_OS8		if (*connHdl and **connHdl) {			CMAbort (*connHdl);			CMClose (*connHdl, false, nil, 0, true);			CMDispose (*connHdl);			*connHdl = nil;		}	#endif}typedef struct {	RBboolVar	dataAvail;	RBboolVar	listening;	RBboolVar	incomingCall;	RBinteger	status;		// 0: closed, 1: opening, 2: open, 3: closing	RBinteger	dataInAmt;	RBinteger	dataOutAmt;} statusRec;static OSErr ctb_status (ConnHandle connHdl, statusRec *sr){	#if TARGET_API_MAC_OS8		if (connHdl) {			CMBufferSizes sizes;			CMStatFlags stat;			OSErr err = CMStatus (connHdl, sizes, &stat);			if (not err) {				sr->dataInAmt = sizes[cmDataIn];				sr->dataOutAmt = sizes[cmDataOut];				sr->dataAvail = (stat & cmStatusDataAvail) != 0;				sr->listening = (stat & cmStatusListenPend) != 0;				sr->incomingCall = (stat & cmStatusIncomingCallPresent) != 0;				short n = stat & 7;				if (n & 4) {					sr->status = 3;	// closing				} else if (n & 2) {					sr->status = 2;	// open				} else {					sr->status = n & 1;				}			}			return err;		}	#endif	return paramErr;}static OSErr ctb_readSync (ConnHandle connHdl, Ptr dest, long reqCnt, long *actCnt, Boolean *isEOM, long timeout){	#if TARGET_API_MAC_OS8		if (connHdl) {			CMFlags flags = 0;			*actCnt = 0;			long n = reqCnt;			OSErr err = CMRead (connHdl, dest, &n, cmData, false, nil, toTicks(timeout), &flags);			*actCnt = n;			if (not err) {				*isEOM = (flags & cmFlagsEOM) != 0;			}			return err;		}	#endif	return paramErr;}static OSErr ctb_write (ConnHandle connHdl, Boolean async, const char* src, long reqCnt, long *actCnt, Boolean isEOM, long timeout){	#if TARGET_API_MAC_OS8		if (connHdl) {			CMFlags flags = isEOM ? cmFlagsEOM : 0;			OSErr err = CMWrite (connHdl, src, &reqCnt, cmData, async, nil, async?-1:toTicks(timeout), flags);			*actCnt = reqCnt;			return err;		}	#endif	return paramErr;}// -- RB plugin specific code --typedef struct ListCarrier {	ListCarrier	*prev, *next;	REALobject	obj;} ListCarrier;ListCarrier	gRoot;static void listAttach (ListCarrier &l, REALobject instance){	ListCarrier &r = gRoot;	l.next = r.prev->next;	l.prev = r.prev;	r.prev->next = &l;	r.prev = &l;	l.obj = instance;}static void listDetach (ListCarrier &l){	l.prev->next = l.next;	l.next->prev = l.prev;}extern REALclassDefinition ctbClass;	// forward declarationstruct ctbClassData{	ListCarrier	link;	ConnHandle	connHdl;	RBinteger	lastError;	RBinteger	toolID;	RBinteger	timeout;	REALstring	toolName;	REALstring	toolConfig;	RBboolVar	isEOM;	statusRec	stat;};static void ctbClassConstructor (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	listAttach (me->link, instance);	me->toolID = -1;	me->stat.dataInAmt = -1;//++testing}static void ctbClassDestructor (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	listDetach (me->link);	ctb_connClose (&me->connHdl);	REALUnlockString (me->toolName);	REALUnlockString (me->toolConfig);}static void selTool (REALobject instance, REALstring str){	ClassData (ctbClass, instance, ctbClassData, me);	REALUnlockString (me->toolName);	me->toolName = str;	REALLockString (me->toolName);	short id;	OSErr err = ctb_selectTool (&id, me->toolName->PString());	if (!err) {		me->toolID = id;	}	me->lastError = err;}static void openConn (REALobject instance, RBboolPar showAlrts){	ClassData (ctbClass, instance, ctbClassData, me);	me->lastError = ctb_newConn (&me->connHdl, me->toolID, showAlrts ? 0 : cmQuiet);}static void configTool (REALobject instance, REALstring str){	ClassData (ctbClass, instance, ctbClassData, me);	REALUnlockString (me->toolConfig);	me->toolConfig = str;	REALLockString (me->toolConfig);	me->lastError = ctb_setConfigStr (me->connHdl, me->toolConfig->CString());}static void idle (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	ctb_idle (me->connHdl);}static void ctblisten (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	me->lastError = ctb_listen (me->connHdl);}static void ctbaccept (REALobject instance, RBboolPar ok){	ClassData (ctbClass, instance, ctbClassData, me);	me->lastError = ctb_accept (me->connHdl, ok);}static void close (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	ctb_connClose (&me->connHdl);	me->lastError = 0;}static void status (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	if (not me->connHdl) {		memset (&me->stat, 0, sizeof (me->stat));	} else {		ctb_status (me->connHdl, &me->stat);	}}static REALstring readStr (REALobject instance, RBinteger amount){	ClassData (ctbClass, instance, ctbClassData, me);	Boolean isEOM = false;	REALstring s = nil;	if (amount > 0) {		long actCnt;		s = REALBuildString (nil, amount);		me->lastError = ctb_readSync (me->connHdl, (Ptr)s->CString(), amount, &actCnt, &isEOM, me->timeout);		if (amount != actCnt) {			// make a new string with the correct size			REALstring s2 = REALBuildString (nil, actCnt);			memcpy ((Ptr)s2->CString(), s->CString(), actCnt);			REALUnlockString (s);			s = s2;		}	} else {		me->lastError = 0;	}	me->isEOM = isEOM;	return s;}static RBinteger readMem (REALobject instance, REALmemoryBlock mb, RBinteger amount){	ClassData (ctbClass, instance, ctbClassData, me);	Boolean isEOM = false;	long actCnt = 0;	long size = REALMemoryBlockGetSize (mb);	if (size != 0 and (amount < 0 or amount > size)) amount = size;	if (amount != 0) {		me->lastError = ctb_readSync (me->connHdl, (Ptr)REALMemoryBlockGetPtr(mb), amount, &actCnt, &isEOM, me->timeout);	} else {		me->lastError = 0;	}	me->isEOM = isEOM;	return actCnt;}static RBinteger writeStr2 (REALobject instance, REALstring str, RBboolPar isEOM){	ClassData (ctbClass, instance, ctbClassData, me);	long amount = str->Length();	long actCnt = 0;	if (amount > 0) {		me->lastError = ctb_write (me->connHdl, false, str->CString(), amount, &actCnt, isEOM, me->timeout);	} else {		me->lastError = 0;	}	return actCnt;}static RBinteger writeStr1 (REALobject instance, REALstring str){	return writeStr2 (instance, str, false);}static RBinteger writeMem2 (REALobject instance, REALmemoryBlock mb, RBinteger amount, RBboolPar isEOM){	ClassData (ctbClass, instance, ctbClassData, me);	long size = REALMemoryBlockGetSize (mb);	if (size != 0 and (amount < 0 or amount > size)) amount = size;	long actCnt = 0;	if (amount != 0) {		me->lastError = ctb_write (me->connHdl, false, (Ptr)REALMemoryBlockGetPtr(mb), amount, &actCnt, isEOM, me->timeout);	} else {		me->lastError = 0;	}	return actCnt;}static RBinteger writeMem1 (REALobject instance, REALmemoryBlock mb, RBinteger amount){	return writeMem2 (instance, mb, amount, false);}static void setTimeout (REALobject instance, RBinteger to){	ClassData (ctbClass, instance, ctbClassData, me);	me->timeout = to;}static void clrErr (REALobject instance){	ClassData (ctbClass, instance, ctbClassData, me);	me->lastError = 0;}static REALmethodDefinition ctbClassMethods[] = {	{ (REALproc) clrErr, REALnoImplementation, "ClearError()"},	{ (REALproc) selTool, REALnoImplementation, "SelectTool(toolName as String)"},	{ (REALproc) openConn, REALnoImplementation, "OpenConnection(showAlerts as Boolean)"},	{ (REALproc) configTool, REALnoImplementation, "ConfigureTool(configStr as String)"},	{ (REALproc) setTimeout, REALnoImplementation, "SetTimeOut(seconds as Integer)"},	{ (REALproc) idle, REALnoImplementation, "Idle()"},	{ (REALproc) ctblisten, REALnoImplementation, "Listen()"},	{ (REALproc) ctbaccept, REALnoImplementation, "Accept(accepted as Boolean)"},	{ (REALproc) close, REALnoImplementation, "Close()"},	{ (REALproc) status, REALnoImplementation, "GetStatus()"},	{ (REALproc) readStr, REALnoImplementation, "Read(byteCnt as Integer) as String"},	{ (REALproc) readMem, REALnoImplementation, "Read(buffer as MemoryBlock, byteCnt as Integer) as Integer"},	{ (REALproc) writeStr1, REALnoImplementation, "Write(data as String) as Integer"},	{ (REALproc) writeStr2, REALnoImplementation, "Write(data as String, endOfMsg as Boolean) as Integer"},	{ (REALproc) writeMem1, REALnoImplementation, "Write(data as MemoryBlock, byteCnt as Integer) as Integer"},	{ (REALproc) writeMem2, REALnoImplementation, "Write(data as MemoryBlock, byteCnt as Integer, endOfMsg as Boolean) as Integer"},};static REALproperty ctbClassProperties[] = {	{ nil, "Error", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, lastError) },	{ nil, "ToolID", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, toolID) },	{ nil, "ConnectionID", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, connHdl) },	{ nil, "TimeOut", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, timeout) },	{ nil, "EndOfMsg", "Boolean", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, isEOM) },	{ nil, "DataAvailable", "Boolean", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, stat.dataAvail) },	{ nil, "Listening", "Boolean", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, stat.listening) },	{ nil, "IncomingCall", "Boolean", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, stat.incomingCall) },	{ nil, "Status", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, stat.status) },	{ nil, "DataInAmount", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, stat.dataInAmt) },	{ nil, "DataOutAmount", "Integer", 0, REALstandardGetter, REALnoImplementation, FieldOffset(ctbClassData, stat.dataOutAmt) },};static REALclassDefinition ctbClass = {	kCurrentREALControlVersion,	"TTsCTBConnection", nil,	sizeof(ctbClassData), 0,	(REALproc) ctbClassConstructor, (REALproc) ctbClassDestructor,	ctbClassProperties,	sizeof(ctbClassProperties) / sizeof(REALproperty),	ctbClassMethods,	sizeof(ctbClassMethods) / sizeof(REALmethodDefinition),	/*	ctbClassEvents,	sizeof(ctbClassEvents) / sizeof(REALevent),	*/	nil, 0};// -- automatic idle task handling --#if TARGET_API_MAC_OS8	static void theEventFilterHandler (EventRecord *event, long)	{		// call active connections' Idle handlers:		ListCarrier *r = &gRoot;		ListCarrier *l = r->next;		while (l != r) {			ClassData (ctbClass, l->obj, ctbClassData, me);			if (me->connHdl) {				ctb_idle (me->connHdl);			}			l = l->next;		}	}#endif// -- plugin init/exit --void PluginExit (void){	// call left over constructors (if RB's IDE kills a running app, it may leave objects behind)	ListCarrier *r = &gRoot;	ListCarrier *l = r->next;	while (l != r) {		ctbClassDestructor (l->obj);		l = l->next;	}}void PluginEntry (void){	InitTTsPluginSupport ();	gRoot.prev = &gRoot;	gRoot.next = &gRoot;	REALRegisterClass (&ctbClass);	#if TARGET_API_MAC_OS8		REALRegisterEventFilter (theEventFilterHandler, 0);	#endif}