1/* 2 * tclXwinDup.c 3 * 4 * Support for the dup command on Windows. 5 *----------------------------------------------------------------------------- 6 * Copyright 1991-1999 Karl Lehenbauer and Mark Diekhans. 7 * 8 * Permission to use, copy, modify, and distribute this software and its 9 * documentation for any purpose and without fee is hereby granted, provided 10 * that the above copyright notice appear in all copies. Karl Lehenbauer and 11 * Mark Diekhans make no representations about the suitability of this 12 * software for any purpose. It is provided "as is" without express or 13 * implied warranty. 14 *----------------------------------------------------------------------------- 15 * $Id: tclXwinDup.c,v 1.1 2001/10/24 23:31:50 hobbs Exp $ 16 *----------------------------------------------------------------------------- 17 */ 18 19#include "tclExtdInt.h" 20 21 22/*----------------------------------------------------------------------------- 23 * ConvertChannelName -- 24 * 25 * Convert a requested channel name to one of the standard channel ids. 26 * 27 * Parameters: 28 * o interp - Errors are returned in result. 29 * o channelName - Desired channel, one of "stdin", "stdout" or "stderr". 30 * o handleIdPtr - One of STD_{INPUT|OUTPUT|ERROR}_HANDLE is returned. 31 * Returns: 32 * TCL_OK or TCL_ERROR. 33 * FIX: Make Unix version parallel this one. 34 *----------------------------------------------------------------------------- 35 */ 36static int 37ConvertChannelName (Tcl_Interp *interp, 38 char *channelName, 39 DWORD *handleIdPtr) 40{ 41 if (channelName [0] == 's') { 42 if (STREQU (channelName, "stdin")) 43 *handleIdPtr = STD_INPUT_HANDLE; 44 else if (STREQU (channelName, "stdout")) 45 *handleIdPtr = STD_OUTPUT_HANDLE; 46 else if (STREQU (channelName, "stderr")) 47 *handleIdPtr = STD_ERROR_HANDLE; 48 } else if (STRNEQU (channelName, "file", 4) || 49 STRNEQU (channelName, "sock", 4)) { 50 TclX_AppendObjResult (interp, "on MS Windows, only stdin, ", 51 "stdout or stderr maybe the dup target", 52 (char *) NULL); 53 return TCL_ERROR; 54 } else { 55 TclX_AppendObjResult (interp, "invalid channel id: ", 56 channelName, (char *) NULL); 57 return TCL_ERROR; 58 } 59 return TCL_OK; 60} 61 62/*----------------------------------------------------------------------------- 63 * TclXOSDupChannel -- 64 * OS dependent duplication of a channel. 65 * 66 * Parameters: 67 * o interp (I) - If an error occures, the error message is in result. 68 * o srcChannel (I) - The channel to dup. 69 * o mode (I) - The channel mode. 70 * o targetChannelId (I) - The id for the new file. NULL if any id maybe 71 * used. 72 * Returns: 73 * The unregistered new channel, or NULL if an error occured. 74 *----------------------------------------------------------------------------- 75 */ 76Tcl_Channel 77TclXOSDupChannel (interp, srcChannel, mode, targetChannelId) 78 Tcl_Interp *interp; 79 Tcl_Channel srcChannel; 80 int mode; 81 char *targetChannelId; 82{ 83 Tcl_Channel newChannel = NULL; 84 int direction; 85 int result; 86 HANDLE srcFileHand, newFileHand = INVALID_HANDLE_VALUE; 87 int sockType; 88 int sockTypeLen = sizeof(sockType); 89 90 /* 91 * On Windows, the channels we can dup share the same file for the read and 92 * write directions, so use either. 93 */ 94 if (mode & TCL_READABLE) { 95 direction = TCL_READABLE; 96 } else { 97 direction = TCL_WRITABLE; 98 } 99 100 result = (Tcl_GetChannelHandle (srcChannel, direction, 101 (ClientData *) &srcFileHand)); 102 if (result != TCL_OK) { 103 TclX_AppendObjResult (interp, "channel \"", 104 Tcl_GetChannelName (srcChannel), 105 "\" has no device handle", (char *) NULL); 106 return NULL; 107 } 108 109 switch (GetFileType (srcFileHand)) 110 { 111 case FILE_TYPE_PIPE: 112 if (getsockopt((SOCKET)srcFileHand, SOL_SOCKET, SO_TYPE, 113 (void *)&sockType, &sockTypeLen) == 0) { 114 TclXNotAvailableError (interp, "duping a socket"); 115 return NULL; 116 } 117 break; 118 119 default: 120 break; 121 } 122 123 /* 124 * Duplicate the channel's file. 125 */ 126 if (!DuplicateHandle (GetCurrentProcess (), 127 srcFileHand, 128 GetCurrentProcess (), 129 &newFileHand, 130 0, FALSE, 131 DUPLICATE_SAME_ACCESS)) { 132 TclWinConvertError (GetLastError ()); 133 TclX_AppendObjResult (interp, "dup failed: ", 134 Tcl_PosixError (interp), (char *) NULL); 135 goto errorExit; 136 } 137 138 /* 139 * If a standard target channel is specified, close the target if its open 140 * and make the new channel one of the std channels. 141 */ 142 if (targetChannelId != NULL) { 143 Tcl_Channel oldChannel; 144 DWORD stdHandleId; 145 146 if (ConvertChannelName (interp, targetChannelId, 147 &stdHandleId) != TCL_OK) 148 goto errorExit; 149 150 oldChannel = Tcl_GetChannel (interp, targetChannelId, NULL); 151 if (oldChannel != NULL) { 152 Tcl_UnregisterChannel (interp, oldChannel); 153 } 154 SetStdHandle (stdHandleId, newFileHand); 155 } 156 157 newChannel = Tcl_MakeFileChannel ((ClientData) newFileHand, mode); 158 return newChannel; 159 160 errorExit: 161 if (newFileHand != INVALID_HANDLE_VALUE) 162 CloseHandle (newFileHand); 163 return NULL; 164} 165 166/*----------------------------------------------------------------------------- 167 * TclXOSBindOpenFile -- 168 * Bind a open file number of a channel. 169 * 170 * Parameters: 171 * o interp (I) - If an error occures, the error message is in result. 172 * o fileNum (I) - The file number of the open file. 173 * Returns: 174 * The unregistered channel or NULL if an error occurs. 175 *----------------------------------------------------------------------------- 176 */ 177Tcl_Channel 178TclXOSBindOpenFile (interp, fileNum) 179 Tcl_Interp *interp; 180 int fileNum; 181{ 182 HANDLE fileHandle; 183 int mode, isSocket; 184 char channelName[20]; 185 char fileNumStr[20]; 186 Tcl_Channel channel = NULL; 187 int sockType; 188 int sockTypeLen = sizeof(sockType); 189 190 /* 191 * Make sure file is open and determine the access mode and file type. 192 * Currently, we just make sure it's open, and assume both read and write. 193 * FIX: find an API under Windows that returns the read/write info. 194 */ 195 fileHandle = (HANDLE) fileNum; 196 switch (GetFileType (fileHandle)) 197 { 198 case FILE_TYPE_UNKNOWN: 199 TclWinConvertError (GetLastError ()); 200 goto posixError; 201 case FILE_TYPE_PIPE: 202 isSocket = getsockopt((SOCKET)fileHandle, SOL_SOCKET, SO_TYPE, 203 (void *)&sockType, &sockTypeLen) == 0; 204 break; 205 default: 206 isSocket = 0; 207 break; 208 } 209 210 mode = TCL_READABLE | TCL_WRITABLE; 211 212 sprintf (fileNumStr, "%d", fileNum); 213 214 if (isSocket) 215 sprintf (channelName, "sock%s", fileNumStr); 216 else 217 sprintf (channelName, "file%s", fileNumStr); 218 219 if (Tcl_GetChannel (interp, channelName, NULL) != NULL) { 220 Tcl_ResetResult (interp); 221 TclX_AppendObjResult (interp, "file number \"", fileNumStr, 222 "\" is already bound to a Tcl channel", 223 (char *) NULL); 224 return NULL; 225 } 226 Tcl_ResetResult (interp); 227 228 if (isSocket) { 229 channel = Tcl_MakeTcpClientChannel ((ClientData) fileNum); 230 } else { 231 channel = Tcl_MakeFileChannel ((ClientData) fileNum, mode); 232 } 233 Tcl_RegisterChannel (interp, channel); 234 235 return channel; 236 237 posixError: 238 TclX_AppendObjResult (interp, "binding open file ", fileNumStr, 239 " to Tcl channel failed: ", Tcl_PosixError (interp), 240 (char *) NULL); 241 242 if (channel != NULL) { 243 Tcl_UnregisterChannel (interp, channel); 244 } 245 return NULL; 246} 247 248 249