Tcl Source Code

Artifact [bb30472909]
Login

Artifact bb30472909e8e2e8750832e045b987e833336a24:

Attachment "halfclosesock-2.patch" to ticket [219159ffff] added by andreas_kupries 2008-11-28 06:47:12.
--- tcl86.orig/generic/tcl.decls	2008-11-27 14:48:17.000000000 -0800
+++ tcl86/generic/tcl.decls	2008-11-27 14:59:25.000000000 -0800
@@ -2204,6 +2204,11 @@
 	    int *objcPtr, Tcl_Obj *const *objv, Tcl_Obj ***remObjv)
 }
 
+# TIP#332, Half Close made public
+declare 605 generic {
+    int Tcl_CloseEx(Tcl_Interp *interp, Tcl_Channel chan, int flags)
+}
+
 ##############################################################################
 
 # Define the platform specific public Tcl interface.  These functions are
--- tcl86.orig/generic/tclDecls.h	2008-11-27 14:48:17.000000000 -0800
+++ tcl86/generic/tclDecls.h	2008-11-27 15:27:01.000000000 -0800
@@ -3657,6 +3657,12 @@
 				const Tcl_ArgvInfo * argTable, int * objcPtr,
 				Tcl_Obj *const * objv, Tcl_Obj *** remObjv);
 #endif
+#ifndef Tcl_CloseEx_TCL_DECLARED
+#define Tcl_CloseEx_TCL_DECLARED
+/* 605 */
+EXTERN int		Tcl_CloseEx (Tcl_Interp * interp, Tcl_Channel chan,
+				int flags);
+#endif
 
 typedef struct TclStubHooks {
     const struct TclPlatStubs *tclPlatStubs;
@@ -4321,6 +4327,7 @@
     int (*tcl_SetEnsembleParameterList) (Tcl_Interp * interp, Tcl_Command token, Tcl_Obj * paramList); /* 602 */
     int (*tcl_GetEnsembleParameterList) (Tcl_Interp * interp, Tcl_Command token, Tcl_Obj ** paramListPtr); /* 603 */
     int (*tcl_ParseArgsObjv) (Tcl_Interp * interp, const Tcl_ArgvInfo * argTable, int * objcPtr, Tcl_Obj *const * objv, Tcl_Obj *** remObjv); /* 604 */
+    int (*tcl_CloseEx) (Tcl_Interp * interp, Tcl_Channel chan, int flags); /* 605 */
 } TclStubs;
 
 #if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
@@ -6813,6 +6820,10 @@
 #define Tcl_ParseArgsObjv \
 	(tclStubsPtr->tcl_ParseArgsObjv) /* 604 */
 #endif
+#ifndef Tcl_CloseEx
+#define Tcl_CloseEx \
+	(tclStubsPtr->tcl_CloseEx) /* 605 */
+#endif
 
 #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
 
--- tcl86.orig/generic/tclIO.c	2008-11-27 14:48:17.000000000 -0800
+++ tcl86/generic/tclIO.c	2008-11-27 15:42:36.000000000 -0800
@@ -3028,6 +3028,132 @@
 /*
  *----------------------------------------------------------------------
  *
+ * Tcl_CloseEx --
+ *
+ *	Half closes a channel.
+ *
+ * Results:
+ *	A standard Tcl result.
+ *
+ * Side effects:
+ *	Closes one direction of the channel.
+ *
+ * NOTE:
+ *	Tcl_CloseEx closes the specified direction of the channel as far as
+ *	the user is concerned. The channel keeps existing however. You cannot
+ *	calls this function to close the last possible direction of the
+ *	channel. Use Tcl_Close for that.
+ *
+ *----------------------------------------------------------------------
+ */
+
+	/* ARGSUSED */
+int
+Tcl_CloseEx(
+    Tcl_Interp *interp,		/* Interpreter for errors. */
+    Tcl_Channel chan,		/* The channel being closed. May still be used by some interpreter */
+    int flags)                  /* Flags telling us which side to close. */
+{
+    Channel *chanPtr;		/* The real IO channel. */
+    ChannelState *statePtr;	/* State of real IO channel. */
+    int result;			/* Of calling FlushChannel. */
+
+    if (chan == NULL) {
+	return TCL_OK;
+    }
+
+    /* TODO: assert flags validity ? */
+
+    chanPtr = (Channel *) chan;
+    statePtr = chanPtr->state;
+
+    /*
+     * Does the channel support half-close anyway ? Error if not.
+     */
+
+    if (!chanPtr->typePtr->close2Proc) {
+	Tcl_AppendResult (interp, "Half-close of channels not supported by ",
+			  chanPtr->typePtr->typeName, "s", NULL);
+	return TCL_ERROR;
+    }
+
+    /*
+     * Is the channel unstacked ? If not we fail.
+     */
+
+    if (chanPtr != statePtr->topChanPtr) {
+	Tcl_AppendResult (interp,
+			  "Half-close not applicable to stack of transformations",
+			  NULL);
+	return TCL_ERROR;
+    }
+    
+    /*
+     * Check direction against channel mode. It is an error if we try to close
+     * a direction not supported by the channel (already closed, or never
+     * opened for that direction).
+     */
+
+    if (!(statePtr->flags & (TCL_READABLE | TCL_WRITABLE) & flags)) {
+	const char *msg;
+	if (flags & TCL_CLOSE_READ) {
+	    msg = "read";
+	} else {
+	    msg = "write";
+	}
+	Tcl_AppendResult (interp, "Half-close of ", msg,
+			      "-side not possible, not supported by channel",
+			      NULL);
+	return TCL_ERROR;
+    }
+
+    /*
+     * A user may try to call half-close from within a channel close
+     * handler. That won't do.
+     */
+
+    if (statePtr->flags & CHANNEL_INCLOSE) {
+	if (interp) {
+	    Tcl_AppendResult(interp, "Illegal recursive call to close "
+		    "through close-handler of channel", NULL);
+	}
+	return TCL_ERROR;
+    }
+
+    /*
+     * Finally do what is asked of us.
+     */
+
+    result = chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp,
+					  flags);
+
+    /*
+     * TIP #219.
+     * Capture error messages put by the driver into the bypass area and put
+     * them into the regular interpreter result.
+     *
+     * Notes: Due to the assertion of CHANNEL_CLOSED in the flags
+     * FlushChannel() has called CloseChannel() and thus freed all the channel
+     * structures. We must not try to access "chan" anymore, hence the NULL
+     * argument in the call below. The only place which may still contain a
+     * message is the interpreter itself, and "CloseChannel" made sure to lift
+     * any channel message it generated into it.
+     */
+
+    if (TclChanCaughtErrorBypass(interp, NULL)) {
+	result = EINVAL;
+    }
+
+    if (result != 0) {
+	return TCL_ERROR;
+    }
+
+    return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * Tcl_ClearChannelHandlers --
  *
  *	Removes all channel handlers and event scripts from the channel,
