/*********************************************************************
Project	:	GUSI				-	Grand Unified Socket Interface
File		:	GUSI_P.h			-	Private stuff
Author	:	Matthias Neeracher
Language	:	MPW C/C++

$Log$
Revision 1.1  1998/08/18 14:52:33  jack
Putting Python-specific GUSI modifications under CVS.

Revision 1.3  1994/12/31  01:30:26  neeri
Reorganize filename dispatching.

Revision 1.2  1994/08/10  00:41:05  neeri
Sanitized for universal headers.

Revision 1.1  1994/02/25  02:57:01  neeri
Initial revision

Revision 0.22  1993/07/17  00:00:00  neeri
GUSIRingBuffer::proc -> defproc

Revision 0.21  1993/07/17  00:00:00  neeri
GUSIO_MAX_DOMAIN -> AF_MAX

Revision 0.20  1993/06/27  00:00:00  neeri
Socket::{pre,post}_select

Revision 0.19  1993/06/27  00:00:00  neeri
Socket::ftruncate

Revision 0.18  1993/02/09  00:00:00  neeri
Socket::lurking, Socket::lurkdescr

Revision 0.17  1993/01/31  00:00:00  neeri
GUSIConfiguration::daemon

Revision 0.16  1993/01/17  00:00:00  neeri
Destructors for Socketdomain

Revision 0.15  1993/01/17  00:00:00  neeri
SAFESPIN

Revision 0.14  1993/01/03  00:00:00  neeri
GUSIConfig

Revision 0.13  1992/09/24  00:00:00  neeri
Include GUSIRsrc_P.h

Revision 0.12  1992/09/13  00:00:00  neeri
SPINVOID didn't return

Revision 0.11  1992/08/30  00:00:00  neeri
AppleTalkIdentity()

Revision 0.10  1992/08/03  00:00:00  neeri
RingBuffer

Revision 0.9  1992/07/30  00:00:00  neeri
Initializer Features

Revision 0.8  1992/07/26  00:00:00  neeri
UnixSockets.choose()

Revision 0.7  1992/07/13  00:00:00  neeri
Make AppleTalkSockets global

Revision 0.6  1992/06/27  00:00:00  neeri
choose(), hasNewSF

Revision 0.5  1992/06/07  00:00:00  neeri
Feature

Revision 0.4  1992/05/21  00:00:00  neeri
Implemented select()

Revision 0.3  1992/04/19  00:00:00  neeri
C++ rewrite

Revision 0.2  1992/04/18  00:00:00  neeri
changed read/write/send/recv dispatchers

Revision 0.1  1992/04/18  00:00:00  neeri
ppc Domain

*********************************************************************/

#ifndef __GUSI_P__
#define __GUSI_P__

#define __useAppleExts__

#include <GUSI.h>
#include <GUSIRsrc_P.h>
#include <TFileSpec.h>


#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/socket.h>

extern "C" {

#include <stdio.h>
#include <string.h>

int 		GUSI_error(int err);
void *	GUSI_error_nil(int err);
}

#include <Memory.h>
#include <Gestalt.h>
#include <Traps.h>
#include <AppleEvents.h>
#include <Processes.h>
#include <MixedMode.h>

#if MSLGUSI
using namespace std;
#endif

#if GENERATING68K
#pragma segment GUSI
#endif

#define GUSI_MAX_DOMAIN			AF_MAX
#define DEFAULT_BUFFER_SIZE	4096

/*
 *	In use and shutdown status.
 */
#define	SOCK_STATUS_USED		0x1		/* Used socket table entry */
#define	SOCK_STATUS_NOREAD	0x2		/* No more reading allowed from socket */
#define	SOCK_STATUS_NOWRITE	0x4		/* No more writing allowed to socket */

/*
 *	Socket connection states.
 */
#define	SOCK_STATE_NO_STREAM		0	/* Socket doesn't have a MacTCP stream yet */
#define	SOCK_STATE_UNCONNECTED	1	/* Socket is unconnected. */
#define	SOCK_STATE_LISTENING		2	/* Socket is listening for connection. */
#define	SOCK_STATE_LIS_CON		3	/* Socket is in transition from listen to connected. */
#define	SOCK_STATE_CONNECTING	4	/* Socket is initiating a connection. */
#define	SOCK_STATE_CONNECTED		5	/* Socket is connected. */
#define	SOCK_STATE_CLOSING      6	/* Socket is closing */
#define	SOCK_STATE_LIS_CLOSE    7	/* Socket closed while listening */

#define		min(a,b)				( (a) < (b) ? (a) : (b))
#define		max(a,b)				( (a) > (b) ? (a) : (b))

extern GUSISpinFn GUSISpin;
extern "C" int GUSIDefaultSpin(spin_msg, long);
extern int GUSICheckAlarm();

