tclwinchan.c

来自「tcl是工具命令语言」· C语言 代码 · 共 1,431 行 · 第 1/3 页

C
1,431
字号
                                 * TCL_WRITABLE to indicate file mode. */{    char channelName[16 + TCL_INTEGER_SPACE];    Tcl_Channel channel = NULL;    HANDLE handle = (HANDLE) rawHandle;    HANDLE dupedHandle;    DCB dcb;    DWORD consoleParams, type;    TclFile readFile = NULL;    TclFile writeFile = NULL;    BOOL result;    if (mode == 0) {	return NULL;    }    /*     * GetFileType() returns FILE_TYPE_UNKNOWN for invalid handles.     */    type = GetFileType(handle);    /*     * If the file is a character device, we need to try to figure out     * whether it is a serial port, a console, or something else.  We     * test for the console case first because this is more common.     */    if (type == FILE_TYPE_CHAR) {	if (GetConsoleMode(handle, &consoleParams)) {	    type = FILE_TYPE_CONSOLE;	} else {	    dcb.DCBlength = sizeof( DCB ) ;	    if (GetCommState(handle, &dcb)) {		type = FILE_TYPE_SERIAL;	    }	}    }    switch (type)    {    case FILE_TYPE_SERIAL:	channel = TclWinOpenSerialChannel(handle, channelName, mode);	break;    case FILE_TYPE_CONSOLE:	channel = TclWinOpenConsoleChannel(handle, channelName, mode);	break;    case FILE_TYPE_PIPE:	if (mode & TCL_READABLE)	{	    readFile = TclWinMakeFile(handle);	}	if (mode & TCL_WRITABLE)	{	    writeFile = TclWinMakeFile(handle);	}	channel = TclpCreateCommandChannel(readFile, writeFile, NULL, 0, NULL);	break;    case FILE_TYPE_DISK:    case FILE_TYPE_CHAR:	channel = TclWinOpenFileChannel(handle, channelName, mode, 0);	break;	    case FILE_TYPE_UNKNOWN:    default:	/*	 * The handle is of an unknown type.  Test the validity of this OS	 * handle by duplicating it, then closing the dupe.  The Win32 API	 * doesn't provide an IsValidHandle() function, so we have to emulate	 * it here.  This test will not work on a console handle reliably,	 * which is why we can't test every handle that comes into this	 * function in this way.	 */	result = DuplicateHandle(GetCurrentProcess(), handle,		GetCurrentProcess(), &dupedHandle, 0, FALSE,		DUPLICATE_SAME_ACCESS);	if (result == 0) {	    /* 	     * Unable to make a duplicate. It's definately invalid at this	     * point.	     */	    return NULL;	}	/*	 * Use structured exception handling (Win32 SEH) to protect the close	 * of this duped handle which might throw EXCEPTION_INVALID_HANDLE.	 */#ifdef HAVE_NO_SEH# ifdef TCL_MEM_DEBUG        __asm__ __volatile__ (            "movl %%esp,  %0" "\n\t"            "movl %%ebp,  %1" "\n\t"            "movl %%fs:0, %2" "\n\t"            : "=m"(INITIAL_ESP),              "=m"(INITIAL_EBP),              "=r"(INITIAL_HANDLER) );# endif /* TCL_MEM_DEBUG */        result = 0;        __asm__ __volatile__ (            "pushl %ebp" "\n\t"            "pushl $__except_makefilechannel_handler" "\n\t"            "pushl %fs:0" "\n\t"            "movl  %esp, %fs:0");#else	__try {#endif /* HAVE_NO_SEH */	    CloseHandle(dupedHandle);#ifdef HAVE_NO_SEH        __asm__ __volatile__ (            "jmp  makefilechannel_pop" "\n"        "makefilechannel_reentry:" "\n\t"            "movl %%fs:0, %%eax" "\n\t"            "movl 0x8(%%eax), %%esp" "\n\t"            "movl 0x8(%%esp), %%ebp" "\n"            "movl $1, %0" "\n"        "makefilechannel_pop:" "\n\t"            "movl (%%esp), %%eax" "\n\t"            "movl %%eax, %%fs:0" "\n\t"            "add  $12, %%esp" "\n\t"            : "=m"(result)            :            : "%eax");# ifdef TCL_MEM_DEBUG    __asm__ __volatile__ (            "movl  %%esp,  %0" "\n\t"            "movl  %%ebp,  %1" "\n\t"            "movl  %%fs:0, %2" "\n\t"            : "=m"(RESTORED_ESP),              "=m"(RESTORED_EBP),              "=r"(RESTORED_HANDLER) );    if (INITIAL_ESP != RESTORED_ESP)        panic("ESP restored incorrectly");    if (INITIAL_EBP != RESTORED_EBP)        panic("EBP restored incorrectly");    if (INITIAL_HANDLER != RESTORED_HANDLER)        panic("HANDLER restored incorrectly");# endif /* TCL_MEM_DEBUG */        if (result)            return NULL;#else	}	__except (EXCEPTION_EXECUTE_HANDLER) {	    /*	     * Definately an invalid handle.  So, therefore, the original	     * is invalid also.	     */	    return NULL;	}#endif /* HAVE_NO_SEH */	/* Fall through, the handle is valid. */	/*	 * Create the undefined channel, anyways, because we know the handle	 * is valid to something.	 */	channel = TclWinOpenFileChannel(handle, channelName, mode, 0);    }    return channel;}#ifdef HAVE_NO_SEHstatic__attribute__ ((cdecl))EXCEPTION_DISPOSITION_except_makefilechannel_handler(    struct _EXCEPTION_RECORD *ExceptionRecord,    void *EstablisherFrame,    struct _CONTEXT *ContextRecord,    void *DispatcherContext){    __asm__ __volatile__ (            "jmp makefilechannel_reentry");    /* Nuke compiler warning about unused static function */    _except_makefilechannel_handler(NULL, NULL, NULL, NULL);    return 0; /* Function does not return */}#endif/* *---------------------------------------------------------------------- * * TclpGetDefaultStdChannel -- * *	Constructs a channel for the specified standard OS handle. * * Results: *	Returns the specified default standard channel, or NULL. * * Side effects: *	May cause the creation of a standard channel and the underlying *	file. * *---------------------------------------------------------------------- */Tcl_ChannelTclpGetDefaultStdChannel(type)    int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */{    Tcl_Channel channel;    HANDLE handle;    int mode;    char *bufMode;    DWORD handleId;		/* Standard handle to retrieve. */    switch (type) {	case TCL_STDIN:	    handleId = STD_INPUT_HANDLE;	    mode = TCL_READABLE;	    bufMode = "line";	    break;	case TCL_STDOUT:	    handleId = STD_OUTPUT_HANDLE;	    mode = TCL_WRITABLE;	    bufMode = "line";	    break;	case TCL_STDERR:	    handleId = STD_ERROR_HANDLE;	    mode = TCL_WRITABLE;	    bufMode = "none";	    break;	default:	    panic("TclGetDefaultStdChannel: Unexpected channel type");	    break;    }    handle = GetStdHandle(handleId);    /*     * Note that we need to check for 0 because Windows may return 0 if this     * is not a console mode application, even though this is not a valid     * handle.     */    if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {	return (Tcl_Channel) NULL;    }    channel = Tcl_MakeFileChannel(handle, mode);    if (channel == NULL) {	return (Tcl_Channel) NULL;    }    /*     * Set up the normal channel options for stdio handles.     */    if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",            "auto") == TCL_ERROR)	    || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",		    "\032 {}") == TCL_ERROR)	    || (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,		    "-buffering", bufMode) == TCL_ERROR)) {        Tcl_Close((Tcl_Interp *) NULL, channel);        return (Tcl_Channel) NULL;    }    return channel;}/* *---------------------------------------------------------------------- * * TclWinOpenFileChannel -- * *	Constructs a File channel for the specified standard OS handle. *      This is a helper function to break up the construction of  *      channels into File, Console, or Serial. * * Results: *	Returns the new channel, or NULL. * * Side effects: *	May open the channel and may cause creation of a file on the *	file system. * *---------------------------------------------------------------------- */Tcl_ChannelTclWinOpenFileChannel(handle, channelName, permissions, appendMode)    HANDLE handle;    char *channelName;    int permissions;    int appendMode;{    FileInfo *infoPtr;    ThreadSpecificData *tsdPtr;    tsdPtr = FileInit();    /*     * See if a channel with this handle already exists.     */        for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 	 infoPtr = infoPtr->nextPtr) {	if (infoPtr->handle == (HANDLE) handle) {	    return (permissions == infoPtr->validMask) ? infoPtr->channel : NULL;	}    }    infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));    infoPtr->nextPtr = tsdPtr->firstFilePtr;    tsdPtr->firstFilePtr = infoPtr;    infoPtr->validMask = permissions;    infoPtr->watchMask = 0;    infoPtr->flags = appendMode;    infoPtr->handle = handle;    infoPtr->dirty = 0;    wsprintfA(channelName, "file%lx", (int) infoPtr);        infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,	    (ClientData) infoPtr, permissions);        /*     * Files have default translation of AUTO and ^Z eof char, which     * means that a ^Z will be accepted as EOF when reading.     */        Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");    Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");    return infoPtr->channel;}/* *---------------------------------------------------------------------- * * TclWinFlushDirtyChannels -- * *	Flush all dirty channels to disk, so that requesting the *	size of any file returns the correct value. * * Results: *	None. * * Side effects: *	Information is actually written to disk now, rather than *	later.  Don't call this too often, or there will be a  *	performance hit (i.e. only call when we need to ask for *	the size of a file). * *---------------------------------------------------------------------- */voidTclWinFlushDirtyChannels (){    FileInfo *infoPtr;    ThreadSpecificData *tsdPtr;    tsdPtr = FileInit();    /*     * Flush all channels which are dirty, i.e. may have data pending     * in the OS     */        for (infoPtr = tsdPtr->firstFilePtr;	 infoPtr != NULL; 	 infoPtr = infoPtr->nextPtr) {	if (infoPtr->dirty) {	    FlushFileBuffers(infoPtr->handle);	    infoPtr->dirty = 0;	}    }}/* *---------------------------------------------------------------------- * * TclpCutFileChannel -- * *	Remove any thread local refs to this channel. See *	Tcl_CutChannel for more info. * * Results: *	None. * * Side effects: *	Changes thread local list of valid channels. * *---------------------------------------------------------------------- */voidTclpCutFileChannel(chan)    Tcl_Channel chan;			/* The channel being removed. Must                                         * not be referenced in any                                         * interpreter. */{    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);    Channel *chanPtr = (Channel *) chan;    FileInfo *infoPtr;    FileInfo **nextPtrPtr;    int removed = 0;    if (chanPtr->typePtr != &fileChannelType)        return;    infoPtr = (FileInfo *) chanPtr->instanceData;    for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;	 nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {	if ((*nextPtrPtr) == infoPtr) {	    (*nextPtrPtr) = infoPtr->nextPtr;	    removed = 1;	    break;	}    }    /*     * This could happen if the channel was created in one thread     * and then moved to another without updating the thread     * local data in each thread.     */    if (!removed)        panic("file info ptr not on thread channel list");}/* *---------------------------------------------------------------------- * * TclpSpliceFileChannel -- * *	Insert thread local ref for this channel. *	Tcl_SpliceChannel for more info. * * Results: *	None. * * Side effects: *	Changes thread local list of valid channels. * *---------------------------------------------------------------------- */voidTclpSpliceFileChannel(chan)    Tcl_Channel chan;			/* The channel being removed. Must                                         * not be referenced in any                                         * interpreter. */{    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);    Channel *chanPtr = (Channel *) chan;    FileInfo *infoPtr;    if (chanPtr->typePtr != &fileChannelType)        return;    infoPtr = (FileInfo *) chanPtr->instanceData;    infoPtr->nextPtr = tsdPtr->firstFilePtr;    tsdPtr->firstFilePtr = infoPtr;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?