--- tcl86.orig/generic/tclIOCmd.c	2008-11-27 14:45:57.000000000 -0800
+++ tcl86/generic/tclIOCmd.c	2008-11-27 15:41:35.000000000 -0800
@@ -648,8 +648,8 @@
 {
     Tcl_Channel chan;		/* The channel to close. */
 
-    if (objc != 2) {
-	Tcl_WrongNumArgs(interp, 1, objv, "channelId");
+    if ((objc != 2) && (objc != 3)) {
+	Tcl_WrongNumArgs(interp, 1, objv, "channelId ?direction?");
 	return TCL_ERROR;
     }
 
@@ -657,6 +657,48 @@
 	return TCL_ERROR;
     }
 
+    if (objc == 3) {
+	int optionIndex, dir;
+	static const char *const dirOptions[] = {
+	    "read", "write", NULL
+	};
+	static int dirArray[] = {TCL_CLOSE_READ, TCL_CLOSE_WRITE};
+
+	/*
+	 * Get direction requested to close, and check syntax.
+	 */
+
+	if (Tcl_GetIndexFromObj(interp, objv[3], dirOptions, "direction", 0,
+		&optionIndex) != TCL_OK) {
+	    return TCL_ERROR;
+	}
+	dir = dirArray[optionIndex];
+
+	/*
+	 * Check direction against channel mode. It is an error if we try to
+	 * close a direction not supported by the channel (already closed, or
+	 * never opened for that direction).
+	 */
+
+	if (!(dir & Tcl_GetChannelMode (chan))) {
+	    Tcl_AppendResult (interp, "Half-close of ", dirOptions[optionIndex],
+			      "-side not possible, not supported by channel",
+			      NULL);
+	    return TCL_ERROR;
+	}
+
+	/*
+	 * Special handling is needed if and only if the channel mode supports
+	 * more than the direction to close. Because if the close the last
+	 * direction suppported we can and will go through the regular
+	 * process.
+	 */
+
+	if ((dir & Tcl_GetChannelMode (chan)) != dir) {
+	    return Tcl_CloseEx (interp, chan, dir) != TCL_OK;
+	}
+    }
+
     if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
 	/*
 	 * If there is an error message and it ends with a newline, remove the
--- tcl86.orig/generic/tclStubInit.c	2008-11-27 14:45:57.000000000 -0800
+++ tcl86/generic/tclStubInit.c	2008-11-27 15:27:02.000000000 -0800
@@ -1130,6 +1130,7 @@
     Tcl_SetEnsembleParameterList, /* 602 */
     Tcl_GetEnsembleParameterList, /* 603 */
     Tcl_ParseArgsObjv, /* 604 */
+    Tcl_CloseEx, /* 605 */
 };
 
 /* !END!: Do not edit above this line. */
--- tcl86.orig/unix/tclUnixChan.c	2008-11-27 14:45:52.000000000 -0800
+++ tcl86/unix/tclUnixChan.c	2008-11-27 15:06:07.000000000 -0800
@@ -218,6 +218,9 @@
 static int		TcpBlockModeProc(ClientData data, int mode);
 static int		TcpCloseProc(ClientData instanceData,
 			    Tcl_Interp *interp);
+static int		TcpClose2Proc(ClientData instanceData,
+				      Tcl_Interp *interp,
+				      int flags);
 static int		TcpGetHandleProc(ClientData instanceData,
 			    int direction, ClientData *handlePtr);
 static int		TcpGetOptionProc(ClientData instanceData,
@@ -318,7 +321,7 @@
     TcpGetOptionProc,		/* Get option proc. */
     TcpWatchProc,		/* Initialize notifier. */
     TcpGetHandleProc,		/* Get OS handles out of channel. */
-    NULL,			/* close2proc. */
+    TcpClose2Proc,		/* Close2 proc. */
     TcpBlockModeProc,		/* Set blocking or non-blocking mode.*/
     NULL,			/* flush proc. */
     NULL,			/* handler proc. */
@@ -2018,6 +2021,57 @@
 /*
  *----------------------------------------------------------------------
  *
+ * TcpClose2Proc --
+ *
+ *	This function is called by the generic IO level to perform the channel
+ *	type specific part of a half-close: namely, a shutdown() on a socket.
+ *
+ * Results:
+ *	0 if successful, the value of errno if failed.
+ *
+ * Side effects:
+ *	Shuts down one side of the socket.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TcpClose2Proc(
+    ClientData instanceData,	/* The socket to close. */
+    Tcl_Interp *interp,		/* For error reporting. */
+    int flags)			/* Flags that indicate which side to close. */
+{
+    TcpState *statePtr = (TcpState *) instanceData;
+    int errorCode = 0;
+    int sd;
+
+    /*
+     * Shutdown the OS socket handle.
+     */
+    switch(flags)
+	{
+	case TCL_CLOSE_READ:
+	    sd=SHUT_RD;
+	    break;
+	case TCL_CLOSE_WRITE:
+	    sd=SHUT_WR;
+	    break;
+	default:
+	    if (interp) {
+		Tcl_AppendResult(interp, "Socket close2proc called bidirectionally", NULL);
+	    }
+	    return TCL_ERROR;
+	}
+    if (shutdown(statePtr->fd,sd)<0) {
+	errorCode = errno;
+    }
+
+    return errorCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * TcpGetOptionProc --
  *
  *	Computes an option value for a TCP socket based channel, or a list of
--- tcl86.orig/win/tclWinSock.c	2008-11-27 14:45:52.000000000 -0800
+++ tcl86/win/tclWinSock.c	2008-11-27 14:49:43.000000000 -0800
@@ -168,6 +168,7 @@
 static Tcl_EventSetupProc	SocketSetupProc;
 static Tcl_DriverBlockModeProc	TcpBlockProc;
 static Tcl_DriverCloseProc	TcpCloseProc;
+static Tcl_DriverClose2Proc	TcpClose2Proc;
 static Tcl_DriverSetOptionProc	TcpSetOptionProc;
 static Tcl_DriverGetOptionProc	TcpGetOptionProc;
 static Tcl_DriverInputProc	TcpInputProc;
@@ -191,7 +192,7 @@
     TcpGetOptionProc,	    /* Get option proc. */
     TcpWatchProc,	    /* Set up notifier to watch this channel. */
     TcpGetHandleProc,	    /* Get an OS handle from channel. */
-    NULL,		    /* close2proc. */
+    TcpClose2Proc,	    /* Close2proc. */
     TcpBlockProc,	    /* Set socket into (non-)blocking mode. */
     NULL,		    /* flush proc. */
     NULL,		    /* handler proc. */
@@ -818,6 +819,58 @@
 /*
  *----------------------------------------------------------------------
  *
+ * TcpClose2Proc --
+ *
+ *	This function is called by the generic IO level to perform the channel
+ *	type specific part of a half-close: namely, a shutdown() on a socket.
+ *
+ * Results:
+ *	0 if successful, the value of errno if failed.
+ *
+ * Side effects:
+ *	Shuts down one side of the socket.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+TcpClose2Proc(
+    ClientData instanceData,	/* The socket to close. */
+    Tcl_Interp *interp,		/* For error reporting. */
+    int flags)			/* Flags that indicate which side to close. */
+{
+    SocketInfo *infoPtr = (SocketInfo *) instanceData;
+    int errorCode = 0;
+    int sd;
+
+    /*
+     * Shutdown the OS socket handle.
+     */
+    switch(flags)
+	{
+	case TCL_CLOSE_READ:
+	    sd=SD_RECEIVE;
+	    break;
+	case TCL_CLOSE_WRITE:
+	    sd=SD_SEND;
+	    break;
+	default:
+	    if (interp) {
+		Tcl_AppendResult(interp, "Socket close2proc called bidirectionally", NULL);
+	    }
+	    return TCL_ERROR;
+	}
+    if (shutdown(infoPtr->socket,sd) == SOCKET_ERROR) {
+	TclWinConvertWSAError((DWORD) WSAGetLastError());
+	errorCode = Tcl_GetErrno();
+    }
+
+    return errorCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * NewSocketInfo --
  *
  *	This function allocates and initializes a new SocketInfo structure.