sl@0: /* sl@0: * tclMacChan.c sl@0: * sl@0: * Channel drivers for Macintosh channels for the sl@0: * console fds. sl@0: * sl@0: * Copyright (c) 1996-1997 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclMacChan.c,v 1.21.2.1 2005/01/27 22:53:34 andreas_kupries Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: #include "tclMacInt.h" sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "tclIO.h" sl@0: sl@0: #ifdef __MSL__ sl@0: #include sl@0: #define TCL_FILE_CREATOR (__getcreator(0)) sl@0: #else sl@0: #define TCL_FILE_CREATOR 'MPW ' sl@0: #endif sl@0: sl@0: /* sl@0: * This structure describes per-instance state of a sl@0: * macintosh file based channel. sl@0: */ sl@0: sl@0: typedef struct FileState { sl@0: short fileRef; /* Macintosh file reference number. */ sl@0: Tcl_Channel fileChan; /* Pointer to the channel for this file. */ sl@0: int watchMask; /* OR'ed set of flags indicating which events sl@0: * are being watched. */ sl@0: int appendMode; /* Flag to tell if in O_APPEND mode or not. */ sl@0: int volumeRef; /* Flag to tell if in O_APPEND mode or not. */ sl@0: int pending; /* 1 if message is pending on queue. */ sl@0: struct FileState *nextPtr; /* Pointer to next registered file. */ sl@0: } FileState; sl@0: sl@0: typedef struct ThreadSpecificData { sl@0: int initialized; /* True after the thread initializes */ sl@0: FileState *firstFilePtr; /* the head of the list of files managed sl@0: * that are being watched for file events. */ sl@0: Tcl_Channel stdinChannel; sl@0: Tcl_Channel stdoutChannel; /* Note - these seem unused */ sl@0: Tcl_Channel stderrChannel; sl@0: } ThreadSpecificData; sl@0: sl@0: static Tcl_ThreadDataKey dataKey; sl@0: sl@0: /* sl@0: * The following structure is what is added to the Tcl event queue when sl@0: * file events are generated. sl@0: */ sl@0: sl@0: typedef struct FileEvent { sl@0: Tcl_Event header; /* Information that is standard for sl@0: * all events. */ sl@0: FileState *infoPtr; /* Pointer to file info structure. Note sl@0: * that we still have to verify that the sl@0: * file exists before dereferencing this sl@0: * pointer. */ sl@0: } FileEvent; sl@0: sl@0: sl@0: /* sl@0: * Static routines for this file: sl@0: */ sl@0: sl@0: static int CommonGetHandle _ANSI_ARGS_((ClientData instanceData, sl@0: int direction, ClientData *handlePtr)); sl@0: static void CommonWatch _ANSI_ARGS_((ClientData instanceData, sl@0: int mask)); sl@0: static int FileBlockMode _ANSI_ARGS_((ClientData instanceData, sl@0: int mode)); sl@0: static void FileChannelExitHandler _ANSI_ARGS_(( sl@0: ClientData clientData)); sl@0: static void FileCheckProc _ANSI_ARGS_((ClientData clientData, sl@0: int flags)); sl@0: static int FileClose _ANSI_ARGS_((ClientData instanceData, sl@0: Tcl_Interp *interp)); sl@0: static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr, sl@0: int flags)); sl@0: static ThreadSpecificData *FileInit _ANSI_ARGS_((void)); sl@0: static int FileInput _ANSI_ARGS_((ClientData instanceData, sl@0: char *buf, int toRead, int *errorCode)); sl@0: static int FileOutput _ANSI_ARGS_((ClientData instanceData, sl@0: CONST char *buf, int toWrite, int *errorCode)); sl@0: static int FileSeek _ANSI_ARGS_((ClientData instanceData, sl@0: long offset, int mode, int *errorCode)); sl@0: static void FileSetupProc _ANSI_ARGS_((ClientData clientData, sl@0: int flags)); sl@0: static void FileThreadActionProc _ANSI_ARGS_ (( sl@0: ClientData instanceData, int action)); sl@0: static Tcl_Channel OpenFileChannel _ANSI_ARGS_((CONST char *fileName, sl@0: int mode, int permissions, int *errorCodePtr)); sl@0: static int StdIOBlockMode _ANSI_ARGS_((ClientData instanceData, sl@0: int mode)); sl@0: static int StdIOClose _ANSI_ARGS_((ClientData instanceData, sl@0: Tcl_Interp *interp)); sl@0: static int StdIOInput _ANSI_ARGS_((ClientData instanceData, sl@0: char *buf, int toRead, int *errorCode)); sl@0: static int StdIOOutput _ANSI_ARGS_((ClientData instanceData, sl@0: CONST char *buf, int toWrite, int *errorCode)); sl@0: static int StdIOSeek _ANSI_ARGS_((ClientData instanceData, sl@0: long offset, int mode, int *errorCode)); sl@0: static int StdReady _ANSI_ARGS_((ClientData instanceData, sl@0: int mask)); sl@0: sl@0: /* sl@0: * This structure describes the channel type structure for file based IO: sl@0: */ sl@0: sl@0: static Tcl_ChannelType consoleChannelType = { sl@0: "file", /* Type name. */ sl@0: TCL_CHANNEL_VERSION_4, /* v4 channel */ sl@0: StdIOClose, /* Close proc. */ sl@0: StdIOInput, /* Input proc. */ sl@0: StdIOOutput, /* Output proc. */ sl@0: StdIOSeek, /* Seek proc. */ sl@0: NULL, /* Set option proc. */ sl@0: NULL, /* Get option proc. */ sl@0: CommonWatch, /* Initialize notifier. */ sl@0: CommonGetHandle /* Get OS handles out of channel. */ sl@0: NULL, /* close2proc. */ sl@0: StdIOBlockMode, /* Set blocking/nonblocking mode.*/ sl@0: NULL, /* flush proc. */ sl@0: NULL, /* handler proc. */ sl@0: NULL, /* wide seek proc. */ sl@0: NULL, /* thread actions */ sl@0: }; sl@0: sl@0: /* sl@0: * This variable describes the channel type structure for file based IO. sl@0: */ sl@0: sl@0: static Tcl_ChannelType fileChannelType = { sl@0: "file", /* Type name. */ sl@0: TCL_CHANNEL_VERSION_4, /* v4 channel */ sl@0: FileClose, /* Close proc. */ sl@0: FileInput, /* Input proc. */ sl@0: FileOutput, /* Output proc. */ sl@0: FileSeek, /* Seek proc. */ sl@0: NULL, /* Set option proc. */ sl@0: NULL, /* Get option proc. */ sl@0: CommonWatch, /* Initialize notifier. */ sl@0: CommonGetHandle /* Get OS handles out of channel. */ sl@0: NULL, /* close2proc. */ sl@0: FileBlockMode, /* Set blocking/nonblocking mode.*/ sl@0: NULL, /* flush proc. */ sl@0: NULL, /* handler proc. */ sl@0: NULL, /* wide seek proc. */ sl@0: FileThreadActionProc, /* thread actions */ sl@0: }; sl@0: sl@0: sl@0: /* sl@0: * Hack to allow Mac Tk to override the TclGetStdChannels function. sl@0: */ sl@0: sl@0: typedef void (*TclGetStdChannelsProc) _ANSI_ARGS_((Tcl_Channel *stdinPtr, sl@0: Tcl_Channel *stdoutPtr, Tcl_Channel *stderrPtr)); sl@0: sl@0: TclGetStdChannelsProc getStdChannelsProc = NULL; sl@0: sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileInit -- sl@0: * sl@0: * This function initializes the file channel event source. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Creates a new event source. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static ThreadSpecificData * sl@0: FileInit() sl@0: { sl@0: ThreadSpecificData *tsdPtr = sl@0: (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey); sl@0: if (tsdPtr == NULL) { sl@0: tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: tsdPtr->firstFilePtr = NULL; sl@0: Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL); sl@0: Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL); sl@0: } sl@0: return tsdPtr; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileChannelExitHandler -- sl@0: * sl@0: * This function is called to cleanup the channel driver before sl@0: * Tcl is unloaded. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Destroys the communication window. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: FileChannelExitHandler( sl@0: ClientData clientData) /* Old window proc */ sl@0: { sl@0: Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileSetupProc -- sl@0: * sl@0: * This procedure is invoked before Tcl_DoOneEvent blocks waiting sl@0: * for an event. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Adjusts the block time if needed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: FileSetupProc( sl@0: ClientData data, /* Not used. */ sl@0: int flags) /* Event flags as passed to Tcl_DoOneEvent. */ sl@0: { sl@0: FileState *infoPtr; sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (!(flags & TCL_FILE_EVENTS)) { sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * Check to see if there is a ready file. If so, poll. sl@0: */ sl@0: sl@0: for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if (infoPtr->watchMask) { sl@0: Tcl_SetMaxBlockTime(&blockTime); sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileCheckProc -- sl@0: * sl@0: * This procedure is called by Tcl_DoOneEvent to check the file sl@0: * event source for events. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * May queue an event. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: FileCheckProc( sl@0: ClientData data, /* Not used. */ sl@0: int flags) /* Event flags as passed to Tcl_DoOneEvent. */ sl@0: { sl@0: FileEvent *evPtr; sl@0: FileState *infoPtr; sl@0: int sentMsg = 0; sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (!(flags & TCL_FILE_EVENTS)) { sl@0: return; sl@0: } sl@0: sl@0: /* sl@0: * Queue events for any ready files that don't already have events sl@0: * queued (caused by persistent states that won't generate WinSock sl@0: * events). sl@0: */ sl@0: sl@0: for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if (infoPtr->watchMask && !infoPtr->pending) { sl@0: infoPtr->pending = 1; sl@0: evPtr = (FileEvent *) ckalloc(sizeof(FileEvent)); sl@0: evPtr->header.proc = FileEventProc; sl@0: evPtr->infoPtr = infoPtr; sl@0: Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /*---------------------------------------------------------------------- sl@0: * sl@0: * FileEventProc -- sl@0: * sl@0: * This function is invoked by Tcl_ServiceEvent when a file event sl@0: * reaches the front of the event queue. This procedure invokes sl@0: * Tcl_NotifyChannel on the file. sl@0: * sl@0: * Results: sl@0: * Returns 1 if the event was handled, meaning it should be removed sl@0: * from the queue. Returns 0 if the event was not handled, meaning sl@0: * it should stay on the queue. The only time the event isn't sl@0: * handled is if the TCL_FILE_EVENTS flag bit isn't set. sl@0: * sl@0: * Side effects: sl@0: * Whatever the notifier callback does. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: FileEventProc( sl@0: Tcl_Event *evPtr, /* Event to service. */ sl@0: int flags) /* Flags that indicate what events to sl@0: * handle, such as TCL_FILE_EVENTS. */ sl@0: { sl@0: FileEvent *fileEvPtr = (FileEvent *)evPtr; sl@0: FileState *infoPtr; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: if (!(flags & TCL_FILE_EVENTS)) { sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: * Search through the list of watched files for the one whose handle sl@0: * matches the event. We do this rather than simply dereferencing sl@0: * the handle in the event so that files can be deleted while the sl@0: * event is in the queue. sl@0: */ sl@0: sl@0: for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; sl@0: infoPtr = infoPtr->nextPtr) { sl@0: if (fileEvPtr->infoPtr == infoPtr) { sl@0: infoPtr->pending = 0; sl@0: Tcl_NotifyChannel(infoPtr->fileChan, infoPtr->watchMask); sl@0: break; sl@0: } sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * StdIOBlockMode -- sl@0: * sl@0: * Set blocking or non-blocking mode on channel. sl@0: * sl@0: * Results: sl@0: * 0 if successful, errno when failed. sl@0: * sl@0: * Side effects: sl@0: * Sets the device into blocking or non-blocking mode. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: StdIOBlockMode( sl@0: ClientData instanceData, /* Unused. */ sl@0: int mode) /* The mode to set. */ sl@0: { sl@0: /* sl@0: * Do not allow putting stdin, stdout or stderr into nonblocking mode. sl@0: */ sl@0: sl@0: if (mode == TCL_MODE_NONBLOCKING) { sl@0: return EFAULT; sl@0: } sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * StdIOClose -- sl@0: * sl@0: * Closes the IO channel. sl@0: * sl@0: * Results: sl@0: * 0 if successful, the value of errno if failed. sl@0: * sl@0: * Side effects: sl@0: * Closes the physical channel sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: StdIOClose( sl@0: ClientData instanceData, /* Unused. */ sl@0: Tcl_Interp *interp) /* Unused. */ sl@0: { sl@0: int fd, errorCode = 0; sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: sl@0: /* sl@0: * Invalidate the stdio cache if necessary. Note that we assume that sl@0: * the stdio file and channel pointers will become invalid at the same sl@0: * time. sl@0: * Do not close standard channels while in thread-exit. sl@0: */ sl@0: sl@0: fd = (int) ((FileState*)instanceData)->fileRef; sl@0: if (!TclInThreadExit()) { sl@0: if (fd == 0) { sl@0: tsdPtr->stdinChannel = NULL; sl@0: } else if (fd == 1) { sl@0: tsdPtr->stdoutChannel = NULL; sl@0: } else if (fd == 2) { sl@0: tsdPtr->stderrChannel = NULL; sl@0: } else { sl@0: panic("recieved invalid std file"); sl@0: } sl@0: sl@0: if (close(fd) < 0) { sl@0: errorCode = errno; sl@0: } sl@0: } sl@0: return errorCode; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * CommonGetHandle -- sl@0: * sl@0: * Called from Tcl_GetChannelHandle to retrieve OS handles from inside sl@0: * a file based channel. sl@0: * sl@0: * Results: sl@0: * The appropriate handle or NULL if not present. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: CommonGetHandle( sl@0: ClientData instanceData, /* The file state. */ sl@0: int direction, /* Which handle to retrieve? */ sl@0: ClientData *handlePtr) sl@0: { sl@0: if ((direction == TCL_READABLE) || (direction == TCL_WRITABLE)) { sl@0: *handlePtr = (ClientData) ((FileState*)instanceData)->fileRef; sl@0: return TCL_OK; sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * StdIOInput -- sl@0: * sl@0: * Reads input from the IO channel into the buffer given. Returns sl@0: * count of how many bytes were actually read, and an error indication. sl@0: * sl@0: * Results: sl@0: * A count of how many bytes were read is returned and an error sl@0: * indication is returned in an output argument. sl@0: * sl@0: * Side effects: sl@0: * Reads input from the actual channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: StdIOInput( sl@0: ClientData instanceData, /* Unused. */ sl@0: char *buf, /* Where to store data read. */ sl@0: int bufSize, /* How much space is available sl@0: * in the buffer? */ sl@0: int *errorCode) /* Where to store error code. */ sl@0: { sl@0: int fd; sl@0: int bytesRead; /* How many bytes were read? */ sl@0: sl@0: *errorCode = 0; sl@0: errno = 0; sl@0: fd = (int) ((FileState*)instanceData)->fileRef; sl@0: bytesRead = read(fd, buf, (size_t) bufSize); sl@0: if (bytesRead > -1) { sl@0: return bytesRead; sl@0: } sl@0: *errorCode = errno; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * StdIOOutput-- sl@0: * sl@0: * Writes the given output on the IO channel. Returns count of how sl@0: * many characters were actually written, and an error indication. sl@0: * sl@0: * Results: sl@0: * A count of how many characters were written is returned and an sl@0: * error indication is returned in an output argument. sl@0: * sl@0: * Side effects: sl@0: * Writes output on the actual channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: StdIOOutput( sl@0: ClientData instanceData, /* Unused. */ sl@0: CONST char *buf, /* The data buffer. */ sl@0: int toWrite, /* How many bytes to write? */ sl@0: int *errorCode) /* Where to store error code. */ sl@0: { sl@0: int written; sl@0: int fd; sl@0: sl@0: *errorCode = 0; sl@0: errno = 0; sl@0: fd = (int) ((FileState*)instanceData)->fileRef; sl@0: written = write(fd, (void*)buf, (size_t) toWrite); sl@0: if (written > -1) { sl@0: return written; sl@0: } sl@0: *errorCode = errno; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * StdIOSeek -- sl@0: * sl@0: * Seeks on an IO channel. Returns the new position. sl@0: * sl@0: * Results: sl@0: * -1 if failed, the new position if successful. If failed, it sl@0: * also sets *errorCodePtr to the error code. sl@0: * sl@0: * Side effects: sl@0: * Moves the location at which the channel will be accessed in sl@0: * future operations. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: StdIOSeek( sl@0: ClientData instanceData, /* Unused. */ sl@0: long offset, /* Offset to seek to. */ sl@0: int mode, /* Relative to where should we seek? */ sl@0: int *errorCodePtr) /* To store error code. */ sl@0: { sl@0: int newLoc; sl@0: int fd; sl@0: sl@0: *errorCodePtr = 0; sl@0: fd = (int) ((FileState*)instanceData)->fileRef; sl@0: newLoc = lseek(fd, offset, mode); sl@0: if (newLoc > -1) { sl@0: return newLoc; sl@0: } sl@0: *errorCodePtr = errno; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_PidObjCmd -- sl@0: * sl@0: * This procedure is invoked to process the "pid" Tcl command. sl@0: * See the user documentation for details on what it does. sl@0: * sl@0: * Results: sl@0: * A standard Tcl result. sl@0: * sl@0: * Side effects: sl@0: * See the user documentation. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: int sl@0: Tcl_PidObjCmd(dummy, interp, objc, objv) sl@0: ClientData dummy; /* Not used. */ sl@0: Tcl_Interp *interp; /* Current interpreter. */ sl@0: int objc; /* Number of arguments. */ sl@0: Tcl_Obj *CONST *objv; /* Argument strings. */ sl@0: { sl@0: ProcessSerialNumber psn; sl@0: char buf[20]; sl@0: Tcl_Channel chan; sl@0: Tcl_Obj *resultPtr; sl@0: sl@0: if (objc > 2) { sl@0: Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); sl@0: return TCL_ERROR; sl@0: } sl@0: if (objc == 1) { sl@0: resultPtr = Tcl_GetObjResult(interp); sl@0: GetCurrentProcess(&psn); sl@0: sprintf(buf, "0x%08x%08x", psn.highLongOfPSN, psn.lowLongOfPSN); sl@0: Tcl_SetStringObj(resultPtr, buf, -1); sl@0: } else { sl@0: chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), sl@0: NULL); sl@0: if (chan == (Tcl_Channel) NULL) { sl@0: return TCL_ERROR; sl@0: } sl@0: /* sl@0: * We can't create pipelines on the Mac so sl@0: * this will always return an empty list. sl@0: */ sl@0: } sl@0: sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpGetDefaultStdChannel -- sl@0: * sl@0: * Constructs a channel for the specified standard OS handle. sl@0: * sl@0: * Results: sl@0: * Returns the specified default standard channel, or NULL. sl@0: * sl@0: * Side effects: sl@0: * May cause the creation of a standard channel and the underlying sl@0: * file. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: TclpGetDefaultStdChannel( sl@0: int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */ sl@0: { sl@0: Tcl_Channel channel = NULL; sl@0: int fd = 0; /* Initializations needed to prevent */ sl@0: int mode = 0; /* compiler warning (used before set). */ sl@0: char *bufMode = NULL; sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: int channelPermissions; sl@0: FileState *fileState; sl@0: sl@0: /* sl@0: * If the channels were not created yet, create them now and sl@0: * store them in the static variables. sl@0: */ sl@0: sl@0: switch (type) { sl@0: case TCL_STDIN: sl@0: fd = 0; sl@0: channelPermissions = TCL_READABLE; sl@0: bufMode = "line"; sl@0: break; sl@0: case TCL_STDOUT: sl@0: fd = 1; sl@0: channelPermissions = TCL_WRITABLE; sl@0: bufMode = "line"; sl@0: break; sl@0: case TCL_STDERR: sl@0: fd = 2; sl@0: channelPermissions = TCL_WRITABLE; sl@0: bufMode = "none"; sl@0: break; sl@0: default: sl@0: panic("TclGetDefaultStdChannel: Unexpected channel type"); sl@0: break; sl@0: } sl@0: sl@0: sprintf(channelName, "console%d", (int) fd); sl@0: fileState = (FileState *) ckalloc((unsigned) sizeof(FileState)); sl@0: channel = Tcl_CreateChannel(&consoleChannelType, channelName, sl@0: (ClientData) fileState, channelPermissions); sl@0: fileState->fileChan = channel; sl@0: fileState->fileRef = fd; sl@0: sl@0: /* sl@0: * Set up the normal channel options for stdio handles. sl@0: */ sl@0: sl@0: Tcl_SetChannelOption(NULL, channel, "-translation", "cr"); sl@0: Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode); sl@0: sl@0: return channel; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpOpenFileChannel -- sl@0: * sl@0: * Open a File based channel on MacOS systems. sl@0: * sl@0: * Results: sl@0: * The new channel or NULL. If NULL, the output argument sl@0: * errorCodePtr is set to a POSIX error. sl@0: * sl@0: * Side effects: sl@0: * May open the channel and may cause creation of a file on the sl@0: * file system. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: TclpOpenFileChannel( sl@0: Tcl_Interp *interp, /* Interpreter for error reporting; sl@0: * can be NULL. */ sl@0: Tcl_Obj *pathPtr, /* Name of file to open. */ sl@0: int mode, /* POSIX open mode. */ sl@0: int permissions) /* If the open involves creating a sl@0: * file, with what modes to create sl@0: * it? */ sl@0: { sl@0: Tcl_Channel chan; sl@0: CONST char *native; sl@0: int errorCode; sl@0: sl@0: native = Tcl_FSGetNativePath(pathPtr); sl@0: if (native == NULL) { sl@0: return NULL; sl@0: } sl@0: chan = OpenFileChannel(native, mode, permissions, &errorCode); sl@0: sl@0: if (chan == NULL) { sl@0: Tcl_SetErrno(errorCode); sl@0: if (interp != (Tcl_Interp *) NULL) { sl@0: Tcl_AppendResult(interp, "couldn't open \"", sl@0: Tcl_GetString(pathPtr), "\": ", sl@0: Tcl_PosixError(interp), (char *) NULL); sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: return chan; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * OpenFileChannel-- sl@0: * sl@0: * Opens a Macintosh file and creates a Tcl channel to control it. sl@0: * sl@0: * Results: sl@0: * A Tcl channel. sl@0: * sl@0: * Side effects: sl@0: * Will open a Macintosh file. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static Tcl_Channel sl@0: OpenFileChannel( sl@0: CONST char *fileName, /* Name of file to open (native). */ sl@0: int mode, /* Mode for opening file. */ sl@0: int permissions, /* If the open involves creating a sl@0: * file, with what modes to create sl@0: * it? */ sl@0: int *errorCodePtr) /* Where to store error code. */ sl@0: { sl@0: int channelPermissions; sl@0: Tcl_Channel chan; sl@0: char macPermision; sl@0: FSSpec fileSpec; sl@0: OSErr err; sl@0: short fileRef; sl@0: FileState *fileState; sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: ThreadSpecificData *tsdPtr; sl@0: sl@0: tsdPtr = FileInit(); sl@0: sl@0: /* sl@0: * Note we use fsRdWrShPerm instead of fsRdWrPerm which allows shared sl@0: * writes on a file. This isn't common on a mac but is common with sl@0: * Windows and UNIX and the feature is used by Tcl. sl@0: */ sl@0: sl@0: switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { sl@0: case O_RDWR: sl@0: channelPermissions = (TCL_READABLE | TCL_WRITABLE); sl@0: macPermision = fsRdWrShPerm; sl@0: break; sl@0: case O_WRONLY: sl@0: /* sl@0: * Mac's fsRdPerm permission actually defaults to fsRdWrPerm because sl@0: * the Mac OS doesn't realy support write only access. We explicitly sl@0: * set the permission fsRdWrShPerm so that we can have shared write sl@0: * access. sl@0: */ sl@0: channelPermissions = TCL_WRITABLE; sl@0: macPermision = fsRdWrShPerm; sl@0: break; sl@0: case O_RDONLY: sl@0: default: sl@0: channelPermissions = TCL_READABLE; sl@0: macPermision = fsRdPerm; sl@0: break; sl@0: } sl@0: sl@0: err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec); sl@0: if ((err != noErr) && (err != fnfErr)) { sl@0: *errorCodePtr = errno = TclMacOSErrorToPosixError(err); sl@0: Tcl_SetErrno(errno); sl@0: return NULL; sl@0: } sl@0: sl@0: if ((err == fnfErr) && (mode & O_CREAT)) { sl@0: err = HCreate(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, TCL_FILE_CREATOR, 'TEXT'); sl@0: if (err != noErr) { sl@0: *errorCodePtr = errno = TclMacOSErrorToPosixError(err); sl@0: Tcl_SetErrno(errno); sl@0: return NULL; sl@0: } sl@0: } else if ((mode & O_CREAT) && (mode & O_EXCL)) { sl@0: *errorCodePtr = errno = EEXIST; sl@0: Tcl_SetErrno(errno); sl@0: return NULL; sl@0: } sl@0: sl@0: err = HOpenDF(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, macPermision, &fileRef); sl@0: if (err != noErr) { sl@0: *errorCodePtr = errno = TclMacOSErrorToPosixError(err); sl@0: Tcl_SetErrno(errno); sl@0: return NULL; sl@0: } sl@0: sl@0: if (mode & O_TRUNC) { sl@0: SetEOF(fileRef, 0); sl@0: } sl@0: sl@0: sprintf(channelName, "file%d", (int) fileRef); sl@0: fileState = (FileState *) ckalloc((unsigned) sizeof(FileState)); sl@0: chan = Tcl_CreateChannel(&fileChannelType, channelName, sl@0: (ClientData) fileState, channelPermissions); sl@0: if (chan == (Tcl_Channel) NULL) { sl@0: *errorCodePtr = errno = EFAULT; sl@0: Tcl_SetErrno(errno); sl@0: FSClose(fileRef); sl@0: ckfree((char *) fileState); sl@0: return NULL; sl@0: } sl@0: sl@0: fileState->fileChan = chan; sl@0: fileState->nextPtr = tsdPtr->firstFilePtr; sl@0: tsdPtr->firstFilePtr = fileState; sl@0: fileState->volumeRef = fileSpec.vRefNum; sl@0: fileState->fileRef = fileRef; sl@0: fileState->pending = 0; sl@0: fileState->watchMask = 0; sl@0: if (mode & O_APPEND) { sl@0: fileState->appendMode = true; sl@0: } else { sl@0: fileState->appendMode = false; sl@0: } sl@0: sl@0: if ((mode & O_APPEND) || (mode & O_APPEND)) { sl@0: if (Tcl_Seek(chan, 0, SEEK_END) < 0) { sl@0: *errorCodePtr = errno = EFAULT; sl@0: Tcl_SetErrno(errno); sl@0: Tcl_Close(NULL, chan); sl@0: FSClose(fileRef); sl@0: ckfree((char *) fileState); sl@0: return NULL; sl@0: } sl@0: } sl@0: sl@0: return chan; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_MakeFileChannel -- sl@0: * sl@0: * Makes a Tcl_Channel from an existing OS level file handle. sl@0: * sl@0: * Results: sl@0: * The Tcl_Channel created around the preexisting OS level file handle. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: Tcl_MakeFileChannel(handle, mode) sl@0: ClientData handle; /* OS level handle. */ sl@0: int mode; /* ORed combination of TCL_READABLE and sl@0: * TCL_WRITABLE to indicate file mode. */ sl@0: { sl@0: /* sl@0: * Not implemented yet. sl@0: */ sl@0: sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileBlockMode -- sl@0: * sl@0: * Set blocking or non-blocking mode on channel. Macintosh files sl@0: * can never really be set to blocking or non-blocking modes. sl@0: * However, we don't generate an error - we just return success. sl@0: * sl@0: * Results: sl@0: * 0 if successful, errno when failed. sl@0: * sl@0: * Side effects: sl@0: * Sets the device into blocking or non-blocking mode. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: FileBlockMode( sl@0: ClientData instanceData, /* Unused. */ sl@0: int mode) /* The mode to set. */ sl@0: { sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileClose -- sl@0: * sl@0: * Closes the IO channel. sl@0: * sl@0: * Results: sl@0: * 0 if successful, the value of errno if failed. sl@0: * sl@0: * Side effects: sl@0: * Closes the physical channel sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: FileClose( sl@0: ClientData instanceData, /* Unused. */ sl@0: Tcl_Interp *interp) /* Unused. */ sl@0: { sl@0: FileState *fileState = (FileState *) instanceData; sl@0: int errorCode = 0; sl@0: OSErr err; sl@0: sl@0: err = FSClose(fileState->fileRef); sl@0: FlushVol(NULL, fileState->volumeRef); sl@0: if (err != noErr) { sl@0: errorCode = errno = TclMacOSErrorToPosixError(err); sl@0: panic("error during file close"); sl@0: } sl@0: sl@0: ckfree((char *) fileState); sl@0: Tcl_SetErrno(errorCode); sl@0: return errorCode; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileInput -- sl@0: * sl@0: * Reads input from the IO channel into the buffer given. Returns sl@0: * count of how many bytes were actually read, and an error indication. sl@0: * sl@0: * Results: sl@0: * A count of how many bytes were read is returned and an error sl@0: * indication is returned in an output argument. sl@0: * sl@0: * Side effects: sl@0: * Reads input from the actual channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: FileInput( sl@0: ClientData instanceData, /* Unused. */ sl@0: char *buffer, /* Where to store data read. */ sl@0: int bufSize, /* How much space is available sl@0: * in the buffer? */ sl@0: int *errorCodePtr) /* Where to store error code. */ sl@0: { sl@0: FileState *fileState = (FileState *) instanceData; sl@0: OSErr err; sl@0: long length = bufSize; sl@0: sl@0: *errorCodePtr = 0; sl@0: errno = 0; sl@0: err = FSRead(fileState->fileRef, &length, buffer); sl@0: if ((err == noErr) || (err == eofErr)) { sl@0: return length; sl@0: } else { sl@0: switch (err) { sl@0: case ioErr: sl@0: *errorCodePtr = errno = EIO; sl@0: case afpAccessDenied: sl@0: *errorCodePtr = errno = EACCES; sl@0: default: sl@0: *errorCodePtr = errno = EINVAL; sl@0: } sl@0: return -1; sl@0: } sl@0: *errorCodePtr = errno; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileOutput-- sl@0: * sl@0: * Writes the given output on the IO channel. Returns count of how sl@0: * many characters were actually written, and an error indication. sl@0: * sl@0: * Results: sl@0: * A count of how many characters were written is returned and an sl@0: * error indication is returned in an output argument. sl@0: * sl@0: * Side effects: sl@0: * Writes output on the actual channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: FileOutput( sl@0: ClientData instanceData, /* Unused. */ sl@0: CONST char *buffer, /* The data buffer. */ sl@0: int toWrite, /* How many bytes to write? */ sl@0: int *errorCodePtr) /* Where to store error code. */ sl@0: { sl@0: FileState *fileState = (FileState *) instanceData; sl@0: long length = toWrite; sl@0: OSErr err; sl@0: sl@0: *errorCodePtr = 0; sl@0: errno = 0; sl@0: sl@0: if (fileState->appendMode == true) { sl@0: FileSeek(instanceData, 0, SEEK_END, errorCodePtr); sl@0: *errorCodePtr = 0; sl@0: } sl@0: sl@0: err = FSWrite(fileState->fileRef, &length, buffer); sl@0: if (err == noErr) { sl@0: err = FlushFile(fileState->fileRef); sl@0: } else { sl@0: *errorCodePtr = errno = TclMacOSErrorToPosixError(err); sl@0: return -1; sl@0: } sl@0: return length; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileSeek -- sl@0: * sl@0: * Seeks on an IO channel. Returns the new position. sl@0: * sl@0: * Results: sl@0: * -1 if failed, the new position if successful. If failed, it sl@0: * also sets *errorCodePtr to the error code. sl@0: * sl@0: * Side effects: sl@0: * Moves the location at which the channel will be accessed in sl@0: * future operations. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: FileSeek( sl@0: ClientData instanceData, /* Unused. */ sl@0: long offset, /* Offset to seek to. */ sl@0: int mode, /* Relative to where should we seek? */ sl@0: int *errorCodePtr) /* To store error code. */ sl@0: { sl@0: FileState *fileState = (FileState *) instanceData; sl@0: IOParam pb; sl@0: OSErr err; sl@0: sl@0: *errorCodePtr = 0; sl@0: pb.ioCompletion = NULL; sl@0: pb.ioRefNum = fileState->fileRef; sl@0: if (mode == SEEK_SET) { sl@0: pb.ioPosMode = fsFromStart; sl@0: } else if (mode == SEEK_END) { sl@0: pb.ioPosMode = fsFromLEOF; sl@0: } else if (mode == SEEK_CUR) { sl@0: err = PBGetFPosSync((ParmBlkPtr) &pb); sl@0: if (pb.ioResult == noErr) { sl@0: if (offset == 0) { sl@0: return pb.ioPosOffset; sl@0: } sl@0: offset += pb.ioPosOffset; sl@0: } sl@0: pb.ioPosMode = fsFromStart; sl@0: } sl@0: pb.ioPosOffset = offset; sl@0: err = PBSetFPosSync((ParmBlkPtr) &pb); sl@0: if (pb.ioResult == noErr){ sl@0: return pb.ioPosOffset; sl@0: } else if (pb.ioResult == eofErr) { sl@0: long currentEOF, newEOF; sl@0: long buffer, i, length; sl@0: sl@0: err = PBGetEOFSync((ParmBlkPtr) &pb); sl@0: currentEOF = (long) pb.ioMisc; sl@0: if (mode == SEEK_SET) { sl@0: newEOF = offset; sl@0: } else if (mode == SEEK_END) { sl@0: newEOF = offset + currentEOF; sl@0: } else if (mode == SEEK_CUR) { sl@0: err = PBGetFPosSync((ParmBlkPtr) &pb); sl@0: newEOF = offset + pb.ioPosOffset; sl@0: } sl@0: sl@0: /* sl@0: * Write 0's to the new EOF. sl@0: */ sl@0: pb.ioPosOffset = 0; sl@0: pb.ioPosMode = fsFromLEOF; sl@0: err = PBGetFPosSync((ParmBlkPtr) &pb); sl@0: length = 1; sl@0: buffer = 0; sl@0: for (i = 0; i < (newEOF - currentEOF); i++) { sl@0: err = FSWrite(fileState->fileRef, &length, &buffer); sl@0: } sl@0: err = PBGetFPosSync((ParmBlkPtr) &pb); sl@0: if (pb.ioResult == noErr){ sl@0: return pb.ioPosOffset; sl@0: } sl@0: } sl@0: *errorCodePtr = errno = TclMacOSErrorToPosixError(err); sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * CommonWatch -- sl@0: * sl@0: * Initialize the notifier to watch handles from this channel. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: CommonWatch( sl@0: ClientData instanceData, /* The file state. */ sl@0: int mask) /* Events of interest; an OR-ed sl@0: * combination of TCL_READABLE, sl@0: * TCL_WRITABLE and TCL_EXCEPTION. */ sl@0: { sl@0: FileState *infoPtr = (FileState *) instanceData; sl@0: Tcl_Time blockTime = { 0, 0 }; sl@0: sl@0: infoPtr->watchMask = mask; sl@0: if (infoPtr->watchMask) { sl@0: Tcl_SetMaxBlockTime(&blockTime); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * FileThreadActionProc -- sl@0: * sl@0: * Insert or remove any thread local refs to this channel. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Changes thread local list of valid channels. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: FileThreadActionProc (instanceData, action) sl@0: ClientData instanceData; sl@0: int action; sl@0: { sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); sl@0: FileState *infoPtr = (FileState *) instanceData; sl@0: sl@0: if (action == TCL_CHANNEL_THREAD_INSERT) { sl@0: infoPtr->nextPtr = tsdPtr->firstFilePtr; sl@0: tsdPtr->firstFilePtr = infoPtr; sl@0: } else { sl@0: FileState **nextPtrPtr; sl@0: int removed = 0; sl@0: sl@0: for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL; sl@0: nextPtrPtr = &((*nextPtrPtr)->nextPtr)) { sl@0: if ((*nextPtrPtr) == infoPtr) { sl@0: (*nextPtrPtr) = infoPtr->nextPtr; sl@0: removed = 1; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: * This could happen if the channel was created in one thread sl@0: * and then moved to another without updating the thread sl@0: * local data in each thread. sl@0: */ sl@0: sl@0: if (!removed) { sl@0: panic("file info ptr not on thread channel list"); sl@0: } sl@0: } sl@0: }