#define GUSI_INTERRUPT(mesg,param)	(GUSICheckAlarm() || (GUSISpin && (*GUSISpin)(mesg,param)))

/* SPIN returns a -1 on user cancel for fn returning integers */
#define		SPIN(cond,mesg,param)							\
					do {												\
						if (GUSI_INTERRUPT(mesg,param))		\
							return GUSI_error(EINTR);			\
					} while(cond)

/* SPINP returns a NULL on user cancel, for fn returning pointers */				
#define		SPINP(cond,mesg,param)							\
					do {												\
						if (GUSI_INTERRUPT(mesg,param)) {	\
							GUSI_error(EINTR);					\
							return NULL;							\
						}												\
					} while(cond)

/* SPINVOID just returns on user cancel, for fn returning void */				
#define		SPINVOID(cond,mesg,param)						\
					do {												\
						if (GUSI_INTERRUPT(mesg,param)) {	\
								GUSI_error(EINTR);				\
								return;								\
							}											\
					} while(cond)
					
/* SAFESPIN doesn't return, you have to check errno */				
#define		SAFESPIN(cond,mesg,param)						\
					do {												\
						if (GUSI_INTERRUPT(mesg,param)) {	\
							GUSI_error(EINTR);					\
							break;									\
						} else										\
							errno = 0;								\
					} while(cond)

//
// Library functions are never allowed to clear errno, so we have to save
//
class ErrnoSaver {
public:
	ErrnoSaver()  { fSavedErrno = ::errno; ::errno = 0; 	}
	~ErrnoSaver() { if (!::errno) ::errno = fSavedErrno;  }
private:
	int fSavedErrno;
};

#define SAVE_AND_CLEAR_ERRNO	ErrnoSaver saveErrno
			
class SocketTable;

#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=mac68k
#endif

class Socket {
	friend class SocketTable;
	
	short			refCount;
protected:
					Socket();
public:
	virtual int	bind(void * name, int namelen);
	virtual int connect(void * address, int addrlen);
	virtual int listen(int qlen);
	virtual Socket * accept(void * address, int * addrlen);
	virtual int	read(void * buffer, int buflen);
	virtual int write(void * buffer, int buflen);
	virtual int recvfrom(void * buffer, int buflen, int flags, void * from, int * fromlen);
	virtual int sendto(void * buffer, int buflen, int flags, void * to, int tolen);
	virtual int getsockname(void * name, int * namelen);
	virtual int getpeername(void * name, int * namelen);
	virtual int getsockopt(int level, int optname, void *optval, int * optlen);
	virtual int setsockopt(int level, int optname, void *optval, int optlen);
	virtual int	fcntl(unsigned int cmd, int arg);
	virtual int	ioctl(unsigned int request, void *argp);
	virtual int	fstat(struct stat * buf);
	virtual long lseek(long offset, int whence);
	virtual int ftruncate(long offset);
	virtual int	isatty();
	virtual int shutdown(int how);
	virtual void pre_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
	virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
	virtual void post_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
	virtual 		~Socket();
	
	void operator++()	{	++refCount;							}
	void operator--()	{	if (!--refCount) delete this;	}
};


#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=reset
#endif

class SocketDomain {
	static SocketDomain *		domains[GUSI_MAX_DOMAIN];
	static ProcessSerialNumber	process;
protected:
	SocketDomain(int domain);
	virtual ~SocketDomain();
public:
	inline static SocketDomain *	Domain(int domain);
	static void Ready();
	
	// Optionally override the following
	
	virtual Socket * socket(int type, short protocol);
	
	// Optionally override the following
	
	virtual int socketpair(int type, short protocol, Socket * sockets[]);
	
	// Optionally define the following
	
	virtual int choose(
						int 		type, 
						char * 	prompt, 
						void * 	constraint,		
						int 		flags,
 						void * 	name, 
						int * 	namelen);
	
	// Never override the following
	
	void DontStrip();
};

class SocketTable {
	Socket *	sockets[GUSI_MAX_FD];
	Boolean	needsConsole;
public:
	SocketTable();
	~SocketTable();
	
	void		InitConsole();
	int		Install(Socket * sock, int start = 0);
	int		Remove(int fd);
	Socket * operator[](int fd);
};

struct GUSISuffix {
	char 		suffix[4];
	OSType	suffType;
	OSType	suffCreator;
};

#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=mac68k
#endif

//
// I learned the hard way not to rely on bit field alignments
//

struct GUSIConfigRsrc {
	OSType			defaultType;
	OSType			defaultCreator;
	
	char				autoSpin;
	unsigned char	flags;
	
	OSType			version;
	short				numSuffices;
	GUSISuffix 		suffices[1];
};

