os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacSock.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacSock.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,2790 @@
     1.4 +/* 
     1.5 + * tclMacSock.c
     1.6 + *
     1.7 + *	Channel drivers for Macintosh sockets.
     1.8 + *
     1.9 + * Copyright (c) 1996-1997 Sun Microsystems, Inc.
    1.10 + *
    1.11 + * See the file "license.terms" for information on usage and redistribution
    1.12 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.13 + *
    1.14 + * RCS: @(#) $Id: tclMacSock.c,v 1.14.2.1 2006/03/10 14:27:41 vasiljevic Exp $
    1.15 + */
    1.16 +
    1.17 +#include "tclInt.h"
    1.18 +#include "tclPort.h"
    1.19 +#include "tclMacInt.h"
    1.20 +#include <AddressXlation.h>
    1.21 +#include <Aliases.h>
    1.22 +#undef Status
    1.23 +#include <Devices.h>
    1.24 +#include <Errors.h>
    1.25 +#include <Events.h>
    1.26 +#include <Files.h>
    1.27 +#include <Gestalt.h>
    1.28 +#include <MacTCP.h>
    1.29 +#include <Processes.h>
    1.30 +#include <Strings.h>
    1.31 +
    1.32 +/*
    1.33 + * The following variable is used to tell whether this module has been
    1.34 + * initialized.
    1.35 + */
    1.36 +
    1.37 +static int initialized = 0;
    1.38 +
    1.39 +/*
    1.40 + * If debugging is on we may drop into the debugger to handle certain cases
    1.41 + * that are not supposed to happen.  Otherwise, we change ignore the error
    1.42 + * and most code should handle such errors ok.
    1.43 + */
    1.44 +
    1.45 +#ifndef TCL_DEBUG
    1.46 +    #define Debugger()
    1.47 +#endif
    1.48 +
    1.49 +/*
    1.50 + * The preferred buffer size for Macintosh channels.
    1.51 + */
    1.52 +
    1.53 +#define CHANNEL_BUF_SIZE	8192
    1.54 +
    1.55 +/*
    1.56 + * Port information structure.  Used to match service names
    1.57 + * to a Tcp/Ip port number.
    1.58 + */
    1.59 +
    1.60 +typedef struct {
    1.61 +    char *name;			/* Name of service. */
    1.62 +    int port;			/* Port number. */
    1.63 +} PortInfo;
    1.64 +
    1.65 +/*
    1.66 + * This structure describes per-instance state of a tcp based channel.
    1.67 + */
    1.68 +
    1.69 +typedef struct TcpState {
    1.70 +    TCPiopb pb;			   /* Parameter block used by this stream. 
    1.71 +				    * This must be in the first position. */
    1.72 +    ProcessSerialNumber	psn;	   /* PSN used to wake up process. */
    1.73 +    StreamPtr tcpStream;	   /* Macintosh tcp stream pointer. */
    1.74 +    int port;			   /* The port we are connected to. */
    1.75 +    int flags;			   /* Bit field comprised of the flags
    1.76 +				    * described below.  */
    1.77 +    int checkMask;		   /* OR'ed combination of TCL_READABLE and
    1.78 +				    * TCL_WRITABLE as set by an asynchronous
    1.79 +				    * event handler. */
    1.80 +    int watchMask;		   /* OR'ed combination of TCL_READABLE and
    1.81 +				    * TCL_WRITABLE as set by TcpWatch. */
    1.82 +    Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */
    1.83 +    ClientData acceptProcData;	   /* The data for the accept proc. */
    1.84 +    wdsEntry dataSegment[2];       /* List of buffers to be written async. */
    1.85 +    rdsEntry rdsarray[5+1];	   /* Array used when cleaning out recieve 
    1.86 +				    * buffers on a closing socket. */
    1.87 +    Tcl_Channel channel;	   /* Channel associated with this socket. */
    1.88 +    int writeBufferSize;           /* Size of buffer to hold data for
    1.89 +                                    *  asynchronous writes. */
    1.90 +    void *writeBuffer;             /* Buffer for async write data. */
    1.91 +    struct TcpState *nextPtr;	   /* The next socket on the global socket
    1.92 +				    * list. */
    1.93 +} TcpState;
    1.94 +
    1.95 +/*
    1.96 + * This structure is used by domain name resolver callback.
    1.97 + */
    1.98 +
    1.99 +typedef struct DNRState {
   1.100 +    struct hostInfo hostInfo;	/* Data structure used by DNR functions. */
   1.101 +    int done;			/* Flag to determine when we are done. */
   1.102 +    ProcessSerialNumber psn;	/* Process to wake up when we are done. */
   1.103 +} DNRState;
   1.104 +
   1.105 +/*
   1.106 + * The following macros may be used to set the flags field of
   1.107 + * a TcpState structure.
   1.108 + */
   1.109 +
   1.110 +#define TCP_ASYNC_SOCKET	(1<<0)  /* The socket is in async mode. */
   1.111 +#define TCP_ASYNC_CONNECT	(1<<1)  /* The socket is trying to connect. */
   1.112 +#define TCP_CONNECTED		(1<<2)  /* The socket is connected. */
   1.113 +#define TCP_PENDING		(1<<3)	/* A SocketEvent is on the queue. */
   1.114 +#define TCP_LISTENING 		(1<<4)  /* This socket is listening for
   1.115 +					 * a connection. */
   1.116 +#define TCP_LISTEN_CONNECT 	(1<<5)  /* Someone has connect to the
   1.117 +					 * listening port. */
   1.118 +#define TCP_REMOTE_CLOSED 	(1<<6)  /* The remote side has closed
   1.119 +					 * the connection. */
   1.120 +#define TCP_RELEASE	 	(1<<7)  /* The socket may now be released. */
   1.121 +#define TCP_WRITING		(1<<8)  /* A background write is in progress. */
   1.122 +#define TCP_SERVER_ZOMBIE	(1<<9)  /* The server can no longer accept connects. */
   1.123 +
   1.124 +/*
   1.125 + * The following structure is what is added to the Tcl event queue when
   1.126 + * a socket event occurs.
   1.127 + */
   1.128 +
   1.129 +typedef struct SocketEvent {
   1.130 +    Tcl_Event header;		/* Information that is standard for
   1.131 +				 * all events. */
   1.132 +    TcpState *statePtr;		/* Socket descriptor that is ready. */
   1.133 +    StreamPtr tcpStream;	/* Low level Macintosh stream. */
   1.134 +} SocketEvent;
   1.135 +
   1.136 +/*
   1.137 + * Static routines for this file:
   1.138 + */
   1.139 +
   1.140 +static pascal void	CleanUpExitProc _ANSI_ARGS_((void));
   1.141 +static void		ClearZombieSockets _ANSI_ARGS_((void));
   1.142 +static void		CloseCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));
   1.143 +static TcpState *	CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
   1.144 +			    int port, CONST char *host, CONST char *myAddr,
   1.145 +			    int myPort, int server, int async));
   1.146 +static pascal void	DNRCompletionRoutine _ANSI_ARGS_((
   1.147 +			    struct hostInfo *hostinfoPtr,
   1.148 +			    DNRState *dnrStatePtr));
   1.149 +static void		FreeSocketInfo _ANSI_ARGS_((TcpState *statePtr));
   1.150 +static long		GetBufferSize _ANSI_ARGS_((void));
   1.151 +static OSErr		GetHostFromString _ANSI_ARGS_((CONST char *name,
   1.152 +			    ip_addr *address));
   1.153 +static OSErr		GetLocalAddress _ANSI_ARGS_((unsigned long *addr));
   1.154 +static void		IOCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));
   1.155 +static void		InitMacTCPParamBlock _ANSI_ARGS_((TCPiopb *pBlock,
   1.156 +			    int csCode));
   1.157 +static void		InitSockets _ANSI_ARGS_((void));
   1.158 +static TcpState *	NewSocketInfo _ANSI_ARGS_((StreamPtr stream));
   1.159 +static OSErr		ResolveAddress _ANSI_ARGS_((ip_addr tcpAddress,
   1.160 +			    Tcl_DString *dsPtr));
   1.161 +static void		SocketCheckProc _ANSI_ARGS_((ClientData clientData,
   1.162 +			    int flags));
   1.163 +static int		SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
   1.164 +			    int flags));
   1.165 +static void		SocketFreeProc _ANSI_ARGS_((ClientData clientData));
   1.166 +static int		SocketReady _ANSI_ARGS_((TcpState *statePtr));
   1.167 +static void		SocketSetupProc _ANSI_ARGS_((ClientData clientData,
   1.168 +			    int flags));
   1.169 +static void		TcpAccept _ANSI_ARGS_((TcpState *statePtr));
   1.170 +static int		TcpBlockMode _ANSI_ARGS_((ClientData instanceData, int mode));
   1.171 +static int		TcpClose _ANSI_ARGS_((ClientData instanceData,
   1.172 +			    Tcl_Interp *interp));
   1.173 +static int		TcpGetHandle _ANSI_ARGS_((ClientData instanceData,
   1.174 +		            int direction, ClientData *handlePtr));
   1.175 +static int		TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
   1.176 +                            Tcl_Interp *interp, CONST char *optionName,
   1.177 +			    Tcl_DString *dsPtr));
   1.178 +static int		TcpInput _ANSI_ARGS_((ClientData instanceData,
   1.179 +			    char *buf, int toRead, int *errorCodePtr));
   1.180 +static int		TcpOutput _ANSI_ARGS_((ClientData instanceData,
   1.181 +			    CONST char *buf, int toWrite, int *errorCodePtr));
   1.182 +static void		TcpWatch _ANSI_ARGS_((ClientData instanceData,
   1.183 +		            int mask));
   1.184 +static int		WaitForSocketEvent _ANSI_ARGS_((TcpState *infoPtr,
   1.185 +		            int mask, int *errorCodePtr));
   1.186 +
   1.187 +pascal void NotifyRoutine (
   1.188 +    StreamPtr tcpStream,
   1.189 +    unsigned short eventCode,
   1.190 +    Ptr userDataPtr,
   1.191 +    unsigned short terminReason,
   1.192 +    struct ICMPReport *icmpMsg);
   1.193 +    
   1.194 +/*
   1.195 + * This structure describes the channel type structure for TCP socket
   1.196 + * based IO:
   1.197 + */
   1.198 +
   1.199 +static Tcl_ChannelType tcpChannelType = {
   1.200 +    "tcp",			/* Type name. */
   1.201 +    (Tcl_ChannelTypeVersion)TcpBlockMode,		/* Set blocking or
   1.202 +                                 * non-blocking mode.*/
   1.203 +    TcpClose,			/* Close proc. */
   1.204 +    TcpInput,			/* Input proc. */
   1.205 +    TcpOutput,			/* Output proc. */
   1.206 +    NULL,			/* Seek proc. */
   1.207 +    NULL,			/* Set option proc. */
   1.208 +    TcpGetOptionProc,		/* Get option proc. */
   1.209 +    TcpWatch,			/* Initialize notifier. */
   1.210 +    TcpGetHandle		/* Get handles out of channel. */
   1.211 +};
   1.212 +
   1.213 +/*
   1.214 + * Universal Procedure Pointers (UPP) for various callback
   1.215 + * routines used by MacTcp code.
   1.216 + */
   1.217 +
   1.218 +ResultUPP resultUPP = NULL;
   1.219 +TCPIOCompletionUPP completeUPP = NULL;
   1.220 +TCPIOCompletionUPP closeUPP = NULL;
   1.221 +TCPNotifyUPP notifyUPP = NULL;
   1.222 +
   1.223 +/*
   1.224 + * Built-in commands, and the procedures associated with them:
   1.225 + */
   1.226 +
   1.227 +static PortInfo portServices[] = {
   1.228 +    {"echo",		7},
   1.229 +    {"discard",		9},
   1.230 +    {"systat",		11},
   1.231 +    {"daytime",		13},
   1.232 +    {"netstat",		15},
   1.233 +    {"chargen",		19},
   1.234 +    {"ftp-data",	20},
   1.235 +    {"ftp",		21},
   1.236 +    {"telnet",		23},
   1.237 +    {"telneto",		24},
   1.238 +    {"smtp",		25},
   1.239 +    {"time",		37},
   1.240 +    {"whois",		43},
   1.241 +    {"domain",		53},
   1.242 +    {"gopher",		70},
   1.243 +    {"finger",		79},
   1.244 +    {"hostnames",	101},
   1.245 +    {"sunrpc",		111},
   1.246 +    {"nntp",		119},
   1.247 +    {"exec",		512},
   1.248 +    {"login",		513},
   1.249 +    {"shell",		514},
   1.250 +    {"printer",		515},
   1.251 +    {"courier",		530},
   1.252 +    {"uucp",		540},
   1.253 +    {NULL,		0},
   1.254 +};
   1.255 +
   1.256 +typedef struct ThreadSpecificData {
   1.257 +    /*
   1.258 +     * Every open socket has an entry on the following list.
   1.259 +     */
   1.260 +    
   1.261 +    TcpState *socketList;
   1.262 +} ThreadSpecificData;
   1.263 +
   1.264 +static Tcl_ThreadDataKey dataKey;
   1.265 +
   1.266 +/*
   1.267 + * Globals for holding information about OS support for sockets.
   1.268 + */
   1.269 +
   1.270 +static int socketsTestInited = false;
   1.271 +static int hasSockets = false;
   1.272 +static short driverRefNum = 0;
   1.273 +static int socketNumber = 0;
   1.274 +static int socketBufferSize = CHANNEL_BUF_SIZE;
   1.275 +static ProcessSerialNumber applicationPSN;
   1.276 +
   1.277 +/*
   1.278 + *----------------------------------------------------------------------
   1.279 + *
   1.280 + * InitSockets --
   1.281 + *
   1.282 + *	Load the MacTCP driver and open the name resolver.  We also
   1.283 + *	create several UPP's used by our code.  Lastly, we install
   1.284 + *	a patch to ExitToShell to clean up socket connections if
   1.285 + *	we are about to exit.
   1.286 + *
   1.287 + * Results:
   1.288 + *	1 if successful, 0 on failure.
   1.289 + *
   1.290 + * Side effects:
   1.291 + *	Creates a new event source, loads the MacTCP driver,
   1.292 + *	registers an exit to shell callback.
   1.293 + *
   1.294 + *----------------------------------------------------------------------
   1.295 + */
   1.296 +
   1.297 +#define gestaltMacTCPVersion 'mtcp'
   1.298 +static void
   1.299 +InitSockets()
   1.300 +{
   1.301 +    ParamBlockRec pb; 
   1.302 +    OSErr err;
   1.303 +    long response;
   1.304 +    ThreadSpecificData *tsdPtr;
   1.305 +    
   1.306 +    if (! initialized) {
   1.307 +	/*
   1.308 +	 * Do process wide initialization.
   1.309 +	 */
   1.310 +
   1.311 +	initialized = 1;
   1.312 +	    
   1.313 +	if (Gestalt(gestaltMacTCPVersion, &response) == noErr) {
   1.314 +	    hasSockets = true;
   1.315 +	} else {
   1.316 +	    hasSockets = false;
   1.317 +	}
   1.318 +    
   1.319 +	if (!hasSockets) {
   1.320 +	    return;
   1.321 +	}
   1.322 +    
   1.323 +	/*
   1.324 +	 * Load MacTcp driver and name server resolver.
   1.325 +	 */
   1.326 +	    
   1.327 +		    
   1.328 +	pb.ioParam.ioCompletion = 0L; 
   1.329 +	pb.ioParam.ioNamePtr = "\p.IPP"; 
   1.330 +	pb.ioParam.ioPermssn = fsCurPerm; 
   1.331 +	err = PBOpenSync(&pb); 
   1.332 +	if (err != noErr) {
   1.333 +	    hasSockets = 0;
   1.334 +	    return;
   1.335 +	}
   1.336 +	driverRefNum = pb.ioParam.ioRefNum; 
   1.337 +	    
   1.338 +	socketBufferSize = GetBufferSize();
   1.339 +	err = OpenResolver(NULL);
   1.340 +	if (err != noErr) {
   1.341 +	    hasSockets = 0;
   1.342 +	    return;
   1.343 +	}
   1.344 +    
   1.345 +	GetCurrentProcess(&applicationPSN);
   1.346 +	/*
   1.347 +	 * Create UPP's for various callback routines.
   1.348 +	 */
   1.349 +    
   1.350 +	resultUPP = NewResultProc(DNRCompletionRoutine);
   1.351 +	completeUPP = NewTCPIOCompletionProc(IOCompletionRoutine);
   1.352 +	closeUPP = NewTCPIOCompletionProc(CloseCompletionRoutine);
   1.353 +	notifyUPP = NewTCPNotifyProc(NotifyRoutine);
   1.354 +    
   1.355 +	/*
   1.356 +	 * Install an ExitToShell patch.  We use this patch instead
   1.357 +	 * of the Tcl exit mechanism because we need to ensure that
   1.358 +	 * these routines are cleaned up even if we crash or are forced
   1.359 +	 * to quit.  There are some circumstances when the Tcl exit
   1.360 +	 * handlers may not fire.
   1.361 +	 */
   1.362 +    
   1.363 +	TclMacInstallExitToShellPatch(CleanUpExitProc);
   1.364 +    }
   1.365 +
   1.366 +    /*
   1.367 +     * Do per-thread initialization.
   1.368 +     */
   1.369 +
   1.370 +    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   1.371 +    if (tsdPtr == NULL) {
   1.372 +	tsdPtr = TCL_TSD_INIT(&dataKey);
   1.373 +	tsdPtr->socketList = NULL;
   1.374 +	Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
   1.375 +    }
   1.376 +}
   1.377 +
   1.378 +/*
   1.379 + *----------------------------------------------------------------------
   1.380 + *
   1.381 + * TclpFinalizeSockets --
   1.382 + *
   1.383 + *	Invoked during exit clean up to deinitialize the socket module.
   1.384 + *
   1.385 + * Results:
   1.386 + *	None.
   1.387 + *
   1.388 + * Side effects:
   1.389 + *	Removed event source.
   1.390 + *
   1.391 + *----------------------------------------------------------------------
   1.392 + */
   1.393 +
   1.394 +void
   1.395 +TclpFinalizeSockets()
   1.396 +{
   1.397 +    ThreadSpecificData *tsdPtr;
   1.398 +
   1.399 +    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   1.400 +    if (tsdPtr != NULL) {
   1.401 +	Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
   1.402 +    }
   1.403 +}
   1.404 +
   1.405 +/*
   1.406 + *----------------------------------------------------------------------
   1.407 + *
   1.408 + * TclpHasSockets --
   1.409 + *
   1.410 + *	This function determines whether sockets are available on the
   1.411 + *	current system and returns an error in interp if they are not.
   1.412 + *	Note that interp may be NULL.
   1.413 + *
   1.414 + * Results:
   1.415 + *	Returns TCL_OK if the system supports sockets, or TCL_ERROR with
   1.416 + *	an error in interp.
   1.417 + *
   1.418 + * Side effects:
   1.419 + *	None.
   1.420 + *
   1.421 + *----------------------------------------------------------------------
   1.422 + */
   1.423 +
   1.424 +int
   1.425 +TclpHasSockets(
   1.426 +    Tcl_Interp *interp)		/* Interp for error messages. */
   1.427 +{
   1.428 +    InitSockets();
   1.429 +
   1.430 +    if (hasSockets) {
   1.431 +	return TCL_OK;
   1.432 +    }
   1.433 +    if (interp != NULL) {
   1.434 +	Tcl_AppendResult(interp, "sockets are not available on this system",
   1.435 +		NULL);
   1.436 +    }
   1.437 +    return TCL_ERROR;
   1.438 +}
   1.439 +
   1.440 +/*
   1.441 + *----------------------------------------------------------------------
   1.442 + *
   1.443 + * SocketSetupProc --
   1.444 + *
   1.445 + *	This procedure is invoked before Tcl_DoOneEvent blocks waiting
   1.446 + *	for an event.
   1.447 + *
   1.448 + * Results:
   1.449 + *	None.
   1.450 + *
   1.451 + * Side effects:
   1.452 + *	Adjusts the block time if needed.
   1.453 + *
   1.454 + *----------------------------------------------------------------------
   1.455 + */
   1.456 +
   1.457 +static void
   1.458 +SocketSetupProc(
   1.459 +    ClientData data,		/* Not used. */
   1.460 +    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
   1.461 +{
   1.462 +    TcpState *statePtr;
   1.463 +    Tcl_Time blockTime = { 0, 0 };
   1.464 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.465 +
   1.466 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.467 +	return;
   1.468 +    }
   1.469 +    
   1.470 +    /*
   1.471 +     * Check to see if there is a ready socket.  If so, poll.
   1.472 +     */
   1.473 +
   1.474 +    for (statePtr = tsdPtr->socketList; statePtr != NULL;
   1.475 +	    statePtr = statePtr->nextPtr) {
   1.476 +	if (statePtr->flags & TCP_RELEASE) {
   1.477 +	    continue;
   1.478 +	}
   1.479 +	if (SocketReady(statePtr)) {
   1.480 +	    Tcl_SetMaxBlockTime(&blockTime);
   1.481 +	    break;
   1.482 +	}
   1.483 +    }
   1.484 +}
   1.485 +
   1.486 +/*
   1.487 + *----------------------------------------------------------------------
   1.488 + *
   1.489 + * SocketCheckProc --
   1.490 + *
   1.491 + *	This procedure is called by Tcl_DoOneEvent to check the socket
   1.492 + *	event source for events. 
   1.493 + *
   1.494 + * Results:
   1.495 + *	None.
   1.496 + *
   1.497 + * Side effects:
   1.498 + *	May queue an event.
   1.499 + *
   1.500 + *----------------------------------------------------------------------
   1.501 + */
   1.502 +
   1.503 +static void
   1.504 +SocketCheckProc(
   1.505 +    ClientData data,		/* Not used. */
   1.506 +    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
   1.507 +{
   1.508 +    TcpState *statePtr;
   1.509 +    SocketEvent *evPtr;
   1.510 +    TcpState dummyState;
   1.511 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.512 +
   1.513 +    if (!(flags & TCL_FILE_EVENTS)) {
   1.514 +	return;
   1.515 +    }
   1.516 +    
   1.517 +    /*
   1.518 +     * Queue events for any ready sockets that don't already have events
   1.519 +     * queued (caused by persistent states that won't generate WinSock
   1.520 +     * events).
   1.521 +     */
   1.522 +
   1.523 +    for (statePtr = tsdPtr->socketList; statePtr != NULL;
   1.524 +	    statePtr = statePtr->nextPtr) {
   1.525 +	/*
   1.526 +	 * Check to see if this socket is dead and needs to be cleaned
   1.527 +	 * up.  We use a dummy statePtr whose only valid field is the
   1.528 +	 * nextPtr to allow the loop to continue even if the element
   1.529 +	 * is deleted.
   1.530 +	 */
   1.531 +
   1.532 +	if (statePtr->flags & TCP_RELEASE) {
   1.533 +	    if (!(statePtr->flags & TCP_PENDING)) {
   1.534 +		dummyState.nextPtr = statePtr->nextPtr;
   1.535 +		SocketFreeProc(statePtr);
   1.536 +		statePtr = &dummyState;
   1.537 +	    }
   1.538 +	    continue;
   1.539 +	}
   1.540 +
   1.541 +	if (!(statePtr->flags & TCP_PENDING) && SocketReady(statePtr)) {
   1.542 +	    statePtr->flags |= TCP_PENDING;
   1.543 +	    evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent));
   1.544 +	    evPtr->header.proc = SocketEventProc;
   1.545 +	    evPtr->statePtr = statePtr;
   1.546 +	    evPtr->tcpStream = statePtr->tcpStream;
   1.547 +	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
   1.548 +	}
   1.549 +    }
   1.550 +}
   1.551 +
   1.552 +/*
   1.553 + *----------------------------------------------------------------------
   1.554 + *
   1.555 + * SocketReady --
   1.556 + *
   1.557 + *	This function checks the current state of a socket to see
   1.558 + *	if any interesting conditions are present.
   1.559 + *
   1.560 + * Results:
   1.561 + *	Returns 1 if an event that someone is watching is present, else
   1.562 + *	returns 0.
   1.563 + *
   1.564 + * Side effects:
   1.565 + *	Updates the checkMask for the socket to reflect any newly
   1.566 + *	detected events.
   1.567 + *
   1.568 + *----------------------------------------------------------------------
   1.569 + */
   1.570 +
   1.571 +static int
   1.572 +SocketReady(
   1.573 +    TcpState *statePtr)
   1.574 +{
   1.575 +    TCPiopb statusPB;
   1.576 +    int foundSomething = 0;
   1.577 +    int didStatus = 0;
   1.578 +    int amount;
   1.579 +    OSErr err;
   1.580 +
   1.581 +    if (statePtr->flags & TCP_LISTEN_CONNECT) {
   1.582 +	foundSomething = 1;
   1.583 +	statePtr->checkMask |= TCL_READABLE;
   1.584 +    }
   1.585 +    if (statePtr->watchMask & TCL_READABLE) {
   1.586 +	if (statePtr->checkMask & TCL_READABLE) {
   1.587 +	    foundSomething = 1;
   1.588 +	} else if (statePtr->flags & TCP_CONNECTED) {
   1.589 +	    statusPB.ioCRefNum = driverRefNum;
   1.590 +	    statusPB.tcpStream = statePtr->tcpStream;
   1.591 +	    statusPB.csCode = TCPStatus;
   1.592 +	    err = PBControlSync((ParmBlkPtr) &statusPB);
   1.593 +	    didStatus = 1;
   1.594 +
   1.595 +	    /*
   1.596 +	     * We make the fchannel readable if 1) we get an error,
   1.597 +	     * 2) there is more data available, or 3) we detect
   1.598 +	     * that a close from the remote connection has arrived.
   1.599 +	     */
   1.600 +
   1.601 +	    if ((err != noErr) ||
   1.602 +		    (statusPB.csParam.status.amtUnreadData > 0) ||
   1.603 +		    (statusPB.csParam.status.connectionState == 14)) {
   1.604 +		statePtr->checkMask |= TCL_READABLE;
   1.605 +		foundSomething = 1;
   1.606 +	    }
   1.607 +	}
   1.608 +    }
   1.609 +    if (statePtr->watchMask & TCL_WRITABLE) {
   1.610 +	if (statePtr->checkMask & TCL_WRITABLE) {
   1.611 +	    foundSomething = 1;
   1.612 +	} else if (statePtr->flags & TCP_CONNECTED) {
   1.613 +	    if (!didStatus) {
   1.614 +		statusPB.ioCRefNum = driverRefNum;
   1.615 +		statusPB.tcpStream = statePtr->tcpStream;
   1.616 +		statusPB.csCode = TCPStatus;
   1.617 +		err = PBControlSync((ParmBlkPtr) &statusPB);
   1.618 +	    }
   1.619 +
   1.620 +	    /*
   1.621 +	     * If there is an error or there if there is room to
   1.622 +	     * send more data we make the channel writeable.
   1.623 +	     */
   1.624 +
   1.625 +	    amount = statusPB.csParam.status.sendWindow - 
   1.626 +		statusPB.csParam.status.amtUnackedData;
   1.627 +	    if ((err != noErr) || (amount > 0)) {
   1.628 +		statePtr->checkMask |= TCL_WRITABLE;
   1.629 +		foundSomething = 1;
   1.630 +	    }
   1.631 +	}
   1.632 +    }
   1.633 +    return foundSomething;
   1.634 +}
   1.635 +
   1.636 +/*
   1.637 + *----------------------------------------------------------------------
   1.638 + *
   1.639 + * InitMacTCPParamBlock--
   1.640 + *
   1.641 + *	Initialize a MacTCP parameter block.
   1.642 + *
   1.643 + * Results:
   1.644 + *	None.
   1.645 + *
   1.646 + * Side effects:
   1.647 + *	Initializes the parameter block.
   1.648 + *
   1.649 + *----------------------------------------------------------------------
   1.650 + */
   1.651 +
   1.652 +static void
   1.653 +InitMacTCPParamBlock(
   1.654 +    TCPiopb *pBlock,		/* Tcp parmeter block. */
   1.655 +    int csCode)			/* Tcp operation code. */
   1.656 +{
   1.657 +    memset(pBlock, 0, sizeof(TCPiopb));
   1.658 +    pBlock->ioResult = 1;
   1.659 +    pBlock->ioCRefNum = driverRefNum;
   1.660 +    pBlock->csCode = (short) csCode;
   1.661 +}
   1.662 +
   1.663 +/*
   1.664 + *----------------------------------------------------------------------
   1.665 + *
   1.666 + * TcpBlockMode --
   1.667 + *
   1.668 + *	Set blocking or non-blocking mode on channel.
   1.669 + *
   1.670 + * Results:
   1.671 + *	0 if successful, errno when failed.
   1.672 + *
   1.673 + * Side effects:
   1.674 + *	Sets the device into blocking or non-blocking mode.
   1.675 + *
   1.676 + *----------------------------------------------------------------------
   1.677 + */
   1.678 +
   1.679 +static int
   1.680 +TcpBlockMode(
   1.681 +    ClientData instanceData, 		/* Channel state. */
   1.682 +    int mode)				/* The mode to set. */
   1.683 +{
   1.684 +    TcpState *statePtr = (TcpState *) instanceData;
   1.685 +    
   1.686 +    if (mode == TCL_MODE_BLOCKING) {
   1.687 +	statePtr->flags &= ~TCP_ASYNC_SOCKET;
   1.688 +    } else {
   1.689 +	statePtr->flags |= TCP_ASYNC_SOCKET;
   1.690 +    }
   1.691 +    return 0;
   1.692 +}
   1.693 +
   1.694 +/*
   1.695 + *----------------------------------------------------------------------
   1.696 + *
   1.697 + * TcpClose --
   1.698 + *
   1.699 + *	Close the socket.
   1.700 + *
   1.701 + * Results:
   1.702 + *	0 if successful, the value of errno if failed.
   1.703 + *
   1.704 + * Side effects:
   1.705 + *	Closes the socket.
   1.706 + *
   1.707 + *----------------------------------------------------------------------
   1.708 + */
   1.709 +
   1.710 +static int
   1.711 +TcpClose(
   1.712 +    ClientData instanceData,		/* The socket to close. */
   1.713 +    Tcl_Interp *interp)			/* Interp for error messages. */
   1.714 +{
   1.715 +    TcpState *statePtr = (TcpState *) instanceData;
   1.716 +    StreamPtr tcpStream;
   1.717 +    TCPiopb closePB;
   1.718 +    OSErr err;
   1.719 +
   1.720 +    tcpStream = statePtr->tcpStream;
   1.721 +    statePtr->flags &= ~TCP_CONNECTED;
   1.722 +    
   1.723 +    /*
   1.724 +     * If this is a server socket we can't use the statePtr
   1.725 +     * param block because it is in use.  However, we can 
   1.726 +     * close syncronously.
   1.727 +     */
   1.728 +
   1.729 +    if ((statePtr->flags & TCP_LISTENING) ||
   1.730 +	    (statePtr->flags & TCP_LISTEN_CONNECT)) {
   1.731 +	InitMacTCPParamBlock(&closePB, TCPClose);
   1.732 +    	closePB.tcpStream = tcpStream;
   1.733 +    	closePB.ioCompletion = NULL; 
   1.734 +	closePB.csParam.close.ulpTimeoutValue = 60 /* seconds */;
   1.735 +	closePB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */;
   1.736 +	closePB.csParam.close.validityFlags = timeoutValue | timeoutAction;
   1.737 +    	err = PBControlSync((ParmBlkPtr) &closePB);
   1.738 +    	if (err != noErr) {
   1.739 +    	    Debugger();
   1.740 +    	    goto afterRelease;
   1.741 +            /* panic("error closing server socket"); */
   1.742 +    	}
   1.743 +	statePtr->flags |= TCP_RELEASE;
   1.744 +
   1.745 +	/*
   1.746 +	 * Server sockets are closed sync.  Therefor, we know it is OK to
   1.747 +	 * release the socket now.
   1.748 +	 */
   1.749 +
   1.750 +	InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
   1.751 +	statePtr->pb.tcpStream = statePtr->tcpStream;
   1.752 +	err = PBControlSync((ParmBlkPtr) &statePtr->pb);
   1.753 +	if (err != noErr) {
   1.754 +            panic("error releasing server socket");
   1.755 +	}
   1.756 +
   1.757 +	/*
   1.758 +	 * Free the buffer space used by the socket and the 
   1.759 +	 * actual socket state data structure.
   1.760 +	 */
   1.761 +      afterRelease:
   1.762 +        
   1.763 +        /*
   1.764 +         * Have to check whether the pointer is NULL, since we could get here
   1.765 +         * on a failed socket open, and then the rcvBuff would never have been
   1.766 +         * allocated.
   1.767 +         */
   1.768 +         
   1.769 +        if (err == noErr) {
   1.770 +	    ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
   1.771 +	}
   1.772 +	FreeSocketInfo(statePtr);
   1.773 +	return 0;
   1.774 +    }
   1.775 +
   1.776 +    /*
   1.777 +     * If this socket is in the midddle on async connect we can just
   1.778 +     * abort the connect and release the stream right now.
   1.779 +     */
   1.780 + 
   1.781 +    if (statePtr->flags & TCP_ASYNC_CONNECT) {
   1.782 +	InitMacTCPParamBlock(&closePB, TCPClose);
   1.783 +    	closePB.tcpStream = tcpStream;
   1.784 +    	closePB.ioCompletion = NULL; 
   1.785 +    	err = PBControlSync((ParmBlkPtr) &closePB);
   1.786 +    	if (err == noErr) {
   1.787 +	    statePtr->flags |= TCP_RELEASE;
   1.788 +
   1.789 +	    InitMacTCPParamBlock(&closePB, TCPRelease);
   1.790 +    	    closePB.tcpStream = tcpStream;
   1.791 +    	    closePB.ioCompletion = NULL; 
   1.792 +
   1.793 +	    err = PBControlSync((ParmBlkPtr) &closePB);
   1.794 +	}
   1.795 +
   1.796 +	/*
   1.797 +	 * Free the buffer space used by the socket and the 
   1.798 +	 * actual socket state data structure.  However, if the
   1.799 +	 * RELEASE returns an error, then the rcvBuff is usually
   1.800 +	 * bad, so we can't release it.  I think this means we will
   1.801 +	 * leak the buffer, so in the future, we may want to track the
   1.802 +	 * buffers separately, and nuke them on our own (or just not
   1.803 +	 * use MacTCP!).
   1.804 +	 */
   1.805 +
   1.806 +        if (err == noErr) {
   1.807 +	    ckfree((char *) closePB.csParam.create.rcvBuff);
   1.808 +	}
   1.809 +	
   1.810 +	FreeSocketInfo(statePtr);
   1.811 +	return err;
   1.812 +    }
   1.813 +
   1.814 +    /*
   1.815 +     * Client sockets:
   1.816 +     * If a background write is in progress, don't close
   1.817 +     * the socket yet.  The completion routine for the 
   1.818 +     * write will take care of it.
   1.819 +     */
   1.820 +    
   1.821 +    if (!(statePtr->flags & TCP_WRITING)) {
   1.822 +	InitMacTCPParamBlock(&statePtr->pb, TCPClose);
   1.823 +    	statePtr->pb.tcpStream = tcpStream;
   1.824 +    	statePtr->pb.ioCompletion = closeUPP; 
   1.825 +    	statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr;
   1.826 +    	err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
   1.827 +    	if (err != noErr) {
   1.828 +	    Debugger();
   1.829 +	    statePtr->flags |= TCP_RELEASE;
   1.830 +            /* return 0; */
   1.831 +    	}
   1.832 +    }
   1.833 +
   1.834 +    SocketFreeProc(instanceData);
   1.835 +    return 0;
   1.836 +}
   1.837 +
   1.838 +/*
   1.839 + *----------------------------------------------------------------------
   1.840 + *
   1.841 + * CloseCompletionRoutine --
   1.842 + *
   1.843 + *	Handles the close protocol for a Tcp socket.  This will do
   1.844 + *	a series of calls to release all data currently buffered for
   1.845 + *	the socket.  This is important to do to as it allows the remote
   1.846 + *	connection to recieve and issue it's own close on the socket.
   1.847 + *	Note that this function is running at interupt time and can't
   1.848 + *	allocate memory or do much else except set state.
   1.849 + *
   1.850 + * Results:
   1.851 + *	None.
   1.852 + *
   1.853 + * Side effects:
   1.854 + *	The buffers for the socket are flushed.
   1.855 + *
   1.856 + *----------------------------------------------------------------------
   1.857 + */
   1.858 +
   1.859 +static void
   1.860 +CloseCompletionRoutine(
   1.861 +    TCPiopb *pbPtr)		/* Tcp parameter block. */
   1.862 +{
   1.863 +    TcpState *statePtr;
   1.864 +    OSErr err;
   1.865 +    
   1.866 +    if (pbPtr->csCode == TCPClose) {
   1.867 +	statePtr = (TcpState *) (pbPtr->csParam.close.userDataPtr);
   1.868 +    } else {
   1.869 +	statePtr = (TcpState *) (pbPtr->csParam.receive.userDataPtr);
   1.870 +    }
   1.871 +
   1.872 +    /*
   1.873 +     * It's very bad if the statePtr is nNULL - we should probably panic...
   1.874 +     */
   1.875 +
   1.876 +    if (statePtr == NULL) {
   1.877 +	Debugger();
   1.878 +	return;
   1.879 +    }
   1.880 +    
   1.881 +    WakeUpProcess(&statePtr->psn);
   1.882 +
   1.883 +    /*
   1.884 +     * If there is an error we assume the remote side has already
   1.885 +     * close.  We are done closing as soon as we decide that the
   1.886 +     * remote connection has closed.
   1.887 +     */
   1.888 +    
   1.889 +    if (pbPtr->ioResult != noErr) {
   1.890 +	statePtr->flags |= TCP_RELEASE;
   1.891 +	return;
   1.892 +    }
   1.893 +    if (statePtr->flags & TCP_REMOTE_CLOSED) {
   1.894 +	statePtr->flags |= TCP_RELEASE;
   1.895 +	return;
   1.896 +    }
   1.897 +    
   1.898 +    /*
   1.899 +     * If we just did a recieve we need to return the buffers.
   1.900 +     * Otherwise, attempt to recieve more data until we recieve an
   1.901 +     * error (usually because we have no more data).
   1.902 +     */
   1.903 +
   1.904 +    if (statePtr->pb.csCode == TCPNoCopyRcv) {
   1.905 +	InitMacTCPParamBlock(&statePtr->pb, TCPRcvBfrReturn);
   1.906 +    	statePtr->pb.tcpStream = statePtr->tcpStream;
   1.907 +	statePtr->pb.ioCompletion = closeUPP; 
   1.908 +	statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray;
   1.909 +    	statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr;
   1.910 +	err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
   1.911 +    } else {
   1.912 +	InitMacTCPParamBlock(&statePtr->pb, TCPNoCopyRcv);
   1.913 +    	statePtr->pb.tcpStream = statePtr->tcpStream;
   1.914 +	statePtr->pb.ioCompletion = closeUPP; 
   1.915 +	statePtr->pb.csParam.receive.commandTimeoutValue = 1;
   1.916 +	statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray;
   1.917 +	statePtr->pb.csParam.receive.rdsLength = 5;
   1.918 +    	statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr;
   1.919 +	err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
   1.920 +    }
   1.921 +
   1.922 +    if (err != noErr) {
   1.923 +	statePtr->flags |= TCP_RELEASE;
   1.924 +    }
   1.925 +}
   1.926 +/*
   1.927 + *----------------------------------------------------------------------
   1.928 + *
   1.929 + * SocketFreeProc --
   1.930 + *
   1.931 + *      This callback is invoked in order to delete
   1.932 + *      the notifier data associated with a file handle.
   1.933 + *
   1.934 + * Results:
   1.935 + *      None.
   1.936 + *
   1.937 + * Side effects:
   1.938 + *      Removes the SocketInfo from the global socket list.
   1.939 + *
   1.940 + *----------------------------------------------------------------------
   1.941 + */
   1.942 +
   1.943 +static void
   1.944 +SocketFreeProc(
   1.945 +    ClientData clientData)      /* Channel state. */
   1.946 +{
   1.947 +    TcpState *statePtr = (TcpState *) clientData;
   1.948 +    OSErr err;
   1.949 +    TCPiopb statusPB;
   1.950 +
   1.951 +    /*
   1.952 +     * Get the status of this connection.  We need to do a
   1.953 +     * few tests to see if it's OK to release the stream now.
   1.954 +     */
   1.955 +
   1.956 +    if (!(statePtr->flags & TCP_RELEASE)) {
   1.957 +	return;
   1.958 +    }
   1.959 +    statusPB.ioCRefNum = driverRefNum;
   1.960 +    statusPB.tcpStream = statePtr->tcpStream;
   1.961 +    statusPB.csCode = TCPStatus;
   1.962 +    err = PBControlSync((ParmBlkPtr) &statusPB);
   1.963 +    if ((statusPB.csParam.status.connectionState == 0) ||
   1.964 +	(statusPB.csParam.status.connectionState == 2)) {
   1.965 +	/*
   1.966 +	 * If the conection state is 0 then this was a client
   1.967 +	 * connection and it's closed.  If it is 2 then this a
   1.968 +	 * server client and we may release it.  If it isn't
   1.969 +	 * one of those values then we return and we'll try to
   1.970 +	 * clean up later.
   1.971 +	 */
   1.972 +
   1.973 +    } else {
   1.974 +	return;
   1.975 +    }
   1.976 +    
   1.977 +    /*
   1.978 +     * The Close request is made async.  We know it's
   1.979 +     * OK to release the socket when the TCP_RELEASE flag
   1.980 +     * gets set.
   1.981 +     */
   1.982 +
   1.983 +    InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
   1.984 +    statePtr->pb.tcpStream = statePtr->tcpStream;
   1.985 +    err = PBControlSync((ParmBlkPtr) &statePtr->pb);
   1.986 +    if (err != noErr) {
   1.987 +        Debugger(); /* Ignoreing leaves stranded stream.  Is there an
   1.988 +		       alternative?  */
   1.989 +    }
   1.990 +
   1.991 +    /*
   1.992 +     * Free the buffer space used by the socket and the 
   1.993 +     * actual socket state data structure.
   1.994 +     */
   1.995 +
   1.996 +    ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
   1.997 +    FreeSocketInfo(statePtr);
   1.998 +}
   1.999 +
  1.1000 +/*
  1.1001 + *----------------------------------------------------------------------
  1.1002 + *
  1.1003 + * TcpInput --
  1.1004 + *
  1.1005 + *	Reads input from the IO channel into the buffer given. Returns
  1.1006 + *	count of how many bytes were actually read, and an error 
  1.1007 + *	indication.
  1.1008 + *
  1.1009 + * Results:
  1.1010 + *	A count of how many bytes were read is returned.  A value of -1
  1.1011 + *	implies an error occured.  A value of zero means we have reached
  1.1012 + *	the end of data (EOF).
  1.1013 + *
  1.1014 + * Side effects:
  1.1015 + *	Reads input from the actual channel.
  1.1016 + *
  1.1017 + *----------------------------------------------------------------------
  1.1018 + */
  1.1019 +
  1.1020 +int
  1.1021 +TcpInput(
  1.1022 +    ClientData instanceData,		/* Channel state. */
  1.1023 +    char *buf, 				/* Where to store data read. */
  1.1024 +    int bufSize, 			/* How much space is available
  1.1025 +                                         * in the buffer? */
  1.1026 +    int *errorCodePtr)			/* Where to store error code. */
  1.1027 +{
  1.1028 +    TcpState *statePtr = (TcpState *) instanceData;
  1.1029 +    StreamPtr tcpStream;
  1.1030 +    OSErr err;
  1.1031 +    TCPiopb statusPB;
  1.1032 +    int toRead, dataAvail;
  1.1033 +
  1.1034 +    *errorCodePtr = 0;
  1.1035 +    errno = 0;
  1.1036 +    tcpStream = statePtr->tcpStream;
  1.1037 +
  1.1038 +    if (bufSize == 0) {
  1.1039 +        return 0;
  1.1040 +    }
  1.1041 +    toRead = bufSize;
  1.1042 +
  1.1043 +    /*
  1.1044 +     * First check to see if EOF was already detected, to prevent
  1.1045 +     * calling the socket stack after the first time EOF is detected.
  1.1046 +     */
  1.1047 +
  1.1048 +    if (statePtr->flags & TCP_REMOTE_CLOSED) {
  1.1049 +	return 0;
  1.1050 +    }
  1.1051 +
  1.1052 +    /*
  1.1053 +     * If an asynchronous connect is in progress, attempt to wait for it
  1.1054 +     * to complete before reading.
  1.1055 +     */
  1.1056 +    
  1.1057 +    if ((statePtr->flags & TCP_ASYNC_CONNECT)
  1.1058 +	    && ! WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
  1.1059 +	return -1;
  1.1060 +    }
  1.1061 +
  1.1062 +    /*
  1.1063 +     * No EOF, and it is connected, so try to read more from the socket.
  1.1064 +     * If the socket is blocking, we keep trying until there is data
  1.1065 +     * available or the socket is closed.
  1.1066 +     */
  1.1067 +
  1.1068 +    while (1) {
  1.1069 +
  1.1070 +	statusPB.ioCRefNum = driverRefNum;
  1.1071 +	statusPB.tcpStream = tcpStream;
  1.1072 +	statusPB.csCode = TCPStatus;
  1.1073 +	err = PBControlSync((ParmBlkPtr) &statusPB);
  1.1074 +	if (err != noErr) {
  1.1075 +	    Debugger();
  1.1076 +	    statePtr->flags |= TCP_REMOTE_CLOSED;
  1.1077 +	    return 0;	/* EOF */
  1.1078 +	}
  1.1079 +	dataAvail = statusPB.csParam.status.amtUnreadData;
  1.1080 +	if (dataAvail < bufSize) {
  1.1081 +	    toRead = dataAvail;
  1.1082 +	} else {
  1.1083 +	    toRead = bufSize;
  1.1084 +	}
  1.1085 +	if (toRead != 0) {
  1.1086 +	    /*
  1.1087 +	     * Try to read the data.
  1.1088 +	     */
  1.1089 +	    
  1.1090 +	    InitMacTCPParamBlock(&statusPB, TCPRcv);
  1.1091 +	    statusPB.tcpStream = tcpStream;
  1.1092 +	    statusPB.csParam.receive.rcvBuff = buf;
  1.1093 +	    statusPB.csParam.receive.rcvBuffLen = toRead;
  1.1094 +	    err = PBControlSync((ParmBlkPtr) &statusPB);
  1.1095 +
  1.1096 +	    statePtr->checkMask &= ~TCL_READABLE;
  1.1097 +	    switch (err) {
  1.1098 +		case noErr:
  1.1099 +		    /*
  1.1100 +		     * The channel remains readable only if this read succeds
  1.1101 +		     * and we had more data then the size of the buffer we were
  1.1102 +		     * trying to fill.  Use the info from the call to status to
  1.1103 +		     * determine this.
  1.1104 +		     */
  1.1105 +
  1.1106 +		    if (dataAvail > bufSize) {
  1.1107 +			statePtr->checkMask |= TCL_READABLE;
  1.1108 +		    }
  1.1109 +		    return statusPB.csParam.receive.rcvBuffLen;
  1.1110 +		case connectionClosing:
  1.1111 +		    *errorCodePtr = errno = ESHUTDOWN;
  1.1112 +		    statePtr->flags |= TCP_REMOTE_CLOSED;
  1.1113 +		    return 0;
  1.1114 +		case connectionDoesntExist:
  1.1115 +		case connectionTerminated:
  1.1116 +		    *errorCodePtr = errno = ENOTCONN;
  1.1117 +		    statePtr->flags |= TCP_REMOTE_CLOSED;
  1.1118 +		    return 0;
  1.1119 +		case invalidStreamPtr:
  1.1120 +		default:
  1.1121 +		    *errorCodePtr = EINVAL;
  1.1122 +		    return -1;
  1.1123 +	    }
  1.1124 +	}
  1.1125 +
  1.1126 +	/*
  1.1127 +	 * No data is available, so check the connection state to
  1.1128 +	 * see why this is the case.  
  1.1129 +	 */
  1.1130 +
  1.1131 +	if (statusPB.csParam.status.connectionState == 14) {
  1.1132 +	    statePtr->flags |= TCP_REMOTE_CLOSED;
  1.1133 +	    return 0;
  1.1134 +	}
  1.1135 +	if (statusPB.csParam.status.connectionState != 8) {
  1.1136 +	    Debugger();
  1.1137 +	}
  1.1138 +	statePtr->checkMask &= ~TCL_READABLE;
  1.1139 +	if (statePtr->flags & TCP_ASYNC_SOCKET) {
  1.1140 +	    *errorCodePtr = EWOULDBLOCK;
  1.1141 +	    return -1;
  1.1142 +	}
  1.1143 +
  1.1144 +	/*
  1.1145 +	 * In the blocking case, wait until the file becomes readable
  1.1146 +	 * or closed and try again.
  1.1147 +	 */
  1.1148 +
  1.1149 +	if (!WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
  1.1150 +	    return -1;
  1.1151 +	}
  1.1152 +    }
  1.1153 +}
  1.1154 +
  1.1155 +/*
  1.1156 + *----------------------------------------------------------------------
  1.1157 + *
  1.1158 + * TcpGetHandle --
  1.1159 + *
  1.1160 + *	Called from Tcl_GetChannelHandle to retrieve handles from inside
  1.1161 + *	a file based channel.
  1.1162 + *
  1.1163 + * Results:
  1.1164 + *	The appropriate handle or NULL if not present. 
  1.1165 + *
  1.1166 + * Side effects:
  1.1167 + *	None.
  1.1168 + *
  1.1169 + *----------------------------------------------------------------------
  1.1170 + */
  1.1171 +
  1.1172 +static int
  1.1173 +TcpGetHandle(
  1.1174 +    ClientData instanceData,		/* The file state. */
  1.1175 +    int direction,			/* Which handle to retrieve? */
  1.1176 +    ClientData *handlePtr)
  1.1177 +{
  1.1178 +    TcpState *statePtr = (TcpState *) instanceData;
  1.1179 +
  1.1180 +    *handlePtr = (ClientData) statePtr->tcpStream;
  1.1181 +    return TCL_OK;
  1.1182 +}
  1.1183 +
  1.1184 +/*
  1.1185 + *----------------------------------------------------------------------
  1.1186 + *
  1.1187 + * TcpOutput--
  1.1188 + *
  1.1189 + *	Writes the given output on the IO channel. Returns count of how
  1.1190 + *	many characters were actually written, and an error indication.
  1.1191 + *
  1.1192 + * Results:
  1.1193 + *	A count of how many characters were written is returned and an
  1.1194 + *	error indication is returned in an output argument.
  1.1195 + *
  1.1196 + * Side effects:
  1.1197 + *	Writes output on the actual channel.
  1.1198 + *
  1.1199 + *----------------------------------------------------------------------
  1.1200 + */
  1.1201 +
  1.1202 +static int
  1.1203 +TcpOutput(
  1.1204 +    ClientData instanceData, 		/* Channel state. */
  1.1205 +    CONST char *buf,			/* The data buffer. */
  1.1206 +    int toWrite, 			/* How many bytes to write? */
  1.1207 +    int *errorCodePtr)			/* Where to store error code. */
  1.1208 +{
  1.1209 +    TcpState *statePtr = (TcpState *) instanceData;
  1.1210 +    StreamPtr tcpStream;
  1.1211 +    OSErr err;
  1.1212 +    int amount;
  1.1213 +    TCPiopb statusPB;
  1.1214 +
  1.1215 +    *errorCodePtr = 0;
  1.1216 +    tcpStream = statePtr->tcpStream;
  1.1217 +
  1.1218 +    /*
  1.1219 +     * If an asynchronous connect is in progress, attempt to wait for it
  1.1220 +     * to complete before writing.
  1.1221 +     */
  1.1222 +    
  1.1223 +    if ((statePtr->flags & TCP_ASYNC_CONNECT)
  1.1224 +	    && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
  1.1225 +	return -1;
  1.1226 +    }
  1.1227 +
  1.1228 +    /*
  1.1229 +     * Loop until we have written some data, or an error occurs.
  1.1230 +     */
  1.1231 +
  1.1232 +    while (1) {
  1.1233 +	statusPB.ioCRefNum = driverRefNum;
  1.1234 +	statusPB.tcpStream = tcpStream;
  1.1235 +	statusPB.csCode = TCPStatus;
  1.1236 +	err = PBControlSync((ParmBlkPtr) &statusPB);
  1.1237 +	if ((err == connectionDoesntExist) || ((err == noErr) && 
  1.1238 +		(statusPB.csParam.status.connectionState == 14))) {
  1.1239 +	    /*
  1.1240 +	     * The remote connection is gone away.  Report an error
  1.1241 +	     * and don't write anything.
  1.1242 +	     */
  1.1243 +
  1.1244 +	    *errorCodePtr = errno = EPIPE;
  1.1245 +	    return -1;
  1.1246 +	} else if (err != noErr) {
  1.1247 +	    return -1;
  1.1248 +	}
  1.1249 +	amount = statusPB.csParam.status.sendWindow
  1.1250 +	    - statusPB.csParam.status.amtUnackedData;
  1.1251 +
  1.1252 +	/*
  1.1253 +	 * Attempt to write the data to the socket if a background
  1.1254 +	 * write isn't in progress and there is room in the output buffers.
  1.1255 +	 */
  1.1256 +
  1.1257 +	if (!(statePtr->flags & TCP_WRITING) && amount > 0) {
  1.1258 +	    if (toWrite < amount) {
  1.1259 +		amount = toWrite;
  1.1260 +	    }
  1.1261 +
  1.1262 +            /* We need to copy the data, otherwise the caller may overwrite
  1.1263 +             * the buffer in the middle of our asynchronous call
  1.1264 +             */
  1.1265 +             
  1.1266 +            if (amount > statePtr->writeBufferSize) {
  1.1267 +                /* 
  1.1268 +                 * need to grow write buffer 
  1.1269 +                 */
  1.1270 +                 
  1.1271 +                if (statePtr->writeBuffer != (void *) NULL) {
  1.1272 +                    ckfree(statePtr->writeBuffer);
  1.1273 +                }
  1.1274 +                statePtr->writeBuffer = (void *) ckalloc(amount);
  1.1275 +                statePtr->writeBufferSize = amount;
  1.1276 +            }
  1.1277 +            memcpy(statePtr->writeBuffer, buf, amount);
  1.1278 +            statePtr->dataSegment[0].ptr = statePtr->writeBuffer;
  1.1279 +
  1.1280 +	    statePtr->dataSegment[0].length = amount;
  1.1281 +	    statePtr->dataSegment[1].length = 0;
  1.1282 +	    InitMacTCPParamBlock(&statePtr->pb, TCPSend);
  1.1283 +	    statePtr->pb.ioCompletion = completeUPP;
  1.1284 +	    statePtr->pb.tcpStream = tcpStream;
  1.1285 +	    statePtr->pb.csParam.send.wdsPtr = (Ptr) statePtr->dataSegment;
  1.1286 +	    statePtr->pb.csParam.send.pushFlag = 1;
  1.1287 +	    statePtr->pb.csParam.send.userDataPtr = (Ptr) statePtr;
  1.1288 +	    statePtr->flags |= TCP_WRITING;
  1.1289 +	    err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1.1290 +	    switch (err) {
  1.1291 +		case noErr:
  1.1292 +		    return amount;
  1.1293 +		case connectionClosing:
  1.1294 +		    *errorCodePtr = errno = ESHUTDOWN;
  1.1295 +		    statePtr->flags |= TCP_REMOTE_CLOSED;
  1.1296 +		    return -1;
  1.1297 +		case connectionDoesntExist:
  1.1298 +		case connectionTerminated:
  1.1299 +		    *errorCodePtr = errno = ENOTCONN;
  1.1300 +		    statePtr->flags |= TCP_REMOTE_CLOSED;
  1.1301 +		    return -1;
  1.1302 +		case invalidStreamPtr:
  1.1303 +		default:
  1.1304 +		    return -1;
  1.1305 +	    }
  1.1306 +
  1.1307 +	}
  1.1308 +
  1.1309 +	/*
  1.1310 +	 * The socket wasn't writable.  In the non-blocking case, return
  1.1311 +	 * immediately, otherwise wait  until the file becomes writable
  1.1312 +	 * or closed and try again.
  1.1313 +	 */
  1.1314 +
  1.1315 +	if (statePtr->flags & TCP_ASYNC_SOCKET) {
  1.1316 +	    statePtr->checkMask &= ~TCL_WRITABLE;
  1.1317 +	    *errorCodePtr = EWOULDBLOCK;
  1.1318 +	    return -1;
  1.1319 +	} else if (!WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
  1.1320 +	    return -1;
  1.1321 +	}
  1.1322 +    }
  1.1323 +}
  1.1324 +
  1.1325 +/*
  1.1326 + *----------------------------------------------------------------------
  1.1327 + *
  1.1328 + * TcpGetOptionProc --
  1.1329 + *
  1.1330 + *	Computes an option value for a TCP socket based channel, or a
  1.1331 + *	list of all options and their values.
  1.1332 + *
  1.1333 + *	Note: This code is based on code contributed by John Haxby.
  1.1334 + *
  1.1335 + * Results:
  1.1336 + *	A standard Tcl result. The value of the specified option or a
  1.1337 + *	list of all options and	their values is returned in the
  1.1338 + *	supplied DString.
  1.1339 + *
  1.1340 + * Side effects:
  1.1341 + *	None.
  1.1342 + *
  1.1343 + *----------------------------------------------------------------------
  1.1344 + */
  1.1345 +
  1.1346 +static int
  1.1347 +TcpGetOptionProc(
  1.1348 +    ClientData instanceData, 		/* Socket state. */
  1.1349 +    Tcl_Interp *interp,                 /* For error reporting - can be NULL.*/
  1.1350 +    CONST char *optionName, 		/* Name of the option to
  1.1351 +                                         * retrieve the value for, or
  1.1352 +                                         * NULL to get all options and
  1.1353 +                                         * their values. */
  1.1354 +    Tcl_DString *dsPtr)			/* Where to store the computed
  1.1355 +                                         * value; initialized by caller. */
  1.1356 +{
  1.1357 +    TcpState *statePtr = (TcpState *) instanceData;
  1.1358 +    int doPeerName = false, doSockName = false, doError = false, doAll = false;
  1.1359 +    ip_addr tcpAddress;
  1.1360 +    char buffer[128];
  1.1361 +    OSErr err;
  1.1362 +    Tcl_DString dString;
  1.1363 +    TCPiopb statusPB;
  1.1364 +    int errorCode;
  1.1365 +    size_t len = 0;
  1.1366 +
  1.1367 +    /*
  1.1368 +     * If an asynchronous connect is in progress, attempt to wait for it
  1.1369 +     * to complete before accessing the socket state.
  1.1370 +     */
  1.1371 +    
  1.1372 +    if ((statePtr->flags & TCP_ASYNC_CONNECT)
  1.1373 +	    && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, &errorCode)) {
  1.1374 +	if (interp) {
  1.1375 +	    /*
  1.1376 +	     * fix the error message.
  1.1377 +	     */
  1.1378 +
  1.1379 +	    Tcl_AppendResult(interp, "connect is in progress and can't wait",
  1.1380 +	    		NULL);
  1.1381 +	}
  1.1382 +	return TCL_ERROR;
  1.1383 +    }
  1.1384 +            
  1.1385 +    /*
  1.1386 +     * Determine which options we need to do.  Do all of them
  1.1387 +     * if optionName is NULL.
  1.1388 +     */
  1.1389 +
  1.1390 +    if (optionName == (CONST char *) NULL || optionName[0] == '\0') {
  1.1391 +        doAll = true;
  1.1392 +    } else {
  1.1393 +	len = strlen(optionName);
  1.1394 +	if (!strncmp(optionName, "-peername", len)) {
  1.1395 +	    doPeerName = true;
  1.1396 +	} else if (!strncmp(optionName, "-sockname", len)) {
  1.1397 +	    doSockName = true;
  1.1398 +	} else if (!strncmp(optionName, "-error", len)) {
  1.1399 +	    /* SF Bug #483575 */
  1.1400 +	    doError = true;
  1.1401 +	} else {
  1.1402 +	    return Tcl_BadChannelOption(interp, optionName, 
  1.1403 +		        "error peername sockname");
  1.1404 +	}
  1.1405 +    }
  1.1406 +
  1.1407 +    /*
  1.1408 +     * SF Bug #483575
  1.1409 +     *
  1.1410 +     * Return error information. Currently we ignore
  1.1411 +     * this option. IOW, we always return the empty
  1.1412 +     * string, signaling 'no error'.
  1.1413 +     *
  1.1414 +     * FIXME: Get a mac/socket expert to write a correct
  1.1415 +     * FIXME: implementation.
  1.1416 +     */
  1.1417 +
  1.1418 +    if (doAll || doError) {
  1.1419 +	if (doAll) {
  1.1420 +	    Tcl_DStringAppendElement(dsPtr, "-error");
  1.1421 +	    Tcl_DStringAppendElement(dsPtr, "");
  1.1422 +	} else {
  1.1423 +	    Tcl_DStringAppend (dsPtr, "", -1);
  1.1424 +	    return TCL_OK;
  1.1425 +	}
  1.1426 +    }
  1.1427 +
  1.1428 +    /*
  1.1429 +     * Get status on the stream.  Make sure to use a new pb struct because
  1.1430 +     * the struct in the statePtr may be part of an asyncronous call.
  1.1431 +     */
  1.1432 +
  1.1433 +    statusPB.ioCRefNum = driverRefNum;
  1.1434 +    statusPB.tcpStream = statePtr->tcpStream;
  1.1435 +    statusPB.csCode = TCPStatus;
  1.1436 +    err = PBControlSync((ParmBlkPtr) &statusPB);
  1.1437 +    if ((err == connectionDoesntExist) ||
  1.1438 +	((err == noErr) && (statusPB.csParam.status.connectionState == 14))) {
  1.1439 +	/*
  1.1440 +	 * The socket was probably closed on the other side of the connection.
  1.1441 +	 */
  1.1442 +
  1.1443 +	if (interp) {
  1.1444 +	    Tcl_AppendResult(interp, "can't access socket info: ",
  1.1445 +			     "connection reset by peer", NULL);
  1.1446 +	}
  1.1447 +	return TCL_ERROR;
  1.1448 +    } else if (err != noErr) {
  1.1449 +	if (interp) { 
  1.1450 +	    Tcl_AppendResult(interp, "unknown socket error", NULL);
  1.1451 +	}
  1.1452 +	Debugger();
  1.1453 +	return TCL_ERROR;
  1.1454 +    }
  1.1455 +
  1.1456 +
  1.1457 +    /*
  1.1458 +     * Get the sockname for the socket.
  1.1459 +     */
  1.1460 +
  1.1461 +    Tcl_DStringInit(&dString);
  1.1462 +    if (doAll || doSockName) {
  1.1463 +	if (doAll) {
  1.1464 +	    Tcl_DStringAppendElement(dsPtr, "-sockname");
  1.1465 +	    Tcl_DStringStartSublist(dsPtr);
  1.1466 +	}
  1.1467 +	tcpAddress = statusPB.csParam.status.localHost;
  1.1468 +	sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
  1.1469 +		tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
  1.1470 +		tcpAddress & 0xff);
  1.1471 +	Tcl_DStringAppendElement(dsPtr, buffer);
  1.1472 +	if (ResolveAddress(tcpAddress, &dString) == noErr) {
  1.1473 +	    Tcl_DStringAppendElement(dsPtr, dString.string);
  1.1474 +	} else {
  1.1475 +	    Tcl_DStringAppendElement(dsPtr, "<unknown>");
  1.1476 +	}
  1.1477 +	sprintf(buffer, "%d", statusPB.csParam.status.localPort);
  1.1478 +	Tcl_DStringAppendElement(dsPtr, buffer);
  1.1479 +	if (doAll) {
  1.1480 +	    Tcl_DStringEndSublist(dsPtr);
  1.1481 +	}
  1.1482 +    }
  1.1483 +
  1.1484 +    /*
  1.1485 +     * Get the peername for the socket.
  1.1486 +     */
  1.1487 +
  1.1488 +    if ((doAll || doPeerName) && (statePtr->flags & TCP_CONNECTED)) {
  1.1489 +	if (doAll) {
  1.1490 +	    Tcl_DStringAppendElement(dsPtr, "-peername");
  1.1491 +	    Tcl_DStringStartSublist(dsPtr);
  1.1492 +	}
  1.1493 +	tcpAddress = statusPB.csParam.status.remoteHost;
  1.1494 +	sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
  1.1495 +		tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
  1.1496 +		tcpAddress & 0xff);
  1.1497 +	Tcl_DStringAppendElement(dsPtr, buffer);
  1.1498 +	Tcl_DStringSetLength(&dString, 0);
  1.1499 +	if (ResolveAddress(tcpAddress, &dString) == noErr) {
  1.1500 +	    Tcl_DStringAppendElement(dsPtr, dString.string);
  1.1501 +	} else {
  1.1502 +	    Tcl_DStringAppendElement(dsPtr, "<unknown>");
  1.1503 +	}
  1.1504 +	sprintf(buffer, "%d", statusPB.csParam.status.remotePort);
  1.1505 +	Tcl_DStringAppendElement(dsPtr, buffer);
  1.1506 +	if (doAll) {
  1.1507 +	    Tcl_DStringEndSublist(dsPtr);
  1.1508 +	}
  1.1509 +    }
  1.1510 +
  1.1511 +    Tcl_DStringFree(&dString);
  1.1512 +    return TCL_OK;
  1.1513 +}
  1.1514 +
  1.1515 +/*
  1.1516 + *----------------------------------------------------------------------
  1.1517 + *
  1.1518 + * TcpWatch --
  1.1519 + *
  1.1520 + *	Initialize the notifier to watch this channel.
  1.1521 + *
  1.1522 + * Results:
  1.1523 + *	None.
  1.1524 + *
  1.1525 + * Side effects:
  1.1526 + *	Sets the watchMask for the channel.
  1.1527 + *
  1.1528 + *----------------------------------------------------------------------
  1.1529 + */
  1.1530 +
  1.1531 +static void
  1.1532 +TcpWatch(instanceData, mask)
  1.1533 +    ClientData instanceData;		/* The file state. */
  1.1534 +    int mask;				/* Events of interest; an OR-ed
  1.1535 +                                         * combination of TCL_READABLE,
  1.1536 +                                         * TCL_WRITABLE and TCL_EXCEPTION. */
  1.1537 +{
  1.1538 +    TcpState *statePtr = (TcpState *) instanceData;
  1.1539 +
  1.1540 +    statePtr->watchMask = mask;
  1.1541 +}
  1.1542 +
  1.1543 +/*
  1.1544 + *----------------------------------------------------------------------
  1.1545 + *
  1.1546 + * NewSocketInfo --
  1.1547 + *
  1.1548 + *	This function allocates and initializes a new SocketInfo
  1.1549 + *	structure.
  1.1550 + *
  1.1551 + * Results:
  1.1552 + *	Returns a newly allocated SocketInfo.
  1.1553 + *
  1.1554 + * Side effects:
  1.1555 + *	Adds the socket to the global socket list, allocates memory.
  1.1556 + *
  1.1557 + *----------------------------------------------------------------------
  1.1558 + */
  1.1559 +
  1.1560 +static TcpState *
  1.1561 +NewSocketInfo(
  1.1562 +    StreamPtr tcpStream)
  1.1563 +{
  1.1564 +    TcpState *statePtr;
  1.1565 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.1566 +
  1.1567 +    statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  1.1568 +    statePtr->tcpStream = tcpStream;
  1.1569 +    statePtr->psn = applicationPSN;
  1.1570 +    statePtr->flags = 0;
  1.1571 +    statePtr->checkMask = 0;
  1.1572 +    statePtr->watchMask = 0;
  1.1573 +    statePtr->acceptProc = (Tcl_TcpAcceptProc *) NULL;
  1.1574 +    statePtr->acceptProcData = (ClientData) NULL;
  1.1575 +    statePtr->writeBuffer = (void *) NULL;
  1.1576 +    statePtr->writeBufferSize = 0;
  1.1577 +    statePtr->nextPtr = tsdPtr->socketList;
  1.1578 +    tsdPtr->socketList = statePtr;
  1.1579 +    return statePtr;
  1.1580 +}
  1.1581 +
  1.1582 +/*
  1.1583 + *----------------------------------------------------------------------
  1.1584 + *
  1.1585 + * FreeSocketInfo --
  1.1586 + *
  1.1587 + *	This function deallocates a SocketInfo structure that is no
  1.1588 + *	longer needed.
  1.1589 + *
  1.1590 + * Results:
  1.1591 + *	None.
  1.1592 + *
  1.1593 + * Side effects:
  1.1594 + *	Removes the socket from the global socket list, frees memory.
  1.1595 + *
  1.1596 + *----------------------------------------------------------------------
  1.1597 + */
  1.1598 +
  1.1599 +static void
  1.1600 +FreeSocketInfo(
  1.1601 +    TcpState *statePtr)		/* The state pointer to free. */
  1.1602 +{
  1.1603 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.1604 +
  1.1605 +    if (statePtr == tsdPtr->socketList) {
  1.1606 +	tsdPtr->socketList = statePtr->nextPtr;
  1.1607 +    } else {
  1.1608 +	TcpState *p;
  1.1609 +	for (p = tsdPtr->socketList; p != NULL; p = p->nextPtr) {
  1.1610 +	    if (p->nextPtr == statePtr) {
  1.1611 +		p->nextPtr = statePtr->nextPtr;
  1.1612 +		break;
  1.1613 +	    }
  1.1614 +	}
  1.1615 +    }
  1.1616 +    
  1.1617 +    if (statePtr->writeBuffer != (void *) NULL) {
  1.1618 +        ckfree(statePtr->writeBuffer);
  1.1619 +    }
  1.1620 +    
  1.1621 +    ckfree((char *) statePtr);
  1.1622 +}
  1.1623 +
  1.1624 +/*
  1.1625 + *----------------------------------------------------------------------
  1.1626 + *
  1.1627 + * Tcl_MakeTcpClientChannel --
  1.1628 + *
  1.1629 + *	Creates a Tcl_Channel from an existing client TCP socket.
  1.1630 + *
  1.1631 + * Results:
  1.1632 + *	The Tcl_Channel wrapped around the preexisting TCP socket.
  1.1633 + *
  1.1634 + * Side effects:
  1.1635 + *	None.
  1.1636 + *
  1.1637 + *----------------------------------------------------------------------
  1.1638 + */
  1.1639 +
  1.1640 +Tcl_Channel
  1.1641 +Tcl_MakeTcpClientChannel(
  1.1642 +    ClientData sock)	/* The socket to wrap up into a channel. */
  1.1643 +{
  1.1644 +    TcpState *statePtr;
  1.1645 +    char channelName[20];
  1.1646 +
  1.1647 +    if (TclpHasSockets(NULL) != TCL_OK) {
  1.1648 +	return NULL;
  1.1649 +    }
  1.1650 +	
  1.1651 +    statePtr = NewSocketInfo((StreamPtr) sock);
  1.1652 +    /* TODO: do we need to set the port??? */
  1.1653 +    
  1.1654 +    sprintf(channelName, "sock%d", socketNumber++);
  1.1655 +    
  1.1656 +    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1.1657 +            (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
  1.1658 +    Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
  1.1659 +    Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
  1.1660 +    return statePtr->channel;
  1.1661 +}
  1.1662 +
  1.1663 +/*
  1.1664 + *----------------------------------------------------------------------
  1.1665 + *
  1.1666 + * CreateSocket --
  1.1667 + *
  1.1668 + *	This function opens a new socket and initializes the
  1.1669 + *	SocketInfo structure.
  1.1670 + *
  1.1671 + * Results:
  1.1672 + *	Returns a new SocketInfo, or NULL with an error in interp.
  1.1673 + *
  1.1674 + * Side effects:
  1.1675 + *	Adds a new socket to the socketList.
  1.1676 + *
  1.1677 + *----------------------------------------------------------------------
  1.1678 + */
  1.1679 +
  1.1680 +static TcpState *
  1.1681 +CreateSocket(
  1.1682 +    Tcl_Interp *interp,		/* For error reporting; can be NULL. */
  1.1683 +    int port,			/* Port number to open. */
  1.1684 +    CONST char *host,		/* Name of host on which to open port. */
  1.1685 +    CONST char *myaddr,		/* Optional client-side address */
  1.1686 +    int myport,			/* Optional client-side port */
  1.1687 +    int server,			/* 1 if socket should be a server socket,
  1.1688 +				 * else 0 for a client socket. */
  1.1689 +    int async)			/* 1 create async, 0 do sync. */
  1.1690 +{
  1.1691 +    ip_addr macAddr;
  1.1692 +    OSErr err;
  1.1693 +    TCPiopb pb;
  1.1694 +    StreamPtr tcpStream;
  1.1695 +    TcpState *statePtr;
  1.1696 +    char * buffer;
  1.1697 +    
  1.1698 +    /*
  1.1699 +     * Figure out the ip address from the host string.
  1.1700 +     */
  1.1701 +
  1.1702 +    if (host == NULL) {
  1.1703 +	err = GetLocalAddress(&macAddr);
  1.1704 +    } else {
  1.1705 +	err = GetHostFromString(host, &macAddr);
  1.1706 +    }
  1.1707 +    if (err != noErr) {
  1.1708 +	Tcl_SetErrno(EHOSTUNREACH);
  1.1709 +	if (interp != (Tcl_Interp *) NULL) {
  1.1710 +	    Tcl_AppendResult(interp, "couldn't open socket: ",
  1.1711 +                        Tcl_PosixError(interp), (char *) NULL);
  1.1712 +	}
  1.1713 +	return (TcpState *) NULL;
  1.1714 +    }
  1.1715 +    
  1.1716 +    /*
  1.1717 +     * Create a MacTCP stream and create the state used for socket
  1.1718 +     * transactions from here on out.
  1.1719 +     */
  1.1720 +
  1.1721 +    ClearZombieSockets();
  1.1722 +    buffer = ckalloc(socketBufferSize);
  1.1723 +    InitMacTCPParamBlock(&pb, TCPCreate);
  1.1724 +    pb.csParam.create.rcvBuff = buffer;
  1.1725 +    pb.csParam.create.rcvBuffLen = socketBufferSize;
  1.1726 +    pb.csParam.create.notifyProc = nil /* notifyUPP */;
  1.1727 +    err = PBControlSync((ParmBlkPtr) &pb);
  1.1728 +    if (err != noErr) {
  1.1729 +        Tcl_SetErrno(0); /* TODO: set to ENOSR - maybe?*/
  1.1730 +        if (interp != (Tcl_Interp *) NULL) {
  1.1731 +	    Tcl_AppendResult(interp, "couldn't open socket: ",
  1.1732 +		Tcl_PosixError(interp), (char *) NULL);
  1.1733 +        }
  1.1734 +	return (TcpState *) NULL;
  1.1735 +    }
  1.1736 +
  1.1737 +    tcpStream = pb.tcpStream;
  1.1738 +    statePtr = NewSocketInfo(tcpStream);
  1.1739 +    statePtr->port = port;
  1.1740 +    
  1.1741 +    if (server) {
  1.1742 +        /* 
  1.1743 +         * Set up server connection.
  1.1744 +         */
  1.1745 +
  1.1746 +	InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen);
  1.1747 +	statePtr->pb.tcpStream = tcpStream;
  1.1748 +	statePtr->pb.csParam.open.localPort = statePtr->port;
  1.1749 +	statePtr->pb.ioCompletion = completeUPP; 
  1.1750 +	statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
  1.1751 +	statePtr->pb.csParam.open.ulpTimeoutValue = 100;
  1.1752 +	statePtr->pb.csParam.open.ulpTimeoutAction 	= 1 /* 1:abort 0:report */;
  1.1753 +	statePtr->pb.csParam.open.commandTimeoutValue	= 0 /* infinity */;
  1.1754 +
  1.1755 +	statePtr->flags |= TCP_LISTENING;
  1.1756 +	err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1.1757 +
  1.1758 +	/*
  1.1759 +	 * If this is a server on port 0 then we need to wait until
  1.1760 +	 * the dynamic port allocation is made by the MacTcp driver.
  1.1761 +	 */
  1.1762 +
  1.1763 +	if (statePtr->port == 0) {
  1.1764 +	    EventRecord dummy;
  1.1765 +
  1.1766 +	    while (statePtr->pb.csParam.open.localPort == 0) {
  1.1767 +		WaitNextEvent(0, &dummy, 1, NULL);
  1.1768 +		if (statePtr->pb.ioResult != 0) {
  1.1769 +		    break;
  1.1770 +		}
  1.1771 +	    }
  1.1772 +	    statePtr->port = statePtr->pb.csParam.open.localPort;
  1.1773 +	}
  1.1774 +	Tcl_SetErrno(EINPROGRESS);
  1.1775 +    } else {
  1.1776 +	/*
  1.1777 +	 * Attempt to connect. The connect may fail at present with an
  1.1778 +	 * EINPROGRESS but at a later time it will complete. The caller
  1.1779 +	 * will set up a file handler on the socket if she is interested in
  1.1780 +	 * being informed when the connect completes.
  1.1781 +	 */
  1.1782 +
  1.1783 +	InitMacTCPParamBlock(&statePtr->pb, TCPActiveOpen);
  1.1784 +	
  1.1785 +	statePtr->pb.tcpStream = tcpStream;
  1.1786 +	statePtr->pb.csParam.open.remoteHost = macAddr;
  1.1787 +	statePtr->pb.csParam.open.remotePort = port;
  1.1788 +	statePtr->pb.csParam.open.localHost = 0;
  1.1789 +	statePtr->pb.csParam.open.localPort = myport;
  1.1790 +	statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;	
  1.1791 +	statePtr->pb.csParam.open.validityFlags 	= timeoutValue | timeoutAction;
  1.1792 +	statePtr->pb.csParam.open.ulpTimeoutValue 	= 60 /* seconds */;
  1.1793 +	statePtr->pb.csParam.open.ulpTimeoutAction 	= 1 /* 1:abort 0:report */;
  1.1794 +	statePtr->pb.csParam.open.commandTimeoutValue   = 0;
  1.1795 +
  1.1796 +	statePtr->pb.ioCompletion = completeUPP;
  1.1797 +	if (async) {
  1.1798 +	    statePtr->flags |= TCP_ASYNC_CONNECT;
  1.1799 +	    err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1.1800 +	    Tcl_SetErrno(EINPROGRESS);
  1.1801 +	} else {
  1.1802 +	    err = PBControlSync((ParmBlkPtr) &(statePtr->pb));
  1.1803 +	}
  1.1804 +    }
  1.1805 +    
  1.1806 +    switch (err) {
  1.1807 +	case noErr:
  1.1808 +	    if (!async) {
  1.1809 +		statePtr->flags |= TCP_CONNECTED;
  1.1810 +	    }
  1.1811 +	    return statePtr;
  1.1812 +	case duplicateSocket:
  1.1813 +	    Tcl_SetErrno(EADDRINUSE);
  1.1814 +	    break;
  1.1815 +	case openFailed:
  1.1816 +	case connectionTerminated:
  1.1817 +	    Tcl_SetErrno(ECONNREFUSED);
  1.1818 +	    break;
  1.1819 +	case invalidStreamPtr:
  1.1820 +	case connectionExists:
  1.1821 +	default:
  1.1822 +	    /*
  1.1823 +	     * These cases should never occur.  However, we will fail
  1.1824 +	     * gracefully and hope Tcl can resume.  The alternative is to panic
  1.1825 +	     * which is probably a bit drastic.
  1.1826 +	     */
  1.1827 +
  1.1828 +	    Debugger();
  1.1829 +	    Tcl_SetErrno(err);
  1.1830 +    }
  1.1831 +
  1.1832 +    /*
  1.1833 +     * We had error during the connection.  Release the stream
  1.1834 +     * and file handle.  Also report to the interp.
  1.1835 +     */
  1.1836 +
  1.1837 +    pb.ioCRefNum = driverRefNum;
  1.1838 +    pb.csCode = TCPRelease;
  1.1839 +    pb.tcpStream = tcpStream;
  1.1840 +    pb.ioCompletion = NULL; 
  1.1841 +    err = PBControlSync((ParmBlkPtr) &pb);
  1.1842 +
  1.1843 +    if (interp != (Tcl_Interp *) NULL) {
  1.1844 +	Tcl_AppendResult(interp, "couldn't open socket: ",
  1.1845 +	    Tcl_PosixError(interp), (char *) NULL);
  1.1846 +    }
  1.1847 +
  1.1848 +    ckfree(buffer);
  1.1849 +    FreeSocketInfo(statePtr);
  1.1850 +    return (TcpState *) NULL;
  1.1851 +}
  1.1852 +
  1.1853 +/*
  1.1854 + *----------------------------------------------------------------------
  1.1855 + *
  1.1856 + * Tcl_OpenTcpClient --
  1.1857 + *
  1.1858 + *	Opens a TCP client socket and creates a channel around it.
  1.1859 + *
  1.1860 + * Results:
  1.1861 + *	The channel or NULL if failed. On failure, the routine also
  1.1862 + *	sets the output argument errorCodePtr to the error code.
  1.1863 + *
  1.1864 + * Side effects:
  1.1865 + *	Opens a client socket and creates a new channel.
  1.1866 + *
  1.1867 + *----------------------------------------------------------------------
  1.1868 + */
  1.1869 +
  1.1870 +Tcl_Channel
  1.1871 +Tcl_OpenTcpClient(
  1.1872 +    Tcl_Interp *interp, 		/* For error reporting; can be NULL. */
  1.1873 +    int port, 				/* Port number to open. */
  1.1874 +    CONST char *host, 			/* Host on which to open port. */
  1.1875 +    CONST char *myaddr,			/* Client-side address */
  1.1876 +    int myport, 			/* Client-side port */
  1.1877 +    int async)				/* If nonzero, attempt to do an
  1.1878 +                                         * asynchronous connect. Otherwise
  1.1879 +                                         * we do a blocking connect. 
  1.1880 +                                         * - currently ignored */
  1.1881 +{
  1.1882 +    TcpState *statePtr;
  1.1883 +    char channelName[20];
  1.1884 +
  1.1885 +    if (TclpHasSockets(interp) != TCL_OK) {
  1.1886 +	return NULL;
  1.1887 +    }
  1.1888 +	
  1.1889 +    /*
  1.1890 +     * Create a new client socket and wrap it in a channel.
  1.1891 +     */
  1.1892 +
  1.1893 +    statePtr = CreateSocket(interp, port, host, myaddr, myport, 0, async);
  1.1894 +    if (statePtr == NULL) {
  1.1895 +	return NULL;
  1.1896 +    }
  1.1897 +    
  1.1898 +    sprintf(channelName, "sock%d", socketNumber++);
  1.1899 +
  1.1900 +    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1.1901 +            (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
  1.1902 +    Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
  1.1903 +    Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
  1.1904 +    return statePtr->channel;
  1.1905 +}
  1.1906 +
  1.1907 +/*
  1.1908 + *----------------------------------------------------------------------
  1.1909 + *
  1.1910 + * Tcl_OpenTcpServer --
  1.1911 + *
  1.1912 + *	Opens a TCP server socket and creates a channel around it.
  1.1913 + *
  1.1914 + * Results:
  1.1915 + *	The channel or NULL if failed.
  1.1916 + *
  1.1917 + * Side effects:
  1.1918 + *	Opens a server socket and creates a new channel.
  1.1919 + *
  1.1920 + *----------------------------------------------------------------------
  1.1921 + */
  1.1922 +
  1.1923 +Tcl_Channel
  1.1924 +Tcl_OpenTcpServer(
  1.1925 +    Tcl_Interp *interp,			/* For error reporting - may be
  1.1926 +                                         * NULL. */
  1.1927 +    int port,				/* Port number to open. */
  1.1928 +    CONST char *host,			/* Name of local host. */
  1.1929 +    Tcl_TcpAcceptProc *acceptProc,	/* Callback for accepting connections
  1.1930 +                                         * from new clients. */
  1.1931 +    ClientData acceptProcData)		/* Data for the callback. */
  1.1932 +{
  1.1933 +    TcpState *statePtr;
  1.1934 +    char channelName[20];
  1.1935 +
  1.1936 +    if (TclpHasSockets(interp) != TCL_OK) {
  1.1937 +	return NULL;
  1.1938 +    }
  1.1939 +
  1.1940 +    /*
  1.1941 +     * Create a new client socket and wrap it in a channel.
  1.1942 +     */
  1.1943 +
  1.1944 +    statePtr = CreateSocket(interp, port, host, NULL, 0, 1, 1);
  1.1945 +    if (statePtr == NULL) {
  1.1946 +	return NULL;
  1.1947 +    }
  1.1948 +
  1.1949 +    statePtr->acceptProc = acceptProc;
  1.1950 +    statePtr->acceptProcData = acceptProcData;
  1.1951 +
  1.1952 +    sprintf(channelName, "sock%d", socketNumber++);
  1.1953 +
  1.1954 +    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1.1955 +            (ClientData) statePtr, 0);
  1.1956 +    Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
  1.1957 +    Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
  1.1958 +    return statePtr->channel;
  1.1959 +}
  1.1960 +
  1.1961 +/*
  1.1962 + *----------------------------------------------------------------------
  1.1963 + *
  1.1964 + * SocketEventProc --
  1.1965 + *
  1.1966 + *	This procedure is called by Tcl_ServiceEvent when a socket event
  1.1967 + *	reaches the front of the event queue.  This procedure is
  1.1968 + *	responsible for notifying the generic channel code.
  1.1969 + *
  1.1970 + * Results:
  1.1971 + *	Returns 1 if the event was handled, meaning it should be removed
  1.1972 + *	from the queue.  Returns 0 if the event was not handled, meaning
  1.1973 + *	it should stay on the queue.  The only time the event isn't
  1.1974 + *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
  1.1975 + *
  1.1976 + * Side effects:
  1.1977 + *	Whatever the channel callback procedures do.
  1.1978 + *
  1.1979 + *----------------------------------------------------------------------
  1.1980 + */
  1.1981 +
  1.1982 +static int
  1.1983 +SocketEventProc(
  1.1984 +    Tcl_Event *evPtr,		/* Event to service. */
  1.1985 +    int flags)			/* Flags that indicate what events to
  1.1986 +				 * handle, such as TCL_FILE_EVENTS. */
  1.1987 +{
  1.1988 +    TcpState *statePtr;
  1.1989 +    SocketEvent *eventPtr = (SocketEvent *) evPtr;
  1.1990 +    int mask = 0;
  1.1991 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.1992 +
  1.1993 +    if (!(flags & TCL_FILE_EVENTS)) {
  1.1994 +	return 0;
  1.1995 +    }
  1.1996 +
  1.1997 +    /*
  1.1998 +     * Find the specified socket on the socket list.
  1.1999 +     */
  1.2000 +
  1.2001 +    for (statePtr = tsdPtr->socketList; statePtr != NULL;
  1.2002 +	    statePtr = statePtr->nextPtr) {
  1.2003 +	if ((statePtr == eventPtr->statePtr) && 
  1.2004 +		(statePtr->tcpStream == eventPtr->tcpStream)) {
  1.2005 +	    break;
  1.2006 +	}
  1.2007 +    }
  1.2008 +
  1.2009 +    /*
  1.2010 +     * Discard events that have gone stale.
  1.2011 +     */
  1.2012 +
  1.2013 +    if (!statePtr) {
  1.2014 +	return 1;
  1.2015 +    }
  1.2016 +    statePtr->flags &= ~(TCP_PENDING);
  1.2017 +    if (statePtr->flags & TCP_RELEASE) {
  1.2018 +	SocketFreeProc(statePtr);
  1.2019 +	return 1;
  1.2020 +    }
  1.2021 +
  1.2022 +
  1.2023 +    /*
  1.2024 +     * Handle connection requests directly.
  1.2025 +     */
  1.2026 +
  1.2027 +    if (statePtr->flags & TCP_LISTEN_CONNECT) {
  1.2028 +	if (statePtr->checkMask & TCL_READABLE) {
  1.2029 +	    TcpAccept(statePtr);
  1.2030 +	}
  1.2031 +	return 1;
  1.2032 +    }
  1.2033 +
  1.2034 +    /*
  1.2035 +     * Mask off unwanted events then notify the channel.
  1.2036 +     */
  1.2037 +
  1.2038 +    mask = statePtr->checkMask & statePtr->watchMask;
  1.2039 +    if (mask) {
  1.2040 +	Tcl_NotifyChannel(statePtr->channel, mask);
  1.2041 +    }
  1.2042 +    return 1;
  1.2043 +}
  1.2044 +
  1.2045 +/*
  1.2046 + *----------------------------------------------------------------------
  1.2047 + *
  1.2048 + * WaitForSocketEvent --
  1.2049 + *
  1.2050 + *	Waits until one of the specified events occurs on a socket.
  1.2051 + *
  1.2052 + * Results:
  1.2053 + *	Returns 1 on success or 0 on failure, with an error code in
  1.2054 + *	errorCodePtr.
  1.2055 + *
  1.2056 + * Side effects:
  1.2057 + *	Processes socket events off the system queue.
  1.2058 + *
  1.2059 + *----------------------------------------------------------------------
  1.2060 + */
  1.2061 +
  1.2062 +static int
  1.2063 +WaitForSocketEvent(
  1.2064 +    TcpState *statePtr,		/* Information about this socket. */
  1.2065 +    int mask,			/* Events to look for. */
  1.2066 +    int *errorCodePtr)		/* Where to store errors? */
  1.2067 +{
  1.2068 +    OSErr err;
  1.2069 +    TCPiopb statusPB;
  1.2070 +    EventRecord dummy;
  1.2071 +
  1.2072 +    /*
  1.2073 +     * Loop until we get the specified condition, unless the socket is
  1.2074 +     * asynchronous.
  1.2075 +     */
  1.2076 +    
  1.2077 +    do {
  1.2078 +	statusPB.ioCRefNum = driverRefNum;
  1.2079 +	statusPB.tcpStream = statePtr->tcpStream;
  1.2080 +	statusPB.csCode = TCPStatus;
  1.2081 +	err = PBControlSync((ParmBlkPtr) &statusPB);
  1.2082 +	if (err != noErr) {
  1.2083 +            /*
  1.2084 +             * I am not sure why it is right to return 1 - indicating success
  1.2085 +             * for synchronous sockets when an attempt to get status on the
  1.2086 +             * driver yeilds an error.   But it is CERTAINLY wrong for async
  1.2087 +             * sockect which have not yet connected.
  1.2088 +             */
  1.2089 +             
  1.2090 +	    if (statePtr->flags & TCP_ASYNC_CONNECT) {
  1.2091 +	        *errorCodePtr = EWOULDBLOCK;
  1.2092 +	        return 0;
  1.2093 +	    } else {
  1.2094 +	        statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE);
  1.2095 +	        return 1;
  1.2096 +	    }
  1.2097 +	}
  1.2098 +	statePtr->checkMask = 0;
  1.2099 +	
  1.2100 +	/*
  1.2101 +	 * The "6" below is the "connection being established" flag.  I couldn't
  1.2102 +	 * find a define for this in MacTCP.h, but that's what the programmer's
  1.2103 +	 * guide says.
  1.2104 +	 */
  1.2105 +	 
  1.2106 +	if ((statusPB.csParam.status.connectionState != 0)
  1.2107 +	        && (statusPB.csParam.status.connectionState != 4)
  1.2108 +	        && (statusPB.csParam.status.connectionState != 6)) {
  1.2109 +	    if (statusPB.csParam.status.amtUnreadData > 0) {
  1.2110 +	        statePtr->checkMask |= TCL_READABLE;
  1.2111 +	    }
  1.2112 +	    if (!(statePtr->flags & TCP_WRITING)
  1.2113 +		    && (statusPB.csParam.status.sendWindow - 
  1.2114 +			    statusPB.csParam.status.amtUnackedData) > 0) {
  1.2115 +	        statePtr->flags &= ~(TCP_ASYNC_CONNECT);
  1.2116 +	        statePtr->checkMask |= TCL_WRITABLE;
  1.2117 +	    }
  1.2118 +	    if (mask & statePtr->checkMask) {
  1.2119 +	        return 1;
  1.2120 +	    }
  1.2121 +        } else {
  1.2122 +            break;
  1.2123 +        }
  1.2124 +        
  1.2125 +	/*
  1.2126 +	 * Call the system to let other applications run while we
  1.2127 +	 * are waiting for this event to occur.
  1.2128 +	 */
  1.2129 +	
  1.2130 +	WaitNextEvent(0, &dummy, 1, NULL);
  1.2131 +    } while (!(statePtr->flags & TCP_ASYNC_SOCKET));
  1.2132 +    *errorCodePtr = EWOULDBLOCK;
  1.2133 +    return 0;
  1.2134 +} 
  1.2135 +
  1.2136 +/*
  1.2137 + *----------------------------------------------------------------------
  1.2138 + *
  1.2139 + * TcpAccept --
  1.2140 + *	Accept a TCP socket connection.  This is called by the event 
  1.2141 + *	loop, and it in turns calls any registered callbacks for this
  1.2142 + *	channel.
  1.2143 + *
  1.2144 + * Results:
  1.2145 + *	None.
  1.2146 + *
  1.2147 + * Side effects:
  1.2148 + *	Evals the Tcl script associated with the server socket.
  1.2149 + *
  1.2150 + *----------------------------------------------------------------------
  1.2151 + */
  1.2152 +
  1.2153 +static void
  1.2154 +TcpAccept(
  1.2155 +    TcpState *statePtr)
  1.2156 +{
  1.2157 +    TcpState *newStatePtr;
  1.2158 +    StreamPtr tcpStream;
  1.2159 +    char remoteHostname[255];
  1.2160 +    OSErr err;
  1.2161 +    ip_addr remoteAddress;
  1.2162 +    long remotePort;
  1.2163 +    char channelName[20];
  1.2164 +    
  1.2165 +    statePtr->flags &= ~TCP_LISTEN_CONNECT;
  1.2166 +    statePtr->checkMask &= ~TCL_READABLE;
  1.2167 +
  1.2168 +    /*
  1.2169 +     * Transfer sever stream to new connection.
  1.2170 +     */
  1.2171 +
  1.2172 +    tcpStream = statePtr->tcpStream;
  1.2173 +    newStatePtr = NewSocketInfo(tcpStream);
  1.2174 +    newStatePtr->tcpStream = tcpStream;
  1.2175 +    sprintf(channelName, "sock%d", socketNumber++);
  1.2176 +
  1.2177 +
  1.2178 +    newStatePtr->flags |= TCP_CONNECTED;
  1.2179 +    newStatePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1.2180 +            (ClientData) newStatePtr, (TCL_READABLE | TCL_WRITABLE));
  1.2181 +    Tcl_SetChannelBufferSize(newStatePtr->channel, socketBufferSize);
  1.2182 +    Tcl_SetChannelOption(NULL, newStatePtr->channel, "-translation",
  1.2183 +	    "auto crlf");
  1.2184 +
  1.2185 +    remoteAddress = statePtr->pb.csParam.open.remoteHost;
  1.2186 +    remotePort = statePtr->pb.csParam.open.remotePort;
  1.2187 +
  1.2188 +    /*
  1.2189 +     * Reopen passive connect.  Make new tcpStream the server.
  1.2190 +     */
  1.2191 +
  1.2192 +    ClearZombieSockets();
  1.2193 +    InitMacTCPParamBlock(&statePtr->pb, TCPCreate);
  1.2194 +    statePtr->pb.csParam.create.rcvBuff = ckalloc(socketBufferSize);
  1.2195 +    statePtr->pb.csParam.create.rcvBuffLen = socketBufferSize;
  1.2196 +    err = PBControlSync((ParmBlkPtr) &statePtr->pb);
  1.2197 +    if (err != noErr) {
  1.2198 +	/* 
  1.2199 +	 * Hmmm...  We can't reopen the server.  We'll go ahead
  1.2200 +	 * an continue - but we are kind of broken now...
  1.2201 +	 */
  1.2202 +	 Debugger();
  1.2203 +	 statePtr->tcpStream = -1;
  1.2204 +	 statePtr->flags |= TCP_SERVER_ZOMBIE;
  1.2205 +    }
  1.2206 +
  1.2207 +    tcpStream = statePtr->tcpStream = statePtr->pb.tcpStream;
  1.2208 +    
  1.2209 +    InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen);
  1.2210 +    statePtr->pb.tcpStream = tcpStream;
  1.2211 +    statePtr->pb.csParam.open.localHost = 0;
  1.2212 +    statePtr->pb.csParam.open.localPort = statePtr->port;
  1.2213 +    statePtr->pb.ioCompletion = completeUPP; 
  1.2214 +    statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
  1.2215 +    statePtr->flags |= TCP_LISTENING;
  1.2216 +    err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1.2217 +    /*
  1.2218 +     * TODO: deal with case where we can't recreate server socket...
  1.2219 +     */
  1.2220 +
  1.2221 +    /*
  1.2222 +     * Finally we run the accept procedure.  We must do this last to make
  1.2223 +     * sure we are in a nice clean state.  This Tcl code can do anything
  1.2224 +     * including closing the server or client sockets we've just delt with.
  1.2225 +     */
  1.2226 +
  1.2227 +    if (statePtr->acceptProc != NULL) {
  1.2228 +	sprintf(remoteHostname, "%d.%d.%d.%d", remoteAddress>>24,
  1.2229 +		remoteAddress>>16 & 0xff, remoteAddress>>8 & 0xff,
  1.2230 +		remoteAddress & 0xff);
  1.2231 +		
  1.2232 +	(statePtr->acceptProc)(statePtr->acceptProcData, newStatePtr->channel, 
  1.2233 +	    remoteHostname, remotePort);
  1.2234 +    }
  1.2235 +}
  1.2236 +
  1.2237 +/*
  1.2238 + *----------------------------------------------------------------------
  1.2239 + *
  1.2240 + * Tcl_GetHostName --
  1.2241 + *
  1.2242 + *	Returns the name of the local host.
  1.2243 + *
  1.2244 + * Results:
  1.2245 + *	A string containing the network name for this machine, or
  1.2246 + *	an empty string if we can't figure out the name.  The caller 
  1.2247 + *	must not modify or free this string.
  1.2248 + *
  1.2249 + * Side effects:
  1.2250 + *	None.
  1.2251 + *
  1.2252 + *----------------------------------------------------------------------
  1.2253 + */
  1.2254 +
  1.2255 +CONST char *
  1.2256 +Tcl_GetHostName()
  1.2257 +{
  1.2258 +    static int  hostnameInited = 0;
  1.2259 +    static char hostname[255];
  1.2260 +    ip_addr ourAddress;
  1.2261 +    Tcl_DString dString;
  1.2262 +    OSErr err;
  1.2263 +    
  1.2264 +    if (hostnameInited) {
  1.2265 +        return hostname;
  1.2266 +    }
  1.2267 +    
  1.2268 +    if (TclpHasSockets(NULL) == TCL_OK) {
  1.2269 +	err = GetLocalAddress(&ourAddress);
  1.2270 +	if (err == noErr) {
  1.2271 +	    /*
  1.2272 +	     * Search for the doman name and return it if found.  Otherwise, 
  1.2273 +	     * just print the IP number to a string and return that.
  1.2274 +	     */
  1.2275 +
  1.2276 +	    Tcl_DStringInit(&dString);
  1.2277 +	    err = ResolveAddress(ourAddress, &dString);
  1.2278 +	    if (err == noErr) {
  1.2279 +		strcpy(hostname, dString.string);
  1.2280 +	    } else {
  1.2281 +		sprintf(hostname, "%d.%d.%d.%d", ourAddress>>24, ourAddress>>16 & 0xff,
  1.2282 +		    ourAddress>>8 & 0xff, ourAddress & 0xff);
  1.2283 +	    }
  1.2284 +	    Tcl_DStringFree(&dString);
  1.2285 +	    
  1.2286 +	    hostnameInited = 1;
  1.2287 +	    return hostname;
  1.2288 +	}
  1.2289 +    }
  1.2290 +
  1.2291 +    hostname[0] = '\0';
  1.2292 +    hostnameInited = 1;
  1.2293 +    return hostname;
  1.2294 +}
  1.2295 +
  1.2296 +/*
  1.2297 + *----------------------------------------------------------------------
  1.2298 + *
  1.2299 + * ResolveAddress --
  1.2300 + *
  1.2301 + *	This function is used to resolve an ip address to it's full 
  1.2302 + *	domain name address.
  1.2303 + *
  1.2304 + * Results:
  1.2305 + *	An os err value.
  1.2306 + *
  1.2307 + * Side effects:
  1.2308 + *	Treats client data as int we set to true.
  1.2309 + *
  1.2310 + *----------------------------------------------------------------------
  1.2311 + */
  1.2312 +
  1.2313 +static OSErr 
  1.2314 +ResolveAddress(
  1.2315 +    ip_addr tcpAddress, 	/* Address to resolve. */
  1.2316 +    Tcl_DString *dsPtr)		/* Returned address in string. */
  1.2317 +{
  1.2318 +    int i;
  1.2319 +    EventRecord dummy;
  1.2320 +    DNRState dnrState;
  1.2321 +    OSErr err;
  1.2322 +
  1.2323 +    /*
  1.2324 +     * Call AddrToName to resolve our ip address to our domain name.
  1.2325 +     * The call is async, so we must wait for a callback to tell us
  1.2326 +     * when to continue.
  1.2327 +     */
  1.2328 +
  1.2329 +     for (i = 0; i < NUM_ALT_ADDRS; i++) {
  1.2330 +	dnrState.hostInfo.addr[i] = 0;
  1.2331 +     }
  1.2332 +    dnrState.done = 0;
  1.2333 +    GetCurrentProcess(&(dnrState.psn));
  1.2334 +    err = AddrToName(tcpAddress, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
  1.2335 +    if (err == cacheFault) {
  1.2336 +	while (!dnrState.done) {
  1.2337 +	    WaitNextEvent(0, &dummy, 1, NULL);
  1.2338 +	}
  1.2339 +    }
  1.2340 +    
  1.2341 +    /*
  1.2342 +     * If there is no error in finding the domain name we set the
  1.2343 +     * result into the dynamic string.  We also work around a bug in
  1.2344 +     * MacTcp where an extranious '.' may be found at the end of the name.
  1.2345 +     */
  1.2346 +
  1.2347 +    if (dnrState.hostInfo.rtnCode == noErr) {
  1.2348 +	i = strlen(dnrState.hostInfo.cname) - 1;
  1.2349 +	if (dnrState.hostInfo.cname[i] == '.') {
  1.2350 +	    dnrState.hostInfo.cname[i] = '\0';
  1.2351 +	}
  1.2352 +	Tcl_DStringAppend(dsPtr, dnrState.hostInfo.cname, -1);
  1.2353 +    }
  1.2354 +    
  1.2355 +    return dnrState.hostInfo.rtnCode;
  1.2356 +}
  1.2357 +
  1.2358 +/*
  1.2359 + *----------------------------------------------------------------------
  1.2360 + *
  1.2361 + * DNRCompletionRoutine --
  1.2362 + *
  1.2363 + *	This function is called when the Domain Name Server is done
  1.2364 + *	seviceing our request.  It just sets a flag that we can poll
  1.2365 + *	in functions like Tcl_GetHostName to let them know to continue.
  1.2366 + *
  1.2367 + * Results:
  1.2368 + *	None.
  1.2369 + *
  1.2370 + * Side effects:
  1.2371 + *	Treats client data as int we set to true.
  1.2372 + *
  1.2373 + *----------------------------------------------------------------------
  1.2374 + */
  1.2375 +
  1.2376 +static pascal void 
  1.2377 +DNRCompletionRoutine(
  1.2378 +    struct hostInfo *hostinfoPtr, 	/* Host infor struct. */
  1.2379 +    DNRState *dnrStatePtr)		/* Completetion state. */
  1.2380 +{
  1.2381 +    dnrStatePtr->done = true;
  1.2382 +    WakeUpProcess(&(dnrStatePtr->psn));
  1.2383 +}
  1.2384 +
  1.2385 +/*
  1.2386 + *----------------------------------------------------------------------
  1.2387 + *
  1.2388 + * CleanUpExitProc --
  1.2389 + *
  1.2390 + *	This procedure is invoked as an exit handler when ExitToShell
  1.2391 + *	is called.  It aborts any lingering socket connections.  This 
  1.2392 + *	must be called or the Mac OS will more than likely crash.
  1.2393 + *
  1.2394 + * Results:
  1.2395 + *	None.
  1.2396 + *
  1.2397 + * Side effects:
  1.2398 + *	None.
  1.2399 + *
  1.2400 + *----------------------------------------------------------------------
  1.2401 + */
  1.2402 +
  1.2403 +static pascal void
  1.2404 +CleanUpExitProc()
  1.2405 +{
  1.2406 +    TCPiopb exitPB;
  1.2407 +    TcpState *statePtr;
  1.2408 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.2409 +
  1.2410 +    while (tsdPtr->socketList != NULL) {
  1.2411 +	statePtr = tsdPtr->socketList;
  1.2412 +	tsdPtr->socketList = statePtr->nextPtr;
  1.2413 +
  1.2414 +	/*
  1.2415 +	 * Close and Release the connection.
  1.2416 +	 */
  1.2417 +
  1.2418 +	exitPB.ioCRefNum = driverRefNum;
  1.2419 +	exitPB.csCode = TCPClose;
  1.2420 +	exitPB.tcpStream = statePtr->tcpStream;
  1.2421 +	exitPB.csParam.close.ulpTimeoutValue = 60 /* seconds */;
  1.2422 +	exitPB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */;
  1.2423 +	exitPB.csParam.close.validityFlags = timeoutValue | timeoutAction;
  1.2424 +	exitPB.ioCompletion = NULL; 
  1.2425 +	PBControlSync((ParmBlkPtr) &exitPB);
  1.2426 +
  1.2427 +	exitPB.ioCRefNum = driverRefNum;
  1.2428 +	exitPB.csCode = TCPRelease;
  1.2429 +	exitPB.tcpStream = statePtr->tcpStream;
  1.2430 +	exitPB.ioCompletion = NULL; 
  1.2431 +	PBControlSync((ParmBlkPtr) &exitPB);
  1.2432 +    }
  1.2433 +}
  1.2434 +
  1.2435 +/*
  1.2436 + *----------------------------------------------------------------------
  1.2437 + *
  1.2438 + * GetHostFromString --
  1.2439 + *
  1.2440 + *	Looks up the passed in domain name in the domain resolver.  It
  1.2441 + *	can accept strings of two types: 1) the ip number in string
  1.2442 + *	format, or 2) the domain name.
  1.2443 + *
  1.2444 + * Results:
  1.2445 + *	We return a ip address or 0 if there was an error or the 
  1.2446 + *	domain does not exist.
  1.2447 + *
  1.2448 + * Side effects:
  1.2449 + *	None.
  1.2450 + *
  1.2451 + *----------------------------------------------------------------------
  1.2452 + */
  1.2453 +
  1.2454 +static OSErr
  1.2455 +GetHostFromString(
  1.2456 +    CONST char *name, 		/* Host in string form. */
  1.2457 +    ip_addr *address)		/* Returned IP address. */
  1.2458 +{
  1.2459 +    OSErr err;
  1.2460 +    int i;
  1.2461 +    EventRecord dummy;
  1.2462 +    DNRState dnrState;
  1.2463 +	
  1.2464 +    if (TclpHasSockets(NULL) != TCL_OK) {
  1.2465 +	return 0;
  1.2466 +    }
  1.2467 +
  1.2468 +    /*
  1.2469 +     * Call StrToAddr to get the ip number for the passed in domain
  1.2470 +     * name.  The call is async, so we must wait for a callback to 
  1.2471 +     * tell us when to continue.
  1.2472 +     */
  1.2473 +
  1.2474 +    for (i = 0; i < NUM_ALT_ADDRS; i++) {
  1.2475 +	dnrState.hostInfo.addr[i] = 0;
  1.2476 +    }
  1.2477 +    dnrState.done = 0;
  1.2478 +    GetCurrentProcess(&(dnrState.psn));
  1.2479 +    err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
  1.2480 +    if (err == cacheFault) {
  1.2481 +	while (!dnrState.done) {
  1.2482 +	    WaitNextEvent(0, &dummy, 1, NULL);
  1.2483 +	}
  1.2484 +    }
  1.2485 +    
  1.2486 +    /*
  1.2487 +     * For some reason MacTcp may return a cachFault a second time via
  1.2488 +     * the hostinfo block.  This seems to be a bug in MacTcp.  In this case 
  1.2489 +     * we run StrToAddr again - which seems to then work just fine.
  1.2490 +     */
  1.2491 +
  1.2492 +    if (dnrState.hostInfo.rtnCode == cacheFault) {
  1.2493 +	dnrState.done = 0;
  1.2494 +	err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
  1.2495 +	if (err == cacheFault) {
  1.2496 +	    while (!dnrState.done) {
  1.2497 +		WaitNextEvent(0, &dummy, 1, NULL);
  1.2498 +	    }
  1.2499 +	}
  1.2500 +    }
  1.2501 +
  1.2502 +    if (dnrState.hostInfo.rtnCode == noErr) {
  1.2503 +	*address = dnrState.hostInfo.addr[0];
  1.2504 +    }
  1.2505 +    
  1.2506 +    return dnrState.hostInfo.rtnCode;
  1.2507 +}
  1.2508 +
  1.2509 +/*
  1.2510 + *----------------------------------------------------------------------
  1.2511 + *
  1.2512 + * IOCompletionRoutine --
  1.2513 + *
  1.2514 + *	This function is called when an asynchronous socket operation
  1.2515 + *	completes.  Since this routine runs as an interrupt handler, 
  1.2516 + *	it will simply set state to tell the notifier that this socket
  1.2517 + *	is now ready for action.  Note that this function is running at
  1.2518 + *	interupt time and can't allocate memory or do much else except 
  1.2519 + *      set state.
  1.2520 + *
  1.2521 + * Results:
  1.2522 + *	None.
  1.2523 + *
  1.2524 + * Side effects:
  1.2525 + *	Sets some state in the socket state.  May also wake the process
  1.2526 + *	if we are not currently running.
  1.2527 + *
  1.2528 + *----------------------------------------------------------------------
  1.2529 + */
  1.2530 +
  1.2531 +static void
  1.2532 +IOCompletionRoutine(
  1.2533 +    TCPiopb *pbPtr)		/* Tcp parameter block. */
  1.2534 +{
  1.2535 +    TcpState *statePtr;
  1.2536 +    
  1.2537 +    if (pbPtr->csCode == TCPSend) {
  1.2538 +    	statePtr = (TcpState *) pbPtr->csParam.send.userDataPtr;
  1.2539 +    } else {
  1.2540 +	statePtr = (TcpState *) pbPtr->csParam.open.userDataPtr;
  1.2541 +    }
  1.2542 +    
  1.2543 +    /*
  1.2544 +     * Always wake the process in case it's in WaitNextEvent.
  1.2545 +     * If an error has a occured - just return.  We will deal
  1.2546 +     * with the problem later.
  1.2547 +     */
  1.2548 +
  1.2549 +    WakeUpProcess(&statePtr->psn);
  1.2550 +    if (pbPtr->ioResult != noErr) {
  1.2551 +	return;
  1.2552 +    }
  1.2553 +    
  1.2554 +    if (statePtr->flags & TCP_ASYNC_CONNECT) {
  1.2555 +	statePtr->flags &= ~TCP_ASYNC_CONNECT;
  1.2556 +	statePtr->flags |= TCP_CONNECTED;
  1.2557 +	statePtr->checkMask |= TCL_READABLE & TCL_WRITABLE;
  1.2558 +    } else if (statePtr->flags & TCP_LISTENING) {
  1.2559 +	if (statePtr->port == 0) {
  1.2560 +	    Debugger();
  1.2561 +	}
  1.2562 +	statePtr->flags &= ~TCP_LISTENING;
  1.2563 +	statePtr->flags |= TCP_LISTEN_CONNECT;
  1.2564 +	statePtr->checkMask |= TCL_READABLE;
  1.2565 +    } else if (statePtr->flags & TCP_WRITING) {
  1.2566 +	statePtr->flags &= ~TCP_WRITING;
  1.2567 +	statePtr->checkMask |= TCL_WRITABLE;
  1.2568 +	if (!(statePtr->flags & TCP_CONNECTED)) {
  1.2569 +	    InitMacTCPParamBlock(&statePtr->pb, TCPClose);
  1.2570 +    	    statePtr->pb.tcpStream = statePtr->tcpStream;
  1.2571 +    	    statePtr->pb.ioCompletion = closeUPP; 
  1.2572 +    	    statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr;
  1.2573 +    	    if (PBControlAsync((ParmBlkPtr) &statePtr->pb) != noErr) {
  1.2574 +	        statePtr->flags |= TCP_RELEASE;
  1.2575 +    	    }
  1.2576 +	}
  1.2577 +    }
  1.2578 +}
  1.2579 +
  1.2580 +/*
  1.2581 + *----------------------------------------------------------------------
  1.2582 + *
  1.2583 + * GetLocalAddress --
  1.2584 + *
  1.2585 + *	Get the IP address for this machine.  The result is cached so
  1.2586 + *	the result is returned quickly after the first call.
  1.2587 + *
  1.2588 + * Results:
  1.2589 + *	Macintosh error code.
  1.2590 + *
  1.2591 + * Side effects:
  1.2592 + *	None.
  1.2593 + *
  1.2594 + *----------------------------------------------------------------------
  1.2595 + */
  1.2596 +
  1.2597 +static OSErr 
  1.2598 +GetLocalAddress(
  1.2599 +    unsigned long *addr)	/* Returns host IP address. */
  1.2600 +{
  1.2601 +    struct GetAddrParamBlock pBlock;
  1.2602 +    OSErr err = noErr;
  1.2603 +    static unsigned long localAddress = 0;
  1.2604 +
  1.2605 +    if (localAddress == 0) {
  1.2606 +	memset(&pBlock, 0, sizeof(pBlock));
  1.2607 +	pBlock.ioResult = 1;
  1.2608 +	pBlock.csCode = ipctlGetAddr;
  1.2609 +	pBlock.ioCRefNum = driverRefNum;
  1.2610 +	err = PBControlSync((ParmBlkPtr) &pBlock);
  1.2611 +
  1.2612 +	if (err != noErr) {
  1.2613 +	    return err;
  1.2614 +	}
  1.2615 +	localAddress = pBlock.ourAddress;
  1.2616 +    }
  1.2617 +    
  1.2618 +    *addr = localAddress;
  1.2619 +    return noErr;
  1.2620 +}
  1.2621 +
  1.2622 +/*
  1.2623 + *----------------------------------------------------------------------
  1.2624 + *
  1.2625 + * GetBufferSize --
  1.2626 + *
  1.2627 + *	Get the appropiate buffer size for our machine & network.  This
  1.2628 + *	value will be used by the rest of Tcl & the MacTcp driver for
  1.2629 + *	the size of its buffers.  If out method for determining the
  1.2630 + *	optimal buffer size fails for any reason - we return a 
  1.2631 + *	reasonable default.
  1.2632 + *
  1.2633 + * Results:
  1.2634 + *	Size of optimal buffer in bytes.
  1.2635 + *
  1.2636 + * Side effects:
  1.2637 + *	None.
  1.2638 + *
  1.2639 + *----------------------------------------------------------------------
  1.2640 + */
  1.2641 +
  1.2642 +static long 
  1.2643 +GetBufferSize()
  1.2644 +{
  1.2645 +    UDPiopb iopb;
  1.2646 +    OSErr err = noErr;
  1.2647 +    long bufferSize;
  1.2648 +	
  1.2649 +    memset(&iopb, 0, sizeof(iopb));
  1.2650 +    err = GetLocalAddress(&iopb.csParam.mtu.remoteHost);
  1.2651 +    if (err != noErr) {
  1.2652 +	return CHANNEL_BUF_SIZE;
  1.2653 +    }
  1.2654 +    iopb.ioCRefNum = driverRefNum;
  1.2655 +    iopb.csCode = UDPMaxMTUSize;
  1.2656 +    err = PBControlSync((ParmBlkPtr)&iopb);
  1.2657 +    if (err != noErr) {
  1.2658 +	return CHANNEL_BUF_SIZE;
  1.2659 +    }
  1.2660 +    bufferSize = (iopb.csParam.mtu.mtuSize * 4) + 1024;
  1.2661 +    if (bufferSize < CHANNEL_BUF_SIZE) {
  1.2662 +	bufferSize = CHANNEL_BUF_SIZE;
  1.2663 +    }
  1.2664 +    return bufferSize;
  1.2665 +}
  1.2666 +
  1.2667 +/*
  1.2668 + *----------------------------------------------------------------------
  1.2669 + *
  1.2670 + * TclSockGetPort --
  1.2671 + *
  1.2672 + *	Maps from a string, which could be a service name, to a port.
  1.2673 + *	Used by socket creation code to get port numbers and resolve
  1.2674 + *	registered service names to port numbers.
  1.2675 + *
  1.2676 + * Results:
  1.2677 + *	A standard Tcl result.  On success, the port number is
  1.2678 + *	returned in portPtr. On failure, an error message is left in
  1.2679 + *	the interp's result.
  1.2680 + *
  1.2681 + * Side effects:
  1.2682 + *	None.
  1.2683 + *
  1.2684 + *----------------------------------------------------------------------
  1.2685 + */
  1.2686 +
  1.2687 +int
  1.2688 +TclSockGetPort(
  1.2689 +    Tcl_Interp *interp, 	/* Interp for error messages. */
  1.2690 +    char *string, 		/* Integer or service name */
  1.2691 +    char *proto, 		/* "tcp" or "udp", typically - 
  1.2692 +    				 * ignored on Mac - assumed to be tcp */
  1.2693 +    int *portPtr)		/* Return port number */
  1.2694 +{
  1.2695 +    PortInfo *portInfoPtr = NULL;
  1.2696 +    
  1.2697 +    if (Tcl_GetInt(interp, string, portPtr) == TCL_OK) {
  1.2698 +	if (*portPtr > 0xFFFF) {
  1.2699 +	    Tcl_AppendResult(interp, "couldn't open socket: port number too high",
  1.2700 +                (char *) NULL);
  1.2701 +	    return TCL_ERROR;
  1.2702 +	}
  1.2703 +	if (*portPtr < 0) {
  1.2704 +	    Tcl_AppendResult(interp, "couldn't open socket: negative port number",
  1.2705 +                (char *) NULL);
  1.2706 +	    return TCL_ERROR;
  1.2707 +	}
  1.2708 +	return TCL_OK;
  1.2709 +    }
  1.2710 +    for (portInfoPtr = portServices; portInfoPtr->name != NULL; portInfoPtr++) {
  1.2711 +	if (!strcmp(portInfoPtr->name, string)) {
  1.2712 +	    break;
  1.2713 +	}
  1.2714 +    }
  1.2715 +    if (portInfoPtr != NULL && portInfoPtr->name != NULL) {
  1.2716 +	*portPtr = portInfoPtr->port;
  1.2717 +	Tcl_ResetResult(interp);
  1.2718 +	return TCL_OK;
  1.2719 +    }
  1.2720 +    
  1.2721 +    return TCL_ERROR;
  1.2722 +}
  1.2723 +
  1.2724 +/*
  1.2725 + *----------------------------------------------------------------------
  1.2726 + *
  1.2727 + * ClearZombieSockets --
  1.2728 + *
  1.2729 + *	This procedure looks through the socket list and removes the
  1.2730 + *	first stream it finds that is ready for release. This procedure 
  1.2731 + *	should be called before we ever try to create new Tcp streams
  1.2732 + *	to ensure we can least allocate one stream.
  1.2733 + *
  1.2734 + * Results:
  1.2735 + *	None.
  1.2736 + *
  1.2737 + * Side effects:
  1.2738 + *	Tcp streams may be released.
  1.2739 + *
  1.2740 + *----------------------------------------------------------------------
  1.2741 + */
  1.2742 +
  1.2743 +static void
  1.2744 +ClearZombieSockets()
  1.2745 +{
  1.2746 +    TcpState *statePtr;
  1.2747 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1.2748 +
  1.2749 +    for (statePtr = tsdPtr->socketList; statePtr != NULL;
  1.2750 +	    statePtr = statePtr->nextPtr) {
  1.2751 +	if (statePtr->flags & TCP_RELEASE) {
  1.2752 +	    SocketFreeProc(statePtr);
  1.2753 +	    return;
  1.2754 +	}
  1.2755 +    }
  1.2756 +}
  1.2757 +
  1.2758 +
  1.2759 +/*
  1.2760 + *----------------------------------------------------------------------
  1.2761 + *
  1.2762 + * NotifyRoutine --
  1.2763 + *
  1.2764 + *	This routine does nothing currently, and is not being used.  But
  1.2765 + *      it is useful if you want to experiment with what MacTCP thinks that
  1.2766 + *      it is doing...
  1.2767 + *
  1.2768 + * Results:
  1.2769 + *	None.
  1.2770 + *
  1.2771 + * Side effects:
  1.2772 + *	None.
  1.2773 + *
  1.2774 + *----------------------------------------------------------------------
  1.2775 + */
  1.2776 +pascal void NotifyRoutine (
  1.2777 +    StreamPtr tcpStream,
  1.2778 +    unsigned short eventCode,
  1.2779 +    Ptr userDataPtr,
  1.2780 +    unsigned short terminReason,
  1.2781 +    struct ICMPReport *icmpMsg)
  1.2782 +{
  1.2783 +    StreamPtr localTcpStream;
  1.2784 +    unsigned short localEventCode;
  1.2785 +    unsigned short localTerminReason;
  1.2786 +    struct ICMPReport localIcmpMsg;
  1.2787 +
  1.2788 +    localTcpStream = tcpStream;
  1.2789 +    localEventCode = eventCode;
  1.2790 +    localTerminReason = terminReason;
  1.2791 +    localIcmpMsg = *icmpMsg;
  1.2792 +        
  1.2793 +}