/*
|
/*
|
* tclWinChan.c
|
* tclWinChan.c
|
*
|
*
|
* Channel drivers for Windows channels based on files, command
|
* Channel drivers for Windows channels based on files, command
|
* pipes and TCP sockets.
|
* pipes and TCP sockets.
|
*
|
*
|
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
|
* Copyright (c) 1995-1997 Sun Microsystems, Inc.
|
*
|
*
|
* See the file "license.terms" for information on usage and redistribution
|
* See the file "license.terms" for information on usage and redistribution
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
*
|
*
|
* RCS: @(#) $Id: tclWinChan.c,v 1.1.1.1 2002-01-16 10:25:38 markom Exp $
|
* RCS: @(#) $Id: tclWinChan.c,v 1.1.1.1 2002-01-16 10:25:38 markom Exp $
|
*/
|
*/
|
|
|
#include "tclWinInt.h"
|
#include "tclWinInt.h"
|
|
|
/*
|
/*
|
* This is the size of the channel name for File based channels
|
* This is the size of the channel name for File based channels
|
*/
|
*/
|
|
|
#define CHANNEL_NAME_SIZE 64
|
#define CHANNEL_NAME_SIZE 64
|
static char channelName[CHANNEL_NAME_SIZE+1];
|
static char channelName[CHANNEL_NAME_SIZE+1];
|
|
|
/*
|
/*
|
* The following variable is used to tell whether this module has been
|
* The following variable is used to tell whether this module has been
|
* initialized.
|
* initialized.
|
*/
|
*/
|
|
|
static int initialized = 0;
|
static int initialized = 0;
|
|
|
/*
|
/*
|
* State flags used in the info structures below.
|
* State flags used in the info structures below.
|
*/
|
*/
|
|
|
#define FILE_PENDING (1<<0) /* Message is pending in the queue. */
|
#define FILE_PENDING (1<<0) /* Message is pending in the queue. */
|
#define FILE_ASYNC (1<<1) /* Channel is non-blocking. */
|
#define FILE_ASYNC (1<<1) /* Channel is non-blocking. */
|
#define FILE_APPEND (1<<2) /* File is in append mode. */
|
#define FILE_APPEND (1<<2) /* File is in append mode. */
|
|
|
/*
|
/*
|
* The following structure contains per-instance data for a file based channel.
|
* The following structure contains per-instance data for a file based channel.
|
*/
|
*/
|
|
|
typedef struct FileInfo {
|
typedef struct FileInfo {
|
Tcl_Channel channel; /* Pointer to channel structure. */
|
Tcl_Channel channel; /* Pointer to channel structure. */
|
int validMask; /* OR'ed combination of TCL_READABLE,
|
int validMask; /* OR'ed combination of TCL_READABLE,
|
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
|
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
|
* which operations are valid on the file. */
|
* which operations are valid on the file. */
|
int watchMask; /* OR'ed combination of TCL_READABLE,
|
int watchMask; /* OR'ed combination of TCL_READABLE,
|
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
|
* TCL_WRITABLE, or TCL_EXCEPTION: indicates
|
* which events should be reported. */
|
* which events should be reported. */
|
int flags; /* State flags, see above for a list. */
|
int flags; /* State flags, see above for a list. */
|
HANDLE handle; /* Input/output file. */
|
HANDLE handle; /* Input/output file. */
|
struct FileInfo *nextPtr; /* Pointer to next registered file. */
|
struct FileInfo *nextPtr; /* Pointer to next registered file. */
|
} FileInfo;
|
} FileInfo;
|
|
|
/*
|
/*
|
* List of all file channels currently open.
|
* List of all file channels currently open.
|
*/
|
*/
|
|
|
static FileInfo *firstFilePtr;
|
static FileInfo *firstFilePtr;
|
|
|
/*
|
/*
|
* The following structure is what is added to the Tcl event queue when
|
* The following structure is what is added to the Tcl event queue when
|
* file events are generated.
|
* file events are generated.
|
*/
|
*/
|
|
|
typedef struct FileEvent {
|
typedef struct FileEvent {
|
Tcl_Event header; /* Information that is standard for
|
Tcl_Event header; /* Information that is standard for
|
* all events. */
|
* all events. */
|
FileInfo *infoPtr; /* Pointer to file info structure. Note
|
FileInfo *infoPtr; /* Pointer to file info structure. Note
|
* that we still have to verify that the
|
* that we still have to verify that the
|
* file exists before dereferencing this
|
* file exists before dereferencing this
|
* pointer. */
|
* pointer. */
|
} FileEvent;
|
} FileEvent;
|
|
|
/*
|
/*
|
* Static routines for this file:
|
* Static routines for this file:
|
*/
|
*/
|
|
|
static int ComGetOptionProc _ANSI_ARGS_((ClientData instanceData,
|
static int ComGetOptionProc _ANSI_ARGS_((ClientData instanceData,
|
Tcl_Interp *interp, char *optionName,
|
Tcl_Interp *interp, char *optionName,
|
Tcl_DString *dsPtr));
|
Tcl_DString *dsPtr));
|
static int ComInputProc _ANSI_ARGS_((ClientData instanceData,
|
static int ComInputProc _ANSI_ARGS_((ClientData instanceData,
|
char *buf, int toRead, int *errorCode));
|
char *buf, int toRead, int *errorCode));
|
static int ComSetOptionProc _ANSI_ARGS_((ClientData instanceData,
|
static int ComSetOptionProc _ANSI_ARGS_((ClientData instanceData,
|
Tcl_Interp *interp, char *optionName,
|
Tcl_Interp *interp, char *optionName,
|
char *value));
|
char *value));
|
static int FileBlockProc _ANSI_ARGS_((ClientData instanceData,
|
static int FileBlockProc _ANSI_ARGS_((ClientData instanceData,
|
int mode));
|
int mode));
|
static void FileChannelExitHandler _ANSI_ARGS_((
|
static void FileChannelExitHandler _ANSI_ARGS_((
|
ClientData clientData));
|
ClientData clientData));
|
static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
|
static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
|
int flags));
|
int flags));
|
static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
|
static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
|
Tcl_Interp *interp));
|
Tcl_Interp *interp));
|
static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
|
static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
|
int flags));
|
int flags));
|
static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
|
static int FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
|
int direction, ClientData *handlePtr));
|
int direction, ClientData *handlePtr));
|
static void FileInit _ANSI_ARGS_((void));
|
static void FileInit _ANSI_ARGS_((void));
|
static int FileInputProc _ANSI_ARGS_((ClientData instanceData,
|
static int FileInputProc _ANSI_ARGS_((ClientData instanceData,
|
char *buf, int toRead, int *errorCode));
|
char *buf, int toRead, int *errorCode));
|
static int FileOutputProc _ANSI_ARGS_((ClientData instanceData,
|
static int FileOutputProc _ANSI_ARGS_((ClientData instanceData,
|
char *buf, int toWrite, int *errorCode));
|
char *buf, int toWrite, int *errorCode));
|
static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
|
static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
|
long offset, int mode, int *errorCode));
|
long offset, int mode, int *errorCode));
|
static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
|
static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
|
int flags));
|
int flags));
|
static void FileWatchProc _ANSI_ARGS_((ClientData instanceData,
|
static void FileWatchProc _ANSI_ARGS_((ClientData instanceData,
|
int mask));
|
int mask));
|
|
|
|
|
/*
|
/*
|
* This structure describes the channel type structure for file based IO.
|
* This structure describes the channel type structure for file based IO.
|
*/
|
*/
|
|
|
static Tcl_ChannelType fileChannelType = {
|
static Tcl_ChannelType fileChannelType = {
|
"file", /* Type name. */
|
"file", /* Type name. */
|
FileBlockProc, /* Set blocking or non-blocking mode.*/
|
FileBlockProc, /* Set blocking or non-blocking mode.*/
|
FileCloseProc, /* Close proc. */
|
FileCloseProc, /* Close proc. */
|
FileInputProc, /* Input proc. */
|
FileInputProc, /* Input proc. */
|
FileOutputProc, /* Output proc. */
|
FileOutputProc, /* Output proc. */
|
FileSeekProc, /* Seek proc. */
|
FileSeekProc, /* Seek proc. */
|
NULL, /* Set option proc. */
|
NULL, /* Set option proc. */
|
NULL, /* Get option proc. */
|
NULL, /* Get option proc. */
|
FileWatchProc, /* Set up the notifier to watch the channel. */
|
FileWatchProc, /* Set up the notifier to watch the channel. */
|
FileGetHandleProc, /* Get an OS handle from channel. */
|
FileGetHandleProc, /* Get an OS handle from channel. */
|
};
|
};
|
|
|
static Tcl_ChannelType comChannelType = {
|
static Tcl_ChannelType comChannelType = {
|
"com", /* Type name. */
|
"com", /* Type name. */
|
FileBlockProc, /* Set blocking or non-blocking mode.*/
|
FileBlockProc, /* Set blocking or non-blocking mode.*/
|
FileCloseProc, /* Close proc. */
|
FileCloseProc, /* Close proc. */
|
ComInputProc, /* Input proc. */
|
ComInputProc, /* Input proc. */
|
FileOutputProc, /* Output proc. */
|
FileOutputProc, /* Output proc. */
|
NULL, /* Seek proc. */
|
NULL, /* Seek proc. */
|
ComSetOptionProc, /* Set option proc. */
|
ComSetOptionProc, /* Set option proc. */
|
ComGetOptionProc, /* Get option proc. */
|
ComGetOptionProc, /* Get option proc. */
|
FileWatchProc, /* Set up notifier to watch the channel. */
|
FileWatchProc, /* Set up notifier to watch the channel. */
|
FileGetHandleProc /* Get an OS handle from channel. */
|
FileGetHandleProc /* Get an OS handle from channel. */
|
};
|
};
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileInit --
|
* FileInit --
|
*
|
*
|
* This function creates the window used to simulate file events.
|
* This function creates the window used to simulate file events.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Creates a new window and creates an exit handler.
|
* Creates a new window and creates an exit handler.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
FileInit()
|
FileInit()
|
{
|
{
|
initialized = 1;
|
initialized = 1;
|
firstFilePtr = NULL;
|
firstFilePtr = NULL;
|
Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
|
Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
|
Tcl_CreateExitHandler(FileChannelExitHandler, NULL);
|
Tcl_CreateExitHandler(FileChannelExitHandler, NULL);
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileChannelExitHandler --
|
* FileChannelExitHandler --
|
*
|
*
|
* This function is called to cleanup the channel driver before
|
* This function is called to cleanup the channel driver before
|
* Tcl is unloaded.
|
* Tcl is unloaded.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Destroys the communication window.
|
* Destroys the communication window.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
FileChannelExitHandler(clientData)
|
FileChannelExitHandler(clientData)
|
ClientData clientData; /* Old window proc */
|
ClientData clientData; /* Old window proc */
|
{
|
{
|
Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
|
Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
|
initialized = 0;
|
initialized = 0;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileSetupProc --
|
* FileSetupProc --
|
*
|
*
|
* This procedure is invoked before Tcl_DoOneEvent blocks waiting
|
* This procedure is invoked before Tcl_DoOneEvent blocks waiting
|
* for an event.
|
* for an event.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Adjusts the block time if needed.
|
* Adjusts the block time if needed.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
void
|
void
|
FileSetupProc(data, flags)
|
FileSetupProc(data, flags)
|
ClientData data; /* Not used. */
|
ClientData data; /* Not used. */
|
int flags; /* Event flags as passed to Tcl_DoOneEvent. */
|
int flags; /* Event flags as passed to Tcl_DoOneEvent. */
|
{
|
{
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
Tcl_Time blockTime = { 0, 0 };
|
Tcl_Time blockTime = { 0, 0 };
|
|
|
if (!(flags & TCL_FILE_EVENTS)) {
|
if (!(flags & TCL_FILE_EVENTS)) {
|
return;
|
return;
|
}
|
}
|
|
|
/*
|
/*
|
* Check to see if there is a ready file. If so, poll.
|
* Check to see if there is a ready file. If so, poll.
|
*/
|
*/
|
|
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
if (infoPtr->watchMask) {
|
if (infoPtr->watchMask) {
|
Tcl_SetMaxBlockTime(&blockTime);
|
Tcl_SetMaxBlockTime(&blockTime);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileCheckProc --
|
* FileCheckProc --
|
*
|
*
|
* This procedure is called by Tcl_DoOneEvent to check the file
|
* This procedure is called by Tcl_DoOneEvent to check the file
|
* event source for events.
|
* event source for events.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May queue an event.
|
* May queue an event.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
FileCheckProc(data, flags)
|
FileCheckProc(data, flags)
|
ClientData data; /* Not used. */
|
ClientData data; /* Not used. */
|
int flags; /* Event flags as passed to Tcl_DoOneEvent. */
|
int flags; /* Event flags as passed to Tcl_DoOneEvent. */
|
{
|
{
|
FileEvent *evPtr;
|
FileEvent *evPtr;
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
|
|
if (!(flags & TCL_FILE_EVENTS)) {
|
if (!(flags & TCL_FILE_EVENTS)) {
|
return;
|
return;
|
}
|
}
|
|
|
/*
|
/*
|
* Queue events for any ready files that don't already have events
|
* Queue events for any ready files that don't already have events
|
* queued (caused by persistent states that won't generate WinSock
|
* queued (caused by persistent states that won't generate WinSock
|
* events).
|
* events).
|
*/
|
*/
|
|
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) {
|
if (infoPtr->watchMask && !(infoPtr->flags & FILE_PENDING)) {
|
infoPtr->flags |= FILE_PENDING;
|
infoPtr->flags |= FILE_PENDING;
|
evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
|
evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
|
evPtr->header.proc = FileEventProc;
|
evPtr->header.proc = FileEventProc;
|
evPtr->infoPtr = infoPtr;
|
evPtr->infoPtr = infoPtr;
|
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
|
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/*----------------------------------------------------------------------
|
/*----------------------------------------------------------------------
|
*
|
*
|
* FileEventProc --
|
* FileEventProc --
|
*
|
*
|
* This function is invoked by Tcl_ServiceEvent when a file event
|
* This function is invoked by Tcl_ServiceEvent when a file event
|
* reaches the front of the event queue. This procedure invokes
|
* reaches the front of the event queue. This procedure invokes
|
* Tcl_NotifyChannel on the file.
|
* Tcl_NotifyChannel on the file.
|
*
|
*
|
* Results:
|
* Results:
|
* Returns 1 if the event was handled, meaning it should be removed
|
* Returns 1 if the event was handled, meaning it should be removed
|
* from the queue. Returns 0 if the event was not handled, meaning
|
* from the queue. Returns 0 if the event was not handled, meaning
|
* it should stay on the queue. The only time the event isn't
|
* it should stay on the queue. The only time the event isn't
|
* handled is if the TCL_FILE_EVENTS flag bit isn't set.
|
* handled is if the TCL_FILE_EVENTS flag bit isn't set.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Whatever the notifier callback does.
|
* Whatever the notifier callback does.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileEventProc(evPtr, flags)
|
FileEventProc(evPtr, flags)
|
Tcl_Event *evPtr; /* Event to service. */
|
Tcl_Event *evPtr; /* Event to service. */
|
int flags; /* Flags that indicate what events to
|
int flags; /* Flags that indicate what events to
|
* handle, such as TCL_FILE_EVENTS. */
|
* handle, such as TCL_FILE_EVENTS. */
|
{
|
{
|
FileEvent *fileEvPtr = (FileEvent *)evPtr;
|
FileEvent *fileEvPtr = (FileEvent *)evPtr;
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
|
|
if (!(flags & TCL_FILE_EVENTS)) {
|
if (!(flags & TCL_FILE_EVENTS)) {
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Search through the list of watched files for the one whose handle
|
* Search through the list of watched files for the one whose handle
|
* matches the event. We do this rather than simply dereferencing
|
* matches the event. We do this rather than simply dereferencing
|
* the handle in the event so that files can be deleted while the
|
* the handle in the event so that files can be deleted while the
|
* event is in the queue.
|
* event is in the queue.
|
*/
|
*/
|
|
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
if (fileEvPtr->infoPtr == infoPtr) {
|
if (fileEvPtr->infoPtr == infoPtr) {
|
infoPtr->flags &= ~(FILE_PENDING);
|
infoPtr->flags &= ~(FILE_PENDING);
|
Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask);
|
Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileBlockProc --
|
* FileBlockProc --
|
*
|
*
|
* Set blocking or non-blocking mode on channel.
|
* Set blocking or non-blocking mode on channel.
|
*
|
*
|
* Results:
|
* Results:
|
* 0 if successful, errno when failed.
|
* 0 if successful, errno when failed.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Sets the device into blocking or non-blocking mode.
|
* Sets the device into blocking or non-blocking mode.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileBlockProc(instanceData, mode)
|
FileBlockProc(instanceData, mode)
|
ClientData instanceData; /* Instance data for channel. */
|
ClientData instanceData; /* Instance data for channel. */
|
int mode; /* TCL_MODE_BLOCKING or
|
int mode; /* TCL_MODE_BLOCKING or
|
* TCL_MODE_NONBLOCKING. */
|
* TCL_MODE_NONBLOCKING. */
|
{
|
{
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
|
|
/*
|
/*
|
* Files on Windows can not be switched between blocking and nonblocking,
|
* Files on Windows can not be switched between blocking and nonblocking,
|
* hence we have to emulate the behavior. This is done in the input
|
* hence we have to emulate the behavior. This is done in the input
|
* function by checking against a bit in the state. We set or unset the
|
* function by checking against a bit in the state. We set or unset the
|
* bit here to cause the input function to emulate the correct behavior.
|
* bit here to cause the input function to emulate the correct behavior.
|
*/
|
*/
|
|
|
if (mode == TCL_MODE_NONBLOCKING) {
|
if (mode == TCL_MODE_NONBLOCKING) {
|
infoPtr->flags |= FILE_ASYNC;
|
infoPtr->flags |= FILE_ASYNC;
|
} else {
|
} else {
|
infoPtr->flags &= ~(FILE_ASYNC);
|
infoPtr->flags &= ~(FILE_ASYNC);
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileCloseProc --
|
* FileCloseProc --
|
*
|
*
|
* Closes the IO channel.
|
* Closes the IO channel.
|
*
|
*
|
* Results:
|
* Results:
|
* 0 if successful, the value of errno if failed.
|
* 0 if successful, the value of errno if failed.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Closes the physical channel
|
* Closes the physical channel
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileCloseProc(instanceData, interp)
|
FileCloseProc(instanceData, interp)
|
ClientData instanceData; /* Pointer to FileInfo structure. */
|
ClientData instanceData; /* Pointer to FileInfo structure. */
|
Tcl_Interp *interp; /* Not used. */
|
Tcl_Interp *interp; /* Not used. */
|
{
|
{
|
FileInfo *fileInfoPtr = (FileInfo *) instanceData;
|
FileInfo *fileInfoPtr = (FileInfo *) instanceData;
|
FileInfo **nextPtrPtr;
|
FileInfo **nextPtrPtr;
|
int errorCode = 0;
|
int errorCode = 0;
|
|
|
/*
|
/*
|
* Remove the file from the watch list.
|
* Remove the file from the watch list.
|
*/
|
*/
|
|
|
FileWatchProc(instanceData, 0);
|
FileWatchProc(instanceData, 0);
|
|
|
if (CloseHandle(fileInfoPtr->handle) == FALSE) {
|
if (CloseHandle(fileInfoPtr->handle) == FALSE) {
|
TclWinConvertError(GetLastError());
|
TclWinConvertError(GetLastError());
|
errorCode = errno;
|
errorCode = errno;
|
}
|
}
|
for (nextPtrPtr = &firstFilePtr; (*nextPtrPtr) != NULL;
|
for (nextPtrPtr = &firstFilePtr; (*nextPtrPtr) != NULL;
|
nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
|
nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
|
if ((*nextPtrPtr) == fileInfoPtr) {
|
if ((*nextPtrPtr) == fileInfoPtr) {
|
(*nextPtrPtr) = fileInfoPtr->nextPtr;
|
(*nextPtrPtr) = fileInfoPtr->nextPtr;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
ckfree((char *)fileInfoPtr);
|
ckfree((char *)fileInfoPtr);
|
return errorCode;
|
return errorCode;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileSeekProc --
|
* FileSeekProc --
|
*
|
*
|
* Seeks on a file-based channel. Returns the new position.
|
* Seeks on a file-based channel. Returns the new position.
|
*
|
*
|
* Results:
|
* Results:
|
* -1 if failed, the new position if successful. If failed, it
|
* -1 if failed, the new position if successful. If failed, it
|
* also sets *errorCodePtr to the error code.
|
* also sets *errorCodePtr to the error code.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Moves the location at which the channel will be accessed in
|
* Moves the location at which the channel will be accessed in
|
* future operations.
|
* future operations.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileSeekProc(instanceData, offset, mode, errorCodePtr)
|
FileSeekProc(instanceData, offset, mode, errorCodePtr)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
long offset; /* Offset to seek to. */
|
long offset; /* Offset to seek to. */
|
int mode; /* Relative to where
|
int mode; /* Relative to where
|
* should we seek? */
|
* should we seek? */
|
int *errorCodePtr; /* To store error code. */
|
int *errorCodePtr; /* To store error code. */
|
{
|
{
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
DWORD moveMethod;
|
DWORD moveMethod;
|
DWORD newPos;
|
DWORD newPos;
|
|
|
*errorCodePtr = 0;
|
*errorCodePtr = 0;
|
if (mode == SEEK_SET) {
|
if (mode == SEEK_SET) {
|
moveMethod = FILE_BEGIN;
|
moveMethod = FILE_BEGIN;
|
} else if (mode == SEEK_CUR) {
|
} else if (mode == SEEK_CUR) {
|
moveMethod = FILE_CURRENT;
|
moveMethod = FILE_CURRENT;
|
} else {
|
} else {
|
moveMethod = FILE_END;
|
moveMethod = FILE_END;
|
}
|
}
|
|
|
newPos = SetFilePointer(infoPtr->handle, offset, NULL, moveMethod);
|
newPos = SetFilePointer(infoPtr->handle, offset, NULL, moveMethod);
|
if (newPos == 0xFFFFFFFF) {
|
if (newPos == 0xFFFFFFFF) {
|
TclWinConvertError(GetLastError());
|
TclWinConvertError(GetLastError());
|
return -1;
|
return -1;
|
}
|
}
|
return newPos;
|
return newPos;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileInputProc --
|
* FileInputProc --
|
*
|
*
|
* Reads input from the IO channel into the buffer given. Returns
|
* Reads input from the IO channel into the buffer given. Returns
|
* count of how many bytes were actually read, and an error indication.
|
* count of how many bytes were actually read, and an error indication.
|
*
|
*
|
* Results:
|
* Results:
|
* A count of how many bytes were read is returned and an error
|
* A count of how many bytes were read is returned and an error
|
* indication is returned in an output argument.
|
* indication is returned in an output argument.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Reads input from the actual channel.
|
* Reads input from the actual channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileInputProc(instanceData, buf, bufSize, errorCode)
|
FileInputProc(instanceData, buf, bufSize, errorCode)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
char *buf; /* Where to store data read. */
|
char *buf; /* Where to store data read. */
|
int bufSize; /* How much space is available
|
int bufSize; /* How much space is available
|
* in the buffer? */
|
* in the buffer? */
|
int *errorCode; /* Where to store error code. */
|
int *errorCode; /* Where to store error code. */
|
{
|
{
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
DWORD bytesRead;
|
DWORD bytesRead;
|
|
|
*errorCode = 0;
|
*errorCode = 0;
|
infoPtr = (FileInfo *) instanceData;
|
infoPtr = (FileInfo *) instanceData;
|
|
|
/*
|
/*
|
* Note that we will block on reads from a console buffer until a
|
* Note that we will block on reads from a console buffer until a
|
* full line has been entered. The only way I know of to get
|
* full line has been entered. The only way I know of to get
|
* around this is to write a console driver. We should probably
|
* around this is to write a console driver. We should probably
|
* do this at some point, but for now, we just block. The same
|
* do this at some point, but for now, we just block. The same
|
* problem exists for files being read over the network.
|
* problem exists for files being read over the network.
|
*/
|
*/
|
|
|
if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
|
if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
|
(LPOVERLAPPED) NULL) != FALSE) {
|
(LPOVERLAPPED) NULL) != FALSE) {
|
return bytesRead;
|
return bytesRead;
|
}
|
}
|
|
|
TclWinConvertError(GetLastError());
|
TclWinConvertError(GetLastError());
|
*errorCode = errno;
|
*errorCode = errno;
|
if (errno == EPIPE) {
|
if (errno == EPIPE) {
|
return 0;
|
return 0;
|
}
|
}
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileOutputProc --
|
* FileOutputProc --
|
*
|
*
|
* Writes the given output on the IO channel. Returns count of how
|
* Writes the given output on the IO channel. Returns count of how
|
* many characters were actually written, and an error indication.
|
* many characters were actually written, and an error indication.
|
*
|
*
|
* Results:
|
* Results:
|
* A count of how many characters were written is returned and an
|
* A count of how many characters were written is returned and an
|
* error indication is returned in an output argument.
|
* error indication is returned in an output argument.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Writes output on the actual channel.
|
* Writes output on the actual channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileOutputProc(instanceData, buf, toWrite, errorCode)
|
FileOutputProc(instanceData, buf, toWrite, errorCode)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
char *buf; /* The data buffer. */
|
char *buf; /* The data buffer. */
|
int toWrite; /* How many bytes to write? */
|
int toWrite; /* How many bytes to write? */
|
int *errorCode; /* Where to store error code. */
|
int *errorCode; /* Where to store error code. */
|
{
|
{
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
DWORD bytesWritten;
|
DWORD bytesWritten;
|
|
|
*errorCode = 0;
|
*errorCode = 0;
|
|
|
/*
|
/*
|
* If we are writing to a file that was opened with O_APPEND, we need to
|
* If we are writing to a file that was opened with O_APPEND, we need to
|
* seek to the end of the file before writing the current buffer.
|
* seek to the end of the file before writing the current buffer.
|
*/
|
*/
|
|
|
if (infoPtr->flags & FILE_APPEND) {
|
if (infoPtr->flags & FILE_APPEND) {
|
SetFilePointer(infoPtr->handle, 0, NULL, FILE_END);
|
SetFilePointer(infoPtr->handle, 0, NULL, FILE_END);
|
}
|
}
|
|
|
if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten,
|
if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite, &bytesWritten,
|
(LPOVERLAPPED) NULL) == FALSE) {
|
(LPOVERLAPPED) NULL) == FALSE) {
|
TclWinConvertError(GetLastError());
|
TclWinConvertError(GetLastError());
|
*errorCode = errno;
|
*errorCode = errno;
|
return -1;
|
return -1;
|
}
|
}
|
FlushFileBuffers(infoPtr->handle);
|
FlushFileBuffers(infoPtr->handle);
|
return bytesWritten;
|
return bytesWritten;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileWatchProc --
|
* FileWatchProc --
|
*
|
*
|
* Called by the notifier to set up to watch for events on this
|
* Called by the notifier to set up to watch for events on this
|
* channel.
|
* channel.
|
*
|
*
|
* Results:
|
* Results:
|
* None.
|
* None.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* None.
|
* None.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static void
|
static void
|
FileWatchProc(instanceData, mask)
|
FileWatchProc(instanceData, mask)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
int mask; /* What events to watch for; OR-ed
|
int mask; /* What events to watch for; OR-ed
|
* combination of TCL_READABLE,
|
* combination of TCL_READABLE,
|
* TCL_WRITABLE and TCL_EXCEPTION. */
|
* TCL_WRITABLE and TCL_EXCEPTION. */
|
{
|
{
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
Tcl_Time blockTime = { 0, 0 };
|
Tcl_Time blockTime = { 0, 0 };
|
|
|
/*
|
/*
|
* Since the file is always ready for events, we set the block time
|
* Since the file is always ready for events, we set the block time
|
* to zero so we will poll.
|
* to zero so we will poll.
|
*/
|
*/
|
|
|
infoPtr->watchMask = mask & infoPtr->validMask;
|
infoPtr->watchMask = mask & infoPtr->validMask;
|
if (infoPtr->watchMask) {
|
if (infoPtr->watchMask) {
|
Tcl_SetMaxBlockTime(&blockTime);
|
Tcl_SetMaxBlockTime(&blockTime);
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* FileGetHandleProc --
|
* FileGetHandleProc --
|
*
|
*
|
* Called from Tcl_GetChannelFile to retrieve OS handles from
|
* Called from Tcl_GetChannelFile to retrieve OS handles from
|
* a file based channel.
|
* a file based channel.
|
*
|
*
|
* Results:
|
* Results:
|
* Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
|
* Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
|
* there is no handle for the specified direction.
|
* there is no handle for the specified direction.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* None.
|
* None.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
FileGetHandleProc(instanceData, direction, handlePtr)
|
FileGetHandleProc(instanceData, direction, handlePtr)
|
ClientData instanceData; /* The file state. */
|
ClientData instanceData; /* The file state. */
|
int direction; /* TCL_READABLE or TCL_WRITABLE */
|
int direction; /* TCL_READABLE or TCL_WRITABLE */
|
ClientData *handlePtr; /* Where to store the handle. */
|
ClientData *handlePtr; /* Where to store the handle. */
|
{
|
{
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
FileInfo *infoPtr = (FileInfo *) instanceData;
|
|
|
if (direction & infoPtr->validMask) {
|
if (direction & infoPtr->validMask) {
|
*handlePtr = (ClientData) infoPtr->handle;
|
*handlePtr = (ClientData) infoPtr->handle;
|
return TCL_OK;
|
return TCL_OK;
|
} else {
|
} else {
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* ComInputProc --
|
* ComInputProc --
|
*
|
*
|
* Reads input from the IO channel into the buffer given. Returns
|
* Reads input from the IO channel into the buffer given. Returns
|
* count of how many bytes were actually read, and an error indication.
|
* count of how many bytes were actually read, and an error indication.
|
*
|
*
|
* Results:
|
* Results:
|
* A count of how many bytes were read is returned and an error
|
* A count of how many bytes were read is returned and an error
|
* indication is returned in an output argument.
|
* indication is returned in an output argument.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* Reads input from the actual channel.
|
* Reads input from the actual channel.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
ComInputProc(instanceData, buf, bufSize, errorCode)
|
ComInputProc(instanceData, buf, bufSize, errorCode)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
char *buf; /* Where to store data read. */
|
char *buf; /* Where to store data read. */
|
int bufSize; /* How much space is available
|
int bufSize; /* How much space is available
|
* in the buffer? */
|
* in the buffer? */
|
int *errorCode; /* Where to store error code. */
|
int *errorCode; /* Where to store error code. */
|
{
|
{
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
DWORD bytesRead;
|
DWORD bytesRead;
|
DWORD dw;
|
DWORD dw;
|
COMSTAT cs;
|
COMSTAT cs;
|
|
|
*errorCode = 0;
|
*errorCode = 0;
|
infoPtr = (FileInfo *) instanceData;
|
infoPtr = (FileInfo *) instanceData;
|
|
|
if (ClearCommError(infoPtr->handle, &dw, &cs)) {
|
if (ClearCommError(infoPtr->handle, &dw, &cs)) {
|
if (dw != 0) {
|
if (dw != 0) {
|
*errorCode = EIO;
|
*errorCode = EIO;
|
return -1;
|
return -1;
|
}
|
}
|
if (cs.cbInQue != 0) {
|
if (cs.cbInQue != 0) {
|
if ((DWORD) bufSize > cs.cbInQue) {
|
if ((DWORD) bufSize > cs.cbInQue) {
|
bufSize = cs.cbInQue;
|
bufSize = cs.cbInQue;
|
}
|
}
|
} else {
|
} else {
|
if (infoPtr->flags & FILE_ASYNC) {
|
if (infoPtr->flags & FILE_ASYNC) {
|
errno = *errorCode = EAGAIN;
|
errno = *errorCode = EAGAIN;
|
return -1;
|
return -1;
|
} else {
|
} else {
|
bufSize = 1;
|
bufSize = 1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
|
if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
|
(LPOVERLAPPED) NULL) == FALSE) {
|
(LPOVERLAPPED) NULL) == FALSE) {
|
TclWinConvertError(GetLastError());
|
TclWinConvertError(GetLastError());
|
*errorCode = errno;
|
*errorCode = errno;
|
return -1;
|
return -1;
|
}
|
}
|
|
|
return bytesRead;
|
return bytesRead;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* ComSetOptionProc --
|
* ComSetOptionProc --
|
*
|
*
|
* Sets an option on a channel.
|
* Sets an option on a channel.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result. Also sets interp->result on error if
|
* A standard Tcl result. Also sets interp->result on error if
|
* interp is not NULL.
|
* interp is not NULL.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May modify an option on a device.
|
* May modify an option on a device.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
ComSetOptionProc(instanceData, interp, optionName, value)
|
ComSetOptionProc(instanceData, interp, optionName, value)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
Tcl_Interp *interp; /* For error reporting - can be NULL. */
|
Tcl_Interp *interp; /* For error reporting - can be NULL. */
|
char *optionName; /* Which option to set? */
|
char *optionName; /* Which option to set? */
|
char *value; /* New value for option. */
|
char *value; /* New value for option. */
|
{
|
{
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
DCB dcb;
|
DCB dcb;
|
int len;
|
int len;
|
|
|
infoPtr = (FileInfo *) instanceData;
|
infoPtr = (FileInfo *) instanceData;
|
|
|
len = strlen(optionName);
|
len = strlen(optionName);
|
if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {
|
if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {
|
if (GetCommState(infoPtr->handle, &dcb)) {
|
if (GetCommState(infoPtr->handle, &dcb)) {
|
if ((BuildCommDCB(value, &dcb) == FALSE) ||
|
if ((BuildCommDCB(value, &dcb) == FALSE) ||
|
(SetCommState(infoPtr->handle, &dcb) == FALSE)) {
|
(SetCommState(infoPtr->handle, &dcb) == FALSE)) {
|
/*
|
/*
|
* one should separate the 2 errors...
|
* one should separate the 2 errors...
|
*/
|
*/
|
if (interp) {
|
if (interp) {
|
Tcl_AppendResult(interp, "bad value for -mode: should be ",
|
Tcl_AppendResult(interp, "bad value for -mode: should be ",
|
"baud,parity,data,stop", NULL);
|
"baud,parity,data,stop", NULL);
|
}
|
}
|
return TCL_ERROR;
|
return TCL_ERROR;
|
} else {
|
} else {
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
} else {
|
} else {
|
if (interp) {
|
if (interp) {
|
Tcl_AppendResult(interp, "can't get comm state", NULL);
|
Tcl_AppendResult(interp, "can't get comm state", NULL);
|
}
|
}
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
} else {
|
} else {
|
return Tcl_BadChannelOption(interp, optionName, "mode");
|
return Tcl_BadChannelOption(interp, optionName, "mode");
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* ComGetOptionProc --
|
* ComGetOptionProc --
|
*
|
*
|
* Gets a mode associated with an IO channel. If the optionName arg
|
* Gets a mode associated with an IO channel. If the optionName arg
|
* is non NULL, retrieves the value of that option. If the optionName
|
* is non NULL, retrieves the value of that option. If the optionName
|
* arg is NULL, retrieves a list of alternating option names and
|
* arg is NULL, retrieves a list of alternating option names and
|
* values for the given channel.
|
* values for the given channel.
|
*
|
*
|
* Results:
|
* Results:
|
* A standard Tcl result. Also sets the supplied DString to the
|
* A standard Tcl result. Also sets the supplied DString to the
|
* string value of the option(s) returned.
|
* string value of the option(s) returned.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* The string returned by this function is in static storage and
|
* The string returned by this function is in static storage and
|
* may be reused at any time subsequent to the call.
|
* may be reused at any time subsequent to the call.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
static int
|
static int
|
ComGetOptionProc(instanceData, interp, optionName, dsPtr)
|
ComGetOptionProc(instanceData, interp, optionName, dsPtr)
|
ClientData instanceData; /* File state. */
|
ClientData instanceData; /* File state. */
|
Tcl_Interp *interp; /* For error reporting - can be NULL. */
|
Tcl_Interp *interp; /* For error reporting - can be NULL. */
|
char *optionName; /* Option to get. */
|
char *optionName; /* Option to get. */
|
Tcl_DString *dsPtr; /* Where to store value(s). */
|
Tcl_DString *dsPtr; /* Where to store value(s). */
|
{
|
{
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
DCB dcb;
|
DCB dcb;
|
int len;
|
int len;
|
|
|
infoPtr = (FileInfo *) instanceData;
|
infoPtr = (FileInfo *) instanceData;
|
|
|
if (optionName == NULL) {
|
if (optionName == NULL) {
|
Tcl_DStringAppendElement(dsPtr, "-mode");
|
Tcl_DStringAppendElement(dsPtr, "-mode");
|
len = 0;
|
len = 0;
|
} else {
|
} else {
|
len = strlen(optionName);
|
len = strlen(optionName);
|
}
|
}
|
if ((len == 0) ||
|
if ((len == 0) ||
|
((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {
|
((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {
|
if (GetCommState(infoPtr->handle, &dcb) == 0) {
|
if (GetCommState(infoPtr->handle, &dcb) == 0) {
|
/*
|
/*
|
* shouldn't we flag an error instead ?
|
* shouldn't we flag an error instead ?
|
*/
|
*/
|
Tcl_DStringAppendElement(dsPtr, "");
|
Tcl_DStringAppendElement(dsPtr, "");
|
} else {
|
} else {
|
char parity;
|
char parity;
|
char *stop;
|
char *stop;
|
char buf[32];
|
char buf[32];
|
|
|
parity = 'n';
|
parity = 'n';
|
if (dcb.Parity < 4) {
|
if (dcb.Parity < 4) {
|
parity = "noems"[dcb.Parity];
|
parity = "noems"[dcb.Parity];
|
}
|
}
|
|
|
stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
|
stop = (dcb.StopBits == ONESTOPBIT) ? "1" :
|
(dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
|
(dcb.StopBits == ONE5STOPBITS) ? "1.5" : "2";
|
|
|
wsprintf(buf, "%d,%c,%d,%s", dcb.BaudRate, parity, dcb.ByteSize,
|
wsprintf(buf, "%d,%c,%d,%s", dcb.BaudRate, parity, dcb.ByteSize,
|
stop);
|
stop);
|
Tcl_DStringAppendElement(dsPtr, buf);
|
Tcl_DStringAppendElement(dsPtr, buf);
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
} else {
|
} else {
|
return Tcl_BadChannelOption(interp, optionName, "mode");
|
return Tcl_BadChannelOption(interp, optionName, "mode");
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* TclpOpenFileChannel --
|
* TclpOpenFileChannel --
|
*
|
*
|
* Open an File based channel on Unix systems.
|
* Open an File based channel on Unix systems.
|
*
|
*
|
* Results:
|
* Results:
|
* The new channel or NULL. If NULL, the output argument
|
* The new channel or NULL. If NULL, the output argument
|
* errorCodePtr is set to a POSIX error.
|
* errorCodePtr is set to a POSIX error.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May open the channel and may cause creation of a file on the
|
* May open the channel and may cause creation of a file on the
|
* file system.
|
* file system.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
Tcl_Channel
|
Tcl_Channel
|
TclpOpenFileChannel(interp, fileName, modeString, permissions)
|
TclpOpenFileChannel(interp, fileName, modeString, permissions)
|
Tcl_Interp *interp; /* Interpreter for error reporting;
|
Tcl_Interp *interp; /* Interpreter for error reporting;
|
* can be NULL. */
|
* can be NULL. */
|
char *fileName; /* Name of file to open. */
|
char *fileName; /* Name of file to open. */
|
char *modeString; /* A list of POSIX open modes or
|
char *modeString; /* A list of POSIX open modes or
|
* a string such as "rw". */
|
* a string such as "rw". */
|
int permissions; /* If the open involves creating a
|
int permissions; /* If the open involves creating a
|
* file, with what modes to create
|
* file, with what modes to create
|
* it? */
|
* it? */
|
{
|
{
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
int seekFlag, mode, channelPermissions;
|
int seekFlag, mode, channelPermissions;
|
DWORD accessMode, createMode, shareMode, flags;
|
DWORD accessMode, createMode, shareMode, flags;
|
char *nativeName;
|
char *nativeName;
|
Tcl_DString buffer;
|
Tcl_DString buffer;
|
DCB dcb;
|
DCB dcb;
|
Tcl_ChannelType *channelTypePtr;
|
Tcl_ChannelType *channelTypePtr;
|
HANDLE handle;
|
HANDLE handle;
|
|
|
if (!initialized) {
|
if (!initialized) {
|
FileInit();
|
FileInit();
|
}
|
}
|
|
|
mode = TclGetOpenMode(interp, modeString, &seekFlag);
|
mode = TclGetOpenMode(interp, modeString, &seekFlag);
|
if (mode == -1) {
|
if (mode == -1) {
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
|
nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
|
if (nativeName == NULL) {
|
if (nativeName == NULL) {
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
|
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
|
case O_RDONLY:
|
case O_RDONLY:
|
accessMode = GENERIC_READ;
|
accessMode = GENERIC_READ;
|
channelPermissions = TCL_READABLE;
|
channelPermissions = TCL_READABLE;
|
break;
|
break;
|
case O_WRONLY:
|
case O_WRONLY:
|
accessMode = GENERIC_WRITE;
|
accessMode = GENERIC_WRITE;
|
channelPermissions = TCL_WRITABLE;
|
channelPermissions = TCL_WRITABLE;
|
break;
|
break;
|
case O_RDWR:
|
case O_RDWR:
|
accessMode = (GENERIC_READ | GENERIC_WRITE);
|
accessMode = (GENERIC_READ | GENERIC_WRITE);
|
channelPermissions = (TCL_READABLE | TCL_WRITABLE);
|
channelPermissions = (TCL_READABLE | TCL_WRITABLE);
|
break;
|
break;
|
default:
|
default:
|
panic("TclpOpenFileChannel: invalid mode value");
|
panic("TclpOpenFileChannel: invalid mode value");
|
break;
|
break;
|
}
|
}
|
|
|
/*
|
/*
|
* Map the creation flags to the NT create mode.
|
* Map the creation flags to the NT create mode.
|
*/
|
*/
|
|
|
switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
|
switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
|
case (O_CREAT | O_EXCL):
|
case (O_CREAT | O_EXCL):
|
case (O_CREAT | O_EXCL | O_TRUNC):
|
case (O_CREAT | O_EXCL | O_TRUNC):
|
createMode = CREATE_NEW;
|
createMode = CREATE_NEW;
|
break;
|
break;
|
case (O_CREAT | O_TRUNC):
|
case (O_CREAT | O_TRUNC):
|
createMode = CREATE_ALWAYS;
|
createMode = CREATE_ALWAYS;
|
break;
|
break;
|
case O_CREAT:
|
case O_CREAT:
|
createMode = OPEN_ALWAYS;
|
createMode = OPEN_ALWAYS;
|
break;
|
break;
|
case O_TRUNC:
|
case O_TRUNC:
|
case (O_TRUNC | O_EXCL):
|
case (O_TRUNC | O_EXCL):
|
createMode = TRUNCATE_EXISTING;
|
createMode = TRUNCATE_EXISTING;
|
break;
|
break;
|
default:
|
default:
|
createMode = OPEN_EXISTING;
|
createMode = OPEN_EXISTING;
|
break;
|
break;
|
}
|
}
|
|
|
/*
|
/*
|
* If the file is being created, get the file attributes from the
|
* If the file is being created, get the file attributes from the
|
* permissions argument, else use the existing file attributes.
|
* permissions argument, else use the existing file attributes.
|
*/
|
*/
|
|
|
if (mode & O_CREAT) {
|
if (mode & O_CREAT) {
|
if (permissions & S_IWRITE) {
|
if (permissions & S_IWRITE) {
|
flags = FILE_ATTRIBUTE_NORMAL;
|
flags = FILE_ATTRIBUTE_NORMAL;
|
} else {
|
} else {
|
flags = FILE_ATTRIBUTE_READONLY;
|
flags = FILE_ATTRIBUTE_READONLY;
|
}
|
}
|
} else {
|
} else {
|
flags = GetFileAttributes(nativeName);
|
flags = GetFileAttributes(nativeName);
|
if (flags == 0xFFFFFFFF) {
|
if (flags == 0xFFFFFFFF) {
|
flags = 0;
|
flags = 0;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Set up the file sharing mode. We want to allow simultaneous access.
|
* Set up the file sharing mode. We want to allow simultaneous access.
|
*/
|
*/
|
|
|
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
|
/*
|
/*
|
* Now we get to create the file.
|
* Now we get to create the file.
|
*/
|
*/
|
|
|
handle = CreateFile(nativeName, accessMode, shareMode, NULL, createMode,
|
handle = CreateFile(nativeName, accessMode, shareMode, NULL, createMode,
|
flags, (HANDLE) NULL);
|
flags, (HANDLE) NULL);
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
if (handle == INVALID_HANDLE_VALUE) {
|
DWORD err;
|
DWORD err;
|
|
|
openerr:
|
openerr:
|
err = GetLastError();
|
err = GetLastError();
|
if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
|
if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
|
err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
|
err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
|
}
|
}
|
TclWinConvertError(err);
|
TclWinConvertError(err);
|
if (interp != (Tcl_Interp *) NULL) {
|
if (interp != (Tcl_Interp *) NULL) {
|
Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
|
Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
|
Tcl_PosixError(interp), (char *) NULL);
|
Tcl_PosixError(interp), (char *) NULL);
|
}
|
}
|
Tcl_DStringFree(&buffer);
|
Tcl_DStringFree(&buffer);
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
if (GetFileType(handle) == FILE_TYPE_CHAR) {
|
if (GetFileType(handle) == FILE_TYPE_CHAR) {
|
dcb.DCBlength = sizeof( DCB ) ;
|
dcb.DCBlength = sizeof( DCB ) ;
|
if (GetCommState(handle, &dcb)) {
|
if (GetCommState(handle, &dcb)) {
|
/*
|
/*
|
* This is a com port. Reopen it with the correct modes.
|
* This is a com port. Reopen it with the correct modes.
|
*/
|
*/
|
|
|
COMMTIMEOUTS cto;
|
COMMTIMEOUTS cto;
|
|
|
CloseHandle(handle);
|
CloseHandle(handle);
|
handle = CreateFile(nativeName, accessMode, 0, NULL, OPEN_EXISTING,
|
handle = CreateFile(nativeName, accessMode, 0, NULL, OPEN_EXISTING,
|
flags, NULL);
|
flags, NULL);
|
if (handle == INVALID_HANDLE_VALUE) {
|
if (handle == INVALID_HANDLE_VALUE) {
|
goto openerr;
|
goto openerr;
|
}
|
}
|
|
|
/*
|
/*
|
* FileInit the com port.
|
* FileInit the com port.
|
*/
|
*/
|
|
|
SetCommMask(handle, EV_RXCHAR);
|
SetCommMask(handle, EV_RXCHAR);
|
SetupComm(handle, 4096, 4096);
|
SetupComm(handle, 4096, 4096);
|
PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
|
PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
|
| PURGE_RXCLEAR);
|
| PURGE_RXCLEAR);
|
cto.ReadIntervalTimeout = MAXDWORD;
|
cto.ReadIntervalTimeout = MAXDWORD;
|
cto.ReadTotalTimeoutMultiplier = 0;
|
cto.ReadTotalTimeoutMultiplier = 0;
|
cto.ReadTotalTimeoutConstant = 0;
|
cto.ReadTotalTimeoutConstant = 0;
|
cto.WriteTotalTimeoutMultiplier = 0;
|
cto.WriteTotalTimeoutMultiplier = 0;
|
cto.WriteTotalTimeoutConstant = 0;
|
cto.WriteTotalTimeoutConstant = 0;
|
SetCommTimeouts(handle, &cto);
|
SetCommTimeouts(handle, &cto);
|
|
|
GetCommState(handle, &dcb);
|
GetCommState(handle, &dcb);
|
SetCommState(handle, &dcb);
|
SetCommState(handle, &dcb);
|
channelTypePtr = &comChannelType;
|
channelTypePtr = &comChannelType;
|
} else {
|
} else {
|
channelTypePtr = &fileChannelType;
|
channelTypePtr = &fileChannelType;
|
}
|
}
|
} else {
|
} else {
|
channelTypePtr = &fileChannelType;
|
channelTypePtr = &fileChannelType;
|
}
|
}
|
Tcl_DStringFree(&buffer);
|
Tcl_DStringFree(&buffer);
|
|
|
infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
|
infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
|
infoPtr->nextPtr = firstFilePtr;
|
infoPtr->nextPtr = firstFilePtr;
|
firstFilePtr = infoPtr;
|
firstFilePtr = infoPtr;
|
infoPtr->validMask = channelPermissions;
|
infoPtr->validMask = channelPermissions;
|
infoPtr->watchMask = 0;
|
infoPtr->watchMask = 0;
|
infoPtr->flags = (mode & O_APPEND) ? FILE_APPEND : 0;
|
infoPtr->flags = (mode & O_APPEND) ? FILE_APPEND : 0;
|
infoPtr->handle = handle;
|
infoPtr->handle = handle;
|
|
|
sprintf(channelName, "file%d", (int) handle);
|
sprintf(channelName, "file%d", (int) handle);
|
|
|
infoPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
|
infoPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
|
(ClientData) infoPtr, channelPermissions);
|
(ClientData) infoPtr, channelPermissions);
|
|
|
if (seekFlag) {
|
if (seekFlag) {
|
if (Tcl_Seek(infoPtr->channel, 0, SEEK_END) < 0) {
|
if (Tcl_Seek(infoPtr->channel, 0, SEEK_END) < 0) {
|
if (interp != (Tcl_Interp *) NULL) {
|
if (interp != (Tcl_Interp *) NULL) {
|
Tcl_AppendResult(interp, "could not seek to end of file on \"",
|
Tcl_AppendResult(interp, "could not seek to end of file on \"",
|
channelName, "\": ", Tcl_PosixError(interp),
|
channelName, "\": ", Tcl_PosixError(interp),
|
(char *) NULL);
|
(char *) NULL);
|
}
|
}
|
Tcl_Close(NULL, infoPtr->channel);
|
Tcl_Close(NULL, infoPtr->channel);
|
return NULL;
|
return NULL;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Files have default translation of AUTO and ^Z eof char, which
|
* Files have default translation of AUTO and ^Z eof char, which
|
* means that a ^Z will be appended to them at close.
|
* means that a ^Z will be appended to them at close.
|
*/
|
*/
|
|
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
|
return infoPtr->channel;
|
return infoPtr->channel;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* Tcl_MakeFileChannel --
|
* Tcl_MakeFileChannel --
|
*
|
*
|
* Creates a Tcl_Channel from an existing platform specific file
|
* Creates a Tcl_Channel from an existing platform specific file
|
* handle.
|
* handle.
|
*
|
*
|
* Results:
|
* Results:
|
* The Tcl_Channel created around the preexisting file.
|
* The Tcl_Channel created around the preexisting file.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* None.
|
* None.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
Tcl_Channel
|
Tcl_Channel
|
Tcl_MakeFileChannel(handle, mode)
|
Tcl_MakeFileChannel(handle, mode)
|
ClientData handle; /* OS level handle */
|
ClientData handle; /* OS level handle */
|
int mode; /* ORed combination of TCL_READABLE and
|
int mode; /* ORed combination of TCL_READABLE and
|
* TCL_WRITABLE to indicate file mode. */
|
* TCL_WRITABLE to indicate file mode. */
|
{
|
{
|
char channelName[20];
|
char channelName[20];
|
FileInfo *infoPtr;
|
FileInfo *infoPtr;
|
|
|
if (!initialized) {
|
if (!initialized) {
|
FileInit();
|
FileInit();
|
}
|
}
|
|
|
if (mode == 0) {
|
if (mode == 0) {
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
sprintf(channelName, "file%d", (int) handle);
|
sprintf(channelName, "file%d", (int) handle);
|
|
|
/*
|
/*
|
* See if a channel with this handle already exists.
|
* See if a channel with this handle already exists.
|
*/
|
*/
|
|
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
|
if (infoPtr->handle == (HANDLE) handle) {
|
if (infoPtr->handle == (HANDLE) handle) {
|
return (mode == infoPtr->validMask) ? infoPtr->channel : NULL;
|
return (mode == infoPtr->validMask) ? infoPtr->channel : NULL;
|
}
|
}
|
}
|
}
|
|
|
infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
|
infoPtr = (FileInfo *) ckalloc((unsigned) sizeof(FileInfo));
|
infoPtr->nextPtr = firstFilePtr;
|
infoPtr->nextPtr = firstFilePtr;
|
firstFilePtr = infoPtr;
|
firstFilePtr = infoPtr;
|
infoPtr->validMask = mode;
|
infoPtr->validMask = mode;
|
infoPtr->watchMask = 0;
|
infoPtr->watchMask = 0;
|
infoPtr->flags = 0;
|
infoPtr->flags = 0;
|
infoPtr->handle = (HANDLE) handle;
|
infoPtr->handle = (HANDLE) handle;
|
infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
|
infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
|
(ClientData) infoPtr, mode);
|
(ClientData) infoPtr, mode);
|
|
|
/*
|
/*
|
* Windows files have AUTO translation mode and ^Z eof char on input.
|
* Windows files have AUTO translation mode and ^Z eof char on input.
|
*/
|
*/
|
|
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
|
Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
|
return infoPtr->channel;
|
return infoPtr->channel;
|
}
|
}
|
|
|
/*
|
/*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*
|
*
|
* TclGetDefaultStdChannel --
|
* TclGetDefaultStdChannel --
|
*
|
*
|
* Constructs a channel for the specified standard OS handle.
|
* Constructs a channel for the specified standard OS handle.
|
*
|
*
|
* Results:
|
* Results:
|
* Returns the specified default standard channel, or NULL.
|
* Returns the specified default standard channel, or NULL.
|
*
|
*
|
* Side effects:
|
* Side effects:
|
* May cause the creation of a standard channel and the underlying
|
* May cause the creation of a standard channel and the underlying
|
* file.
|
* file.
|
*
|
*
|
*----------------------------------------------------------------------
|
*----------------------------------------------------------------------
|
*/
|
*/
|
|
|
Tcl_Channel
|
Tcl_Channel
|
TclGetDefaultStdChannel(type)
|
TclGetDefaultStdChannel(type)
|
int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
|
int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
|
{
|
{
|
Tcl_Channel channel;
|
Tcl_Channel channel;
|
HANDLE handle;
|
HANDLE handle;
|
int mode;
|
int mode;
|
char *bufMode;
|
char *bufMode;
|
DWORD handleId; /* Standard handle to retrieve. */
|
DWORD handleId; /* Standard handle to retrieve. */
|
|
|
switch (type) {
|
switch (type) {
|
case TCL_STDIN:
|
case TCL_STDIN:
|
handleId = STD_INPUT_HANDLE;
|
handleId = STD_INPUT_HANDLE;
|
mode = TCL_READABLE;
|
mode = TCL_READABLE;
|
bufMode = "line";
|
bufMode = "line";
|
break;
|
break;
|
case TCL_STDOUT:
|
case TCL_STDOUT:
|
handleId = STD_OUTPUT_HANDLE;
|
handleId = STD_OUTPUT_HANDLE;
|
mode = TCL_WRITABLE;
|
mode = TCL_WRITABLE;
|
bufMode = "line";
|
bufMode = "line";
|
break;
|
break;
|
case TCL_STDERR:
|
case TCL_STDERR:
|
handleId = STD_ERROR_HANDLE;
|
handleId = STD_ERROR_HANDLE;
|
mode = TCL_WRITABLE;
|
mode = TCL_WRITABLE;
|
bufMode = "none";
|
bufMode = "none";
|
break;
|
break;
|
default:
|
default:
|
panic("TclGetDefaultStdChannel: Unexpected channel type");
|
panic("TclGetDefaultStdChannel: Unexpected channel type");
|
break;
|
break;
|
}
|
}
|
handle = GetStdHandle(handleId);
|
handle = GetStdHandle(handleId);
|
|
|
/*
|
/*
|
* Note that we need to check for 0 because Windows will return 0 if this
|
* Note that we need to check for 0 because Windows will return 0 if this
|
* is not a console mode application, even though this is not a valid
|
* is not a console mode application, even though this is not a valid
|
* handle.
|
* handle.
|
*/
|
*/
|
|
|
if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
|
if ((handle == INVALID_HANDLE_VALUE) || (handle == 0)) {
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
channel = Tcl_MakeFileChannel(handle, mode);
|
channel = Tcl_MakeFileChannel(handle, mode);
|
|
|
/*
|
/*
|
* Set up the normal channel options for stdio handles.
|
* Set up the normal channel options for stdio handles.
|
*/
|
*/
|
|
|
if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",
|
if ((Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-translation",
|
"auto") == TCL_ERROR)
|
"auto") == TCL_ERROR)
|
|| (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",
|
|| (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel, "-eofchar",
|
"\032 {}") == TCL_ERROR)
|
"\032 {}") == TCL_ERROR)
|
|| (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,
|
|| (Tcl_SetChannelOption((Tcl_Interp *) NULL, channel,
|
"-buffering", bufMode) == TCL_ERROR)) {
|
"-buffering", bufMode) == TCL_ERROR)) {
|
Tcl_Close((Tcl_Interp *) NULL, channel);
|
Tcl_Close((Tcl_Interp *) NULL, channel);
|
return (Tcl_Channel) NULL;
|
return (Tcl_Channel) NULL;
|
}
|
}
|
return channel;
|
return channel;
|
}
|
}
|
|
|