#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=reset
#endif

struct GUSIConfiguration {
	OSType			defaultType;
	OSType			defaultCreator;
	
	char				autoSpin;
	
	Boolean	 		noChdir;		// Set current directory without chdir()
	Boolean 			accurStat;	// Return # of subdirectories + 2 in st_nlink
	Boolean	 		hasConsole;	// Do we have our own console ?
	Boolean			noAutoInitGraf;	// Never automatically do InitGraf
	Boolean			sharedOpen;	// Open files with shared permissions
	Boolean			sigPipe;		// raise SIGPIPE on write to closed socket
	Boolean			noAppleEvents; // Don't solicit AppleEvents for MPW tools
	Boolean			delayConsole;	// Do not open console until needed
	
	OSType			version;
	short				numSuffices;
	GUSISuffix *	suffices;
	
	GUSIConfiguration();
	void GUSILoadConfiguration(Handle config);
	
	void SetDefaultFType(const TFileSpec & name) const;
	void DoAutoSpin() const;
	void AutoInitGraf()	const {	if (!noAutoInitGraf) DoAutoInitGraf();	}
	void DoAutoInitGraf() const;
	Boolean DelayConsole() const;
private:
	static Boolean firstTime;
	static short	we;
};

extern GUSIConfiguration	GUSIConfig;
extern SocketTable					Sockets;

typedef pascal OSErr (*OSErrInitializer)();
typedef pascal void  (*voidInitializer)();

class Feature {
	Boolean	good;
public:
	Feature(unsigned short trapNum, TrapType tTyp);
	Feature(OSType type, long value);
	Feature(OSType type, long mask, long value);
	Feature(const Feature & precondition, OSErrInitializer init);
	Feature(OSErrInitializer init);
	Feature(const Feature & precondition, voidInitializer init);
	Feature(voidInitializer init);
	Feature(const Feature & cond1, const Feature & cond2);

	operator void*() const {	return (void *) good;	}
};

extern Feature hasMakeFSSpec;
extern Feature hasAlias;
extern Feature hasNewSF;
extern Feature hasProcessMgr;
extern Feature hasCRM;
extern Feature hasCTB;
extern Feature hasStdNBP;
extern Feature hasCM;
extern Feature hasFT;
extern Feature hasTM;
extern Feature	hasPPC;
extern Feature hasRevisedTimeMgr;

class ScattGath	{
	Handle			scratch;
protected:
	void *			buf;
	int						len;
	int						count;
	const struct iovec *	io;

	ScattGath(const struct iovec *iov, int cnt);
	virtual ~ScattGath();
public:
	void *			buffer()			{	return buf;			}
	int				buflen()			{	return len;			}
	int				length(int l)	{	return len = l;	}
	operator void *()					{	return buf;			}
};

class Scatterer : public ScattGath {
public:
	Scatterer(const struct iovec *iov, int count);
	virtual ~Scatterer();
};

class Gatherer : public ScattGath {
public:
	Gatherer(const struct iovec *iov, int count);
	virtual ~Gatherer();
};

typedef pascal void (*Deferred)(void *);

class RingBuffer {
	// Valid bytes are between consume and produce
	// Free bytes are between produce and consume
	// bytes between endbuf-spare and endbuf are neither
	Ptr		buffer;
	Ptr		endbuf;
	Ptr 		consume;
	Ptr		produce;
	u_short	free;
	u_short	valid;
	u_short	spare;
	Boolean	lock;
	Deferred	defproc;
	void *	arg;
	
public:
				RingBuffer(u_short bufsiz);
				~RingBuffer();
	
	Ptr		Producer(long & len);			//	Find continuous memory for producer
	Ptr		Consumer(long & len);			//	Find continuous memory for consumer
	void		Validate(long len);				// Validate this, unallocate rest
	void 		Invalidate(long len);
	void		Produce(Ptr from, long & len);//	Allocate, copy & validate
	void		Consume(Ptr to, long & len);	// Copy & invalidate
	
	long		Free()								{ return free;									}		
	long		Valid()								{ return valid;								}
	
	void 		Defer()								{ lock = true;									}
	void 		Undefer()							{ lock = false; if (defproc) defproc(arg);}
	Boolean	Locked()								{ return lock;									}
	void		Later(Deferred def, void * ar){ defproc = def; arg = ar;					}
	
	operator void *()								{ return buffer;								}
};

Boolean GUSIInterrupt();

Boolean CopyIconFamily(short srcResFile, short srcID, short dstResFile, short dstID);

pascal OSErr PPCInit_P();

OSErr AppleTalkIdentity(short & net, short & node);

void CopyC2PStr(const char * cstr, StringPtr pstr);

#endif