os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacChan.c
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacChan.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1275 @@
1.4 +/*
1.5 + * tclMacChan.c
1.6 + *
1.7 + * Channel drivers for Macintosh channels for the
1.8 + * console fds.
1.9 + *
1.10 + * Copyright (c) 1996-1997 Sun Microsystems, Inc.
1.11 + *
1.12 + * See the file "license.terms" for information on usage and redistribution
1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
1.14 + *
1.15 + * RCS: @(#) $Id: tclMacChan.c,v 1.21.2.1 2005/01/27 22:53:34 andreas_kupries Exp $
1.16 + */
1.17 +
1.18 +#include "tclInt.h"
1.19 +#include "tclPort.h"
1.20 +#include "tclMacInt.h"
1.21 +#include <Aliases.h>
1.22 +#include <Errors.h>
1.23 +#include <Files.h>
1.24 +#include <Gestalt.h>
1.25 +#include <Processes.h>
1.26 +#include <Strings.h>
1.27 +#include <FSpCompat.h>
1.28 +#include <MoreFiles.h>
1.29 +#include <MoreFilesExtras.h>
1.30 +#include "tclIO.h"
1.31 +
1.32 +#ifdef __MSL__
1.33 +#include <unix.mac.h>
1.34 +#define TCL_FILE_CREATOR (__getcreator(0))
1.35 +#else
1.36 +#define TCL_FILE_CREATOR 'MPW '
1.37 +#endif
1.38 +
1.39 +/*
1.40 + * This structure describes per-instance state of a
1.41 + * macintosh file based channel.
1.42 + */
1.43 +
1.44 +typedef struct FileState {
1.45 + short fileRef; /* Macintosh file reference number. */
1.46 + Tcl_Channel fileChan; /* Pointer to the channel for this file. */
1.47 + int watchMask; /* OR'ed set of flags indicating which events
1.48 + * are being watched. */
1.49 + int appendMode; /* Flag to tell if in O_APPEND mode or not. */
1.50 + int volumeRef; /* Flag to tell if in O_APPEND mode or not. */
1.51 + int pending; /* 1 if message is pending on queue. */
1.52 + struct FileState *nextPtr; /* Pointer to next registered file. */
1.53 +} FileState;
1.54 +
1.55 +typedef struct ThreadSpecificData {
1.56 + int initialized; /* True after the thread initializes */
1.57 + FileState *firstFilePtr; /* the head of the list of files managed
1.58 + * that are being watched for file events. */
1.59 + Tcl_Channel stdinChannel;
1.60 + Tcl_Channel stdoutChannel; /* Note - these seem unused */
1.61 + Tcl_Channel stderrChannel;
1.62 +} ThreadSpecificData;
1.63 +
1.64 +static Tcl_ThreadDataKey dataKey;
1.65 +
1.66 +/*
1.67 + * The following structure is what is added to the Tcl event queue when
1.68 + * file events are generated.
1.69 + */
1.70 +
1.71 +typedef struct FileEvent {
1.72 + Tcl_Event header; /* Information that is standard for
1.73 + * all events. */
1.74 + FileState *infoPtr; /* Pointer to file info structure. Note
1.75 + * that we still have to verify that the
1.76 + * file exists before dereferencing this
1.77 + * pointer. */
1.78 +} FileEvent;
1.79 +
1.80 +
1.81 +/*
1.82 + * Static routines for this file:
1.83 + */
1.84 +
1.85 +static int CommonGetHandle _ANSI_ARGS_((ClientData instanceData,
1.86 + int direction, ClientData *handlePtr));
1.87 +static void CommonWatch _ANSI_ARGS_((ClientData instanceData,
1.88 + int mask));
1.89 +static int FileBlockMode _ANSI_ARGS_((ClientData instanceData,
1.90 + int mode));
1.91 +static void FileChannelExitHandler _ANSI_ARGS_((
1.92 + ClientData clientData));
1.93 +static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
1.94 + int flags));
1.95 +static int FileClose _ANSI_ARGS_((ClientData instanceData,
1.96 + Tcl_Interp *interp));
1.97 +static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
1.98 + int flags));
1.99 +static ThreadSpecificData *FileInit _ANSI_ARGS_((void));
1.100 +static int FileInput _ANSI_ARGS_((ClientData instanceData,
1.101 + char *buf, int toRead, int *errorCode));
1.102 +static int FileOutput _ANSI_ARGS_((ClientData instanceData,
1.103 + CONST char *buf, int toWrite, int *errorCode));
1.104 +static int FileSeek _ANSI_ARGS_((ClientData instanceData,
1.105 + long offset, int mode, int *errorCode));
1.106 +static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
1.107 + int flags));
1.108 +static void FileThreadActionProc _ANSI_ARGS_ ((
1.109 + ClientData instanceData, int action));
1.110 +static Tcl_Channel OpenFileChannel _ANSI_ARGS_((CONST char *fileName,
1.111 + int mode, int permissions, int *errorCodePtr));
1.112 +static int StdIOBlockMode _ANSI_ARGS_((ClientData instanceData,
1.113 + int mode));
1.114 +static int StdIOClose _ANSI_ARGS_((ClientData instanceData,
1.115 + Tcl_Interp *interp));
1.116 +static int StdIOInput _ANSI_ARGS_((ClientData instanceData,
1.117 + char *buf, int toRead, int *errorCode));
1.118 +static int StdIOOutput _ANSI_ARGS_((ClientData instanceData,
1.119 + CONST char *buf, int toWrite, int *errorCode));
1.120 +static int StdIOSeek _ANSI_ARGS_((ClientData instanceData,
1.121 + long offset, int mode, int *errorCode));
1.122 +static int StdReady _ANSI_ARGS_((ClientData instanceData,
1.123 + int mask));
1.124 +
1.125 +/*
1.126 + * This structure describes the channel type structure for file based IO:
1.127 + */
1.128 +
1.129 +static Tcl_ChannelType consoleChannelType = {
1.130 + "file", /* Type name. */
1.131 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.132 + StdIOClose, /* Close proc. */
1.133 + StdIOInput, /* Input proc. */
1.134 + StdIOOutput, /* Output proc. */
1.135 + StdIOSeek, /* Seek proc. */
1.136 + NULL, /* Set option proc. */
1.137 + NULL, /* Get option proc. */
1.138 + CommonWatch, /* Initialize notifier. */
1.139 + CommonGetHandle /* Get OS handles out of channel. */
1.140 + NULL, /* close2proc. */
1.141 + StdIOBlockMode, /* Set blocking/nonblocking mode.*/
1.142 + NULL, /* flush proc. */
1.143 + NULL, /* handler proc. */
1.144 + NULL, /* wide seek proc. */
1.145 + NULL, /* thread actions */
1.146 +};
1.147 +
1.148 +/*
1.149 + * This variable describes the channel type structure for file based IO.
1.150 + */
1.151 +
1.152 +static Tcl_ChannelType fileChannelType = {
1.153 + "file", /* Type name. */
1.154 + TCL_CHANNEL_VERSION_4, /* v4 channel */
1.155 + FileClose, /* Close proc. */
1.156 + FileInput, /* Input proc. */
1.157 + FileOutput, /* Output proc. */
1.158 + FileSeek, /* Seek proc. */
1.159 + NULL, /* Set option proc. */
1.160 + NULL, /* Get option proc. */
1.161 + CommonWatch, /* Initialize notifier. */
1.162 + CommonGetHandle /* Get OS handles out of channel. */
1.163 + NULL, /* close2proc. */
1.164 + FileBlockMode, /* Set blocking/nonblocking mode.*/
1.165 + NULL, /* flush proc. */
1.166 + NULL, /* handler proc. */
1.167 + NULL, /* wide seek proc. */
1.168 + FileThreadActionProc, /* thread actions */
1.169 +};
1.170 +
1.171 +
1.172 +/*
1.173 + * Hack to allow Mac Tk to override the TclGetStdChannels function.
1.174 + */
1.175 +
1.176 +typedef void (*TclGetStdChannelsProc) _ANSI_ARGS_((Tcl_Channel *stdinPtr,
1.177 + Tcl_Channel *stdoutPtr, Tcl_Channel *stderrPtr));
1.178 +
1.179 +TclGetStdChannelsProc getStdChannelsProc = NULL;
1.180 +
1.181 +
1.182 +/*
1.183 + *----------------------------------------------------------------------
1.184 + *
1.185 + * FileInit --
1.186 + *
1.187 + * This function initializes the file channel event source.
1.188 + *
1.189 + * Results:
1.190 + * None.
1.191 + *
1.192 + * Side effects:
1.193 + * Creates a new event source.
1.194 + *
1.195 + *----------------------------------------------------------------------
1.196 + */
1.197 +
1.198 +static ThreadSpecificData *
1.199 +FileInit()
1.200 +{
1.201 + ThreadSpecificData *tsdPtr =
1.202 + (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
1.203 + if (tsdPtr == NULL) {
1.204 + tsdPtr = TCL_TSD_INIT(&dataKey);
1.205 + tsdPtr->firstFilePtr = NULL;
1.206 + Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
1.207 + Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL);
1.208 + }
1.209 + return tsdPtr;
1.210 +}
1.211 +
1.212 +/*
1.213 + *----------------------------------------------------------------------
1.214 + *
1.215 + * FileChannelExitHandler --
1.216 + *
1.217 + * This function is called to cleanup the channel driver before
1.218 + * Tcl is unloaded.
1.219 + *
1.220 + * Results:
1.221 + * None.
1.222 + *
1.223 + * Side effects:
1.224 + * Destroys the communication window.
1.225 + *
1.226 + *----------------------------------------------------------------------
1.227 + */
1.228 +
1.229 +static void
1.230 +FileChannelExitHandler(
1.231 + ClientData clientData) /* Old window proc */
1.232 +{
1.233 + Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
1.234 +}
1.235 +
1.236 +/*
1.237 + *----------------------------------------------------------------------
1.238 + *
1.239 + * FileSetupProc --
1.240 + *
1.241 + * This procedure is invoked before Tcl_DoOneEvent blocks waiting
1.242 + * for an event.
1.243 + *
1.244 + * Results:
1.245 + * None.
1.246 + *
1.247 + * Side effects:
1.248 + * Adjusts the block time if needed.
1.249 + *
1.250 + *----------------------------------------------------------------------
1.251 + */
1.252 +
1.253 +void
1.254 +FileSetupProc(
1.255 + ClientData data, /* Not used. */
1.256 + int flags) /* Event flags as passed to Tcl_DoOneEvent. */
1.257 +{
1.258 + FileState *infoPtr;
1.259 + Tcl_Time blockTime = { 0, 0 };
1.260 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.261 +
1.262 + if (!(flags & TCL_FILE_EVENTS)) {
1.263 + return;
1.264 + }
1.265 +
1.266 + /*
1.267 + * Check to see if there is a ready file. If so, poll.
1.268 + */
1.269 +
1.270 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.271 + infoPtr = infoPtr->nextPtr) {
1.272 + if (infoPtr->watchMask) {
1.273 + Tcl_SetMaxBlockTime(&blockTime);
1.274 + break;
1.275 + }
1.276 + }
1.277 +}
1.278 +
1.279 +/*
1.280 + *----------------------------------------------------------------------
1.281 + *
1.282 + * FileCheckProc --
1.283 + *
1.284 + * This procedure is called by Tcl_DoOneEvent to check the file
1.285 + * event source for events.
1.286 + *
1.287 + * Results:
1.288 + * None.
1.289 + *
1.290 + * Side effects:
1.291 + * May queue an event.
1.292 + *
1.293 + *----------------------------------------------------------------------
1.294 + */
1.295 +
1.296 +static void
1.297 +FileCheckProc(
1.298 + ClientData data, /* Not used. */
1.299 + int flags) /* Event flags as passed to Tcl_DoOneEvent. */
1.300 +{
1.301 + FileEvent *evPtr;
1.302 + FileState *infoPtr;
1.303 + int sentMsg = 0;
1.304 + Tcl_Time blockTime = { 0, 0 };
1.305 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.306 +
1.307 + if (!(flags & TCL_FILE_EVENTS)) {
1.308 + return;
1.309 + }
1.310 +
1.311 + /*
1.312 + * Queue events for any ready files that don't already have events
1.313 + * queued (caused by persistent states that won't generate WinSock
1.314 + * events).
1.315 + */
1.316 +
1.317 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.318 + infoPtr = infoPtr->nextPtr) {
1.319 + if (infoPtr->watchMask && !infoPtr->pending) {
1.320 + infoPtr->pending = 1;
1.321 + evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
1.322 + evPtr->header.proc = FileEventProc;
1.323 + evPtr->infoPtr = infoPtr;
1.324 + Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
1.325 + }
1.326 + }
1.327 +}
1.328 +
1.329 +/*----------------------------------------------------------------------
1.330 + *
1.331 + * FileEventProc --
1.332 + *
1.333 + * This function is invoked by Tcl_ServiceEvent when a file event
1.334 + * reaches the front of the event queue. This procedure invokes
1.335 + * Tcl_NotifyChannel on the file.
1.336 + *
1.337 + * Results:
1.338 + * Returns 1 if the event was handled, meaning it should be removed
1.339 + * from the queue. Returns 0 if the event was not handled, meaning
1.340 + * it should stay on the queue. The only time the event isn't
1.341 + * handled is if the TCL_FILE_EVENTS flag bit isn't set.
1.342 + *
1.343 + * Side effects:
1.344 + * Whatever the notifier callback does.
1.345 + *
1.346 + *----------------------------------------------------------------------
1.347 + */
1.348 +
1.349 +static int
1.350 +FileEventProc(
1.351 + Tcl_Event *evPtr, /* Event to service. */
1.352 + int flags) /* Flags that indicate what events to
1.353 + * handle, such as TCL_FILE_EVENTS. */
1.354 +{
1.355 + FileEvent *fileEvPtr = (FileEvent *)evPtr;
1.356 + FileState *infoPtr;
1.357 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.358 +
1.359 + if (!(flags & TCL_FILE_EVENTS)) {
1.360 + return 0;
1.361 + }
1.362 +
1.363 + /*
1.364 + * Search through the list of watched files for the one whose handle
1.365 + * matches the event. We do this rather than simply dereferencing
1.366 + * the handle in the event so that files can be deleted while the
1.367 + * event is in the queue.
1.368 + */
1.369 +
1.370 + for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL;
1.371 + infoPtr = infoPtr->nextPtr) {
1.372 + if (fileEvPtr->infoPtr == infoPtr) {
1.373 + infoPtr->pending = 0;
1.374 + Tcl_NotifyChannel(infoPtr->fileChan, infoPtr->watchMask);
1.375 + break;
1.376 + }
1.377 + }
1.378 + return 1;
1.379 +}
1.380 +
1.381 +/*
1.382 + *----------------------------------------------------------------------
1.383 + *
1.384 + * StdIOBlockMode --
1.385 + *
1.386 + * Set blocking or non-blocking mode on channel.
1.387 + *
1.388 + * Results:
1.389 + * 0 if successful, errno when failed.
1.390 + *
1.391 + * Side effects:
1.392 + * Sets the device into blocking or non-blocking mode.
1.393 + *
1.394 + *----------------------------------------------------------------------
1.395 + */
1.396 +
1.397 +static int
1.398 +StdIOBlockMode(
1.399 + ClientData instanceData, /* Unused. */
1.400 + int mode) /* The mode to set. */
1.401 +{
1.402 + /*
1.403 + * Do not allow putting stdin, stdout or stderr into nonblocking mode.
1.404 + */
1.405 +
1.406 + if (mode == TCL_MODE_NONBLOCKING) {
1.407 + return EFAULT;
1.408 + }
1.409 +
1.410 + return 0;
1.411 +}
1.412 +
1.413 +/*
1.414 + *----------------------------------------------------------------------
1.415 + *
1.416 + * StdIOClose --
1.417 + *
1.418 + * Closes the IO channel.
1.419 + *
1.420 + * Results:
1.421 + * 0 if successful, the value of errno if failed.
1.422 + *
1.423 + * Side effects:
1.424 + * Closes the physical channel
1.425 + *
1.426 + *----------------------------------------------------------------------
1.427 + */
1.428 +
1.429 +static int
1.430 +StdIOClose(
1.431 + ClientData instanceData, /* Unused. */
1.432 + Tcl_Interp *interp) /* Unused. */
1.433 +{
1.434 + int fd, errorCode = 0;
1.435 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.436 +
1.437 + /*
1.438 + * Invalidate the stdio cache if necessary. Note that we assume that
1.439 + * the stdio file and channel pointers will become invalid at the same
1.440 + * time.
1.441 + * Do not close standard channels while in thread-exit.
1.442 + */
1.443 +
1.444 + fd = (int) ((FileState*)instanceData)->fileRef;
1.445 + if (!TclInThreadExit()) {
1.446 + if (fd == 0) {
1.447 + tsdPtr->stdinChannel = NULL;
1.448 + } else if (fd == 1) {
1.449 + tsdPtr->stdoutChannel = NULL;
1.450 + } else if (fd == 2) {
1.451 + tsdPtr->stderrChannel = NULL;
1.452 + } else {
1.453 + panic("recieved invalid std file");
1.454 + }
1.455 +
1.456 + if (close(fd) < 0) {
1.457 + errorCode = errno;
1.458 + }
1.459 + }
1.460 + return errorCode;
1.461 +}
1.462 +
1.463 +/*
1.464 + *----------------------------------------------------------------------
1.465 + *
1.466 + * CommonGetHandle --
1.467 + *
1.468 + * Called from Tcl_GetChannelHandle to retrieve OS handles from inside
1.469 + * a file based channel.
1.470 + *
1.471 + * Results:
1.472 + * The appropriate handle or NULL if not present.
1.473 + *
1.474 + * Side effects:
1.475 + * None.
1.476 + *
1.477 + *----------------------------------------------------------------------
1.478 + */
1.479 +
1.480 +static int
1.481 +CommonGetHandle(
1.482 + ClientData instanceData, /* The file state. */
1.483 + int direction, /* Which handle to retrieve? */
1.484 + ClientData *handlePtr)
1.485 +{
1.486 + if ((direction == TCL_READABLE) || (direction == TCL_WRITABLE)) {
1.487 + *handlePtr = (ClientData) ((FileState*)instanceData)->fileRef;
1.488 + return TCL_OK;
1.489 + }
1.490 + return TCL_ERROR;
1.491 +}
1.492 +
1.493 +/*
1.494 + *----------------------------------------------------------------------
1.495 + *
1.496 + * StdIOInput --
1.497 + *
1.498 + * Reads input from the IO channel into the buffer given. Returns
1.499 + * count of how many bytes were actually read, and an error indication.
1.500 + *
1.501 + * Results:
1.502 + * A count of how many bytes were read is returned and an error
1.503 + * indication is returned in an output argument.
1.504 + *
1.505 + * Side effects:
1.506 + * Reads input from the actual channel.
1.507 + *
1.508 + *----------------------------------------------------------------------
1.509 + */
1.510 +
1.511 +int
1.512 +StdIOInput(
1.513 + ClientData instanceData, /* Unused. */
1.514 + char *buf, /* Where to store data read. */
1.515 + int bufSize, /* How much space is available
1.516 + * in the buffer? */
1.517 + int *errorCode) /* Where to store error code. */
1.518 +{
1.519 + int fd;
1.520 + int bytesRead; /* How many bytes were read? */
1.521 +
1.522 + *errorCode = 0;
1.523 + errno = 0;
1.524 + fd = (int) ((FileState*)instanceData)->fileRef;
1.525 + bytesRead = read(fd, buf, (size_t) bufSize);
1.526 + if (bytesRead > -1) {
1.527 + return bytesRead;
1.528 + }
1.529 + *errorCode = errno;
1.530 + return -1;
1.531 +}
1.532 +
1.533 +/*
1.534 + *----------------------------------------------------------------------
1.535 + *
1.536 + * StdIOOutput--
1.537 + *
1.538 + * Writes the given output on the IO channel. Returns count of how
1.539 + * many characters were actually written, and an error indication.
1.540 + *
1.541 + * Results:
1.542 + * A count of how many characters were written is returned and an
1.543 + * error indication is returned in an output argument.
1.544 + *
1.545 + * Side effects:
1.546 + * Writes output on the actual channel.
1.547 + *
1.548 + *----------------------------------------------------------------------
1.549 + */
1.550 +
1.551 +static int
1.552 +StdIOOutput(
1.553 + ClientData instanceData, /* Unused. */
1.554 + CONST char *buf, /* The data buffer. */
1.555 + int toWrite, /* How many bytes to write? */
1.556 + int *errorCode) /* Where to store error code. */
1.557 +{
1.558 + int written;
1.559 + int fd;
1.560 +
1.561 + *errorCode = 0;
1.562 + errno = 0;
1.563 + fd = (int) ((FileState*)instanceData)->fileRef;
1.564 + written = write(fd, (void*)buf, (size_t) toWrite);
1.565 + if (written > -1) {
1.566 + return written;
1.567 + }
1.568 + *errorCode = errno;
1.569 + return -1;
1.570 +}
1.571 +
1.572 +/*
1.573 + *----------------------------------------------------------------------
1.574 + *
1.575 + * StdIOSeek --
1.576 + *
1.577 + * Seeks on an IO channel. Returns the new position.
1.578 + *
1.579 + * Results:
1.580 + * -1 if failed, the new position if successful. If failed, it
1.581 + * also sets *errorCodePtr to the error code.
1.582 + *
1.583 + * Side effects:
1.584 + * Moves the location at which the channel will be accessed in
1.585 + * future operations.
1.586 + *
1.587 + *----------------------------------------------------------------------
1.588 + */
1.589 +
1.590 +static int
1.591 +StdIOSeek(
1.592 + ClientData instanceData, /* Unused. */
1.593 + long offset, /* Offset to seek to. */
1.594 + int mode, /* Relative to where should we seek? */
1.595 + int *errorCodePtr) /* To store error code. */
1.596 +{
1.597 + int newLoc;
1.598 + int fd;
1.599 +
1.600 + *errorCodePtr = 0;
1.601 + fd = (int) ((FileState*)instanceData)->fileRef;
1.602 + newLoc = lseek(fd, offset, mode);
1.603 + if (newLoc > -1) {
1.604 + return newLoc;
1.605 + }
1.606 + *errorCodePtr = errno;
1.607 + return -1;
1.608 +}
1.609 +
1.610 +/*
1.611 + *----------------------------------------------------------------------
1.612 + *
1.613 + * Tcl_PidObjCmd --
1.614 + *
1.615 + * This procedure is invoked to process the "pid" Tcl command.
1.616 + * See the user documentation for details on what it does.
1.617 + *
1.618 + * Results:
1.619 + * A standard Tcl result.
1.620 + *
1.621 + * Side effects:
1.622 + * See the user documentation.
1.623 + *
1.624 + *----------------------------------------------------------------------
1.625 + */
1.626 +
1.627 + /* ARGSUSED */
1.628 +int
1.629 +Tcl_PidObjCmd(dummy, interp, objc, objv)
1.630 + ClientData dummy; /* Not used. */
1.631 + Tcl_Interp *interp; /* Current interpreter. */
1.632 + int objc; /* Number of arguments. */
1.633 + Tcl_Obj *CONST *objv; /* Argument strings. */
1.634 +{
1.635 + ProcessSerialNumber psn;
1.636 + char buf[20];
1.637 + Tcl_Channel chan;
1.638 + Tcl_Obj *resultPtr;
1.639 +
1.640 + if (objc > 2) {
1.641 + Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
1.642 + return TCL_ERROR;
1.643 + }
1.644 + if (objc == 1) {
1.645 + resultPtr = Tcl_GetObjResult(interp);
1.646 + GetCurrentProcess(&psn);
1.647 + sprintf(buf, "0x%08x%08x", psn.highLongOfPSN, psn.lowLongOfPSN);
1.648 + Tcl_SetStringObj(resultPtr, buf, -1);
1.649 + } else {
1.650 + chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]),
1.651 + NULL);
1.652 + if (chan == (Tcl_Channel) NULL) {
1.653 + return TCL_ERROR;
1.654 + }
1.655 + /*
1.656 + * We can't create pipelines on the Mac so
1.657 + * this will always return an empty list.
1.658 + */
1.659 + }
1.660 +
1.661 + return TCL_OK;
1.662 +}
1.663 +
1.664 +/*
1.665 + *----------------------------------------------------------------------
1.666 + *
1.667 + * TclpGetDefaultStdChannel --
1.668 + *
1.669 + * Constructs a channel for the specified standard OS handle.
1.670 + *
1.671 + * Results:
1.672 + * Returns the specified default standard channel, or NULL.
1.673 + *
1.674 + * Side effects:
1.675 + * May cause the creation of a standard channel and the underlying
1.676 + * file.
1.677 + *
1.678 + *----------------------------------------------------------------------
1.679 + */
1.680 +
1.681 +Tcl_Channel
1.682 +TclpGetDefaultStdChannel(
1.683 + int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1.684 +{
1.685 + Tcl_Channel channel = NULL;
1.686 + int fd = 0; /* Initializations needed to prevent */
1.687 + int mode = 0; /* compiler warning (used before set). */
1.688 + char *bufMode = NULL;
1.689 + char channelName[16 + TCL_INTEGER_SPACE];
1.690 + int channelPermissions;
1.691 + FileState *fileState;
1.692 +
1.693 + /*
1.694 + * If the channels were not created yet, create them now and
1.695 + * store them in the static variables.
1.696 + */
1.697 +
1.698 + switch (type) {
1.699 + case TCL_STDIN:
1.700 + fd = 0;
1.701 + channelPermissions = TCL_READABLE;
1.702 + bufMode = "line";
1.703 + break;
1.704 + case TCL_STDOUT:
1.705 + fd = 1;
1.706 + channelPermissions = TCL_WRITABLE;
1.707 + bufMode = "line";
1.708 + break;
1.709 + case TCL_STDERR:
1.710 + fd = 2;
1.711 + channelPermissions = TCL_WRITABLE;
1.712 + bufMode = "none";
1.713 + break;
1.714 + default:
1.715 + panic("TclGetDefaultStdChannel: Unexpected channel type");
1.716 + break;
1.717 + }
1.718 +
1.719 + sprintf(channelName, "console%d", (int) fd);
1.720 + fileState = (FileState *) ckalloc((unsigned) sizeof(FileState));
1.721 + channel = Tcl_CreateChannel(&consoleChannelType, channelName,
1.722 + (ClientData) fileState, channelPermissions);
1.723 + fileState->fileChan = channel;
1.724 + fileState->fileRef = fd;
1.725 +
1.726 + /*
1.727 + * Set up the normal channel options for stdio handles.
1.728 + */
1.729 +
1.730 + Tcl_SetChannelOption(NULL, channel, "-translation", "cr");
1.731 + Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
1.732 +
1.733 + return channel;
1.734 +}
1.735 +
1.736 +/*
1.737 + *----------------------------------------------------------------------
1.738 + *
1.739 + * TclpOpenFileChannel --
1.740 + *
1.741 + * Open a File based channel on MacOS systems.
1.742 + *
1.743 + * Results:
1.744 + * The new channel or NULL. If NULL, the output argument
1.745 + * errorCodePtr is set to a POSIX error.
1.746 + *
1.747 + * Side effects:
1.748 + * May open the channel and may cause creation of a file on the
1.749 + * file system.
1.750 + *
1.751 + *----------------------------------------------------------------------
1.752 + */
1.753 +
1.754 +Tcl_Channel
1.755 +TclpOpenFileChannel(
1.756 + Tcl_Interp *interp, /* Interpreter for error reporting;
1.757 + * can be NULL. */
1.758 + Tcl_Obj *pathPtr, /* Name of file to open. */
1.759 + int mode, /* POSIX open mode. */
1.760 + int permissions) /* If the open involves creating a
1.761 + * file, with what modes to create
1.762 + * it? */
1.763 +{
1.764 + Tcl_Channel chan;
1.765 + CONST char *native;
1.766 + int errorCode;
1.767 +
1.768 + native = Tcl_FSGetNativePath(pathPtr);
1.769 + if (native == NULL) {
1.770 + return NULL;
1.771 + }
1.772 + chan = OpenFileChannel(native, mode, permissions, &errorCode);
1.773 +
1.774 + if (chan == NULL) {
1.775 + Tcl_SetErrno(errorCode);
1.776 + if (interp != (Tcl_Interp *) NULL) {
1.777 + Tcl_AppendResult(interp, "couldn't open \"",
1.778 + Tcl_GetString(pathPtr), "\": ",
1.779 + Tcl_PosixError(interp), (char *) NULL);
1.780 + }
1.781 + return NULL;
1.782 + }
1.783 +
1.784 + return chan;
1.785 +}
1.786 +
1.787 +/*
1.788 + *----------------------------------------------------------------------
1.789 + *
1.790 + * OpenFileChannel--
1.791 + *
1.792 + * Opens a Macintosh file and creates a Tcl channel to control it.
1.793 + *
1.794 + * Results:
1.795 + * A Tcl channel.
1.796 + *
1.797 + * Side effects:
1.798 + * Will open a Macintosh file.
1.799 + *
1.800 + *----------------------------------------------------------------------
1.801 + */
1.802 +
1.803 +static Tcl_Channel
1.804 +OpenFileChannel(
1.805 + CONST char *fileName, /* Name of file to open (native). */
1.806 + int mode, /* Mode for opening file. */
1.807 + int permissions, /* If the open involves creating a
1.808 + * file, with what modes to create
1.809 + * it? */
1.810 + int *errorCodePtr) /* Where to store error code. */
1.811 +{
1.812 + int channelPermissions;
1.813 + Tcl_Channel chan;
1.814 + char macPermision;
1.815 + FSSpec fileSpec;
1.816 + OSErr err;
1.817 + short fileRef;
1.818 + FileState *fileState;
1.819 + char channelName[16 + TCL_INTEGER_SPACE];
1.820 + ThreadSpecificData *tsdPtr;
1.821 +
1.822 + tsdPtr = FileInit();
1.823 +
1.824 + /*
1.825 + * Note we use fsRdWrShPerm instead of fsRdWrPerm which allows shared
1.826 + * writes on a file. This isn't common on a mac but is common with
1.827 + * Windows and UNIX and the feature is used by Tcl.
1.828 + */
1.829 +
1.830 + switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1.831 + case O_RDWR:
1.832 + channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1.833 + macPermision = fsRdWrShPerm;
1.834 + break;
1.835 + case O_WRONLY:
1.836 + /*
1.837 + * Mac's fsRdPerm permission actually defaults to fsRdWrPerm because
1.838 + * the Mac OS doesn't realy support write only access. We explicitly
1.839 + * set the permission fsRdWrShPerm so that we can have shared write
1.840 + * access.
1.841 + */
1.842 + channelPermissions = TCL_WRITABLE;
1.843 + macPermision = fsRdWrShPerm;
1.844 + break;
1.845 + case O_RDONLY:
1.846 + default:
1.847 + channelPermissions = TCL_READABLE;
1.848 + macPermision = fsRdPerm;
1.849 + break;
1.850 + }
1.851 +
1.852 + err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec);
1.853 + if ((err != noErr) && (err != fnfErr)) {
1.854 + *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1.855 + Tcl_SetErrno(errno);
1.856 + return NULL;
1.857 + }
1.858 +
1.859 + if ((err == fnfErr) && (mode & O_CREAT)) {
1.860 + err = HCreate(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, TCL_FILE_CREATOR, 'TEXT');
1.861 + if (err != noErr) {
1.862 + *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1.863 + Tcl_SetErrno(errno);
1.864 + return NULL;
1.865 + }
1.866 + } else if ((mode & O_CREAT) && (mode & O_EXCL)) {
1.867 + *errorCodePtr = errno = EEXIST;
1.868 + Tcl_SetErrno(errno);
1.869 + return NULL;
1.870 + }
1.871 +
1.872 + err = HOpenDF(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, macPermision, &fileRef);
1.873 + if (err != noErr) {
1.874 + *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1.875 + Tcl_SetErrno(errno);
1.876 + return NULL;
1.877 + }
1.878 +
1.879 + if (mode & O_TRUNC) {
1.880 + SetEOF(fileRef, 0);
1.881 + }
1.882 +
1.883 + sprintf(channelName, "file%d", (int) fileRef);
1.884 + fileState = (FileState *) ckalloc((unsigned) sizeof(FileState));
1.885 + chan = Tcl_CreateChannel(&fileChannelType, channelName,
1.886 + (ClientData) fileState, channelPermissions);
1.887 + if (chan == (Tcl_Channel) NULL) {
1.888 + *errorCodePtr = errno = EFAULT;
1.889 + Tcl_SetErrno(errno);
1.890 + FSClose(fileRef);
1.891 + ckfree((char *) fileState);
1.892 + return NULL;
1.893 + }
1.894 +
1.895 + fileState->fileChan = chan;
1.896 + fileState->nextPtr = tsdPtr->firstFilePtr;
1.897 + tsdPtr->firstFilePtr = fileState;
1.898 + fileState->volumeRef = fileSpec.vRefNum;
1.899 + fileState->fileRef = fileRef;
1.900 + fileState->pending = 0;
1.901 + fileState->watchMask = 0;
1.902 + if (mode & O_APPEND) {
1.903 + fileState->appendMode = true;
1.904 + } else {
1.905 + fileState->appendMode = false;
1.906 + }
1.907 +
1.908 + if ((mode & O_APPEND) || (mode & O_APPEND)) {
1.909 + if (Tcl_Seek(chan, 0, SEEK_END) < 0) {
1.910 + *errorCodePtr = errno = EFAULT;
1.911 + Tcl_SetErrno(errno);
1.912 + Tcl_Close(NULL, chan);
1.913 + FSClose(fileRef);
1.914 + ckfree((char *) fileState);
1.915 + return NULL;
1.916 + }
1.917 + }
1.918 +
1.919 + return chan;
1.920 +}
1.921 +
1.922 +/*
1.923 + *----------------------------------------------------------------------
1.924 + *
1.925 + * Tcl_MakeFileChannel --
1.926 + *
1.927 + * Makes a Tcl_Channel from an existing OS level file handle.
1.928 + *
1.929 + * Results:
1.930 + * The Tcl_Channel created around the preexisting OS level file handle.
1.931 + *
1.932 + * Side effects:
1.933 + * None.
1.934 + *
1.935 + *----------------------------------------------------------------------
1.936 + */
1.937 +
1.938 +Tcl_Channel
1.939 +Tcl_MakeFileChannel(handle, mode)
1.940 + ClientData handle; /* OS level handle. */
1.941 + int mode; /* ORed combination of TCL_READABLE and
1.942 + * TCL_WRITABLE to indicate file mode. */
1.943 +{
1.944 + /*
1.945 + * Not implemented yet.
1.946 + */
1.947 +
1.948 + return NULL;
1.949 +}
1.950 +
1.951 +/*
1.952 + *----------------------------------------------------------------------
1.953 + *
1.954 + * FileBlockMode --
1.955 + *
1.956 + * Set blocking or non-blocking mode on channel. Macintosh files
1.957 + * can never really be set to blocking or non-blocking modes.
1.958 + * However, we don't generate an error - we just return success.
1.959 + *
1.960 + * Results:
1.961 + * 0 if successful, errno when failed.
1.962 + *
1.963 + * Side effects:
1.964 + * Sets the device into blocking or non-blocking mode.
1.965 + *
1.966 + *----------------------------------------------------------------------
1.967 + */
1.968 +
1.969 +static int
1.970 +FileBlockMode(
1.971 + ClientData instanceData, /* Unused. */
1.972 + int mode) /* The mode to set. */
1.973 +{
1.974 + return 0;
1.975 +}
1.976 +
1.977 +/*
1.978 + *----------------------------------------------------------------------
1.979 + *
1.980 + * FileClose --
1.981 + *
1.982 + * Closes the IO channel.
1.983 + *
1.984 + * Results:
1.985 + * 0 if successful, the value of errno if failed.
1.986 + *
1.987 + * Side effects:
1.988 + * Closes the physical channel
1.989 + *
1.990 + *----------------------------------------------------------------------
1.991 + */
1.992 +
1.993 +static int
1.994 +FileClose(
1.995 + ClientData instanceData, /* Unused. */
1.996 + Tcl_Interp *interp) /* Unused. */
1.997 +{
1.998 + FileState *fileState = (FileState *) instanceData;
1.999 + int errorCode = 0;
1.1000 + OSErr err;
1.1001 +
1.1002 + err = FSClose(fileState->fileRef);
1.1003 + FlushVol(NULL, fileState->volumeRef);
1.1004 + if (err != noErr) {
1.1005 + errorCode = errno = TclMacOSErrorToPosixError(err);
1.1006 + panic("error during file close");
1.1007 + }
1.1008 +
1.1009 + ckfree((char *) fileState);
1.1010 + Tcl_SetErrno(errorCode);
1.1011 + return errorCode;
1.1012 +}
1.1013 +
1.1014 +/*
1.1015 + *----------------------------------------------------------------------
1.1016 + *
1.1017 + * FileInput --
1.1018 + *
1.1019 + * Reads input from the IO channel into the buffer given. Returns
1.1020 + * count of how many bytes were actually read, and an error indication.
1.1021 + *
1.1022 + * Results:
1.1023 + * A count of how many bytes were read is returned and an error
1.1024 + * indication is returned in an output argument.
1.1025 + *
1.1026 + * Side effects:
1.1027 + * Reads input from the actual channel.
1.1028 + *
1.1029 + *----------------------------------------------------------------------
1.1030 + */
1.1031 +
1.1032 +int
1.1033 +FileInput(
1.1034 + ClientData instanceData, /* Unused. */
1.1035 + char *buffer, /* Where to store data read. */
1.1036 + int bufSize, /* How much space is available
1.1037 + * in the buffer? */
1.1038 + int *errorCodePtr) /* Where to store error code. */
1.1039 +{
1.1040 + FileState *fileState = (FileState *) instanceData;
1.1041 + OSErr err;
1.1042 + long length = bufSize;
1.1043 +
1.1044 + *errorCodePtr = 0;
1.1045 + errno = 0;
1.1046 + err = FSRead(fileState->fileRef, &length, buffer);
1.1047 + if ((err == noErr) || (err == eofErr)) {
1.1048 + return length;
1.1049 + } else {
1.1050 + switch (err) {
1.1051 + case ioErr:
1.1052 + *errorCodePtr = errno = EIO;
1.1053 + case afpAccessDenied:
1.1054 + *errorCodePtr = errno = EACCES;
1.1055 + default:
1.1056 + *errorCodePtr = errno = EINVAL;
1.1057 + }
1.1058 + return -1;
1.1059 + }
1.1060 + *errorCodePtr = errno;
1.1061 + return -1;
1.1062 +}
1.1063 +
1.1064 +/*
1.1065 + *----------------------------------------------------------------------
1.1066 + *
1.1067 + * FileOutput--
1.1068 + *
1.1069 + * Writes the given output on the IO channel. Returns count of how
1.1070 + * many characters were actually written, and an error indication.
1.1071 + *
1.1072 + * Results:
1.1073 + * A count of how many characters were written is returned and an
1.1074 + * error indication is returned in an output argument.
1.1075 + *
1.1076 + * Side effects:
1.1077 + * Writes output on the actual channel.
1.1078 + *
1.1079 + *----------------------------------------------------------------------
1.1080 + */
1.1081 +
1.1082 +static int
1.1083 +FileOutput(
1.1084 + ClientData instanceData, /* Unused. */
1.1085 + CONST char *buffer, /* The data buffer. */
1.1086 + int toWrite, /* How many bytes to write? */
1.1087 + int *errorCodePtr) /* Where to store error code. */
1.1088 +{
1.1089 + FileState *fileState = (FileState *) instanceData;
1.1090 + long length = toWrite;
1.1091 + OSErr err;
1.1092 +
1.1093 + *errorCodePtr = 0;
1.1094 + errno = 0;
1.1095 +
1.1096 + if (fileState->appendMode == true) {
1.1097 + FileSeek(instanceData, 0, SEEK_END, errorCodePtr);
1.1098 + *errorCodePtr = 0;
1.1099 + }
1.1100 +
1.1101 + err = FSWrite(fileState->fileRef, &length, buffer);
1.1102 + if (err == noErr) {
1.1103 + err = FlushFile(fileState->fileRef);
1.1104 + } else {
1.1105 + *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1.1106 + return -1;
1.1107 + }
1.1108 + return length;
1.1109 +}
1.1110 +
1.1111 +/*
1.1112 + *----------------------------------------------------------------------
1.1113 + *
1.1114 + * FileSeek --
1.1115 + *
1.1116 + * Seeks on an IO channel. Returns the new position.
1.1117 + *
1.1118 + * Results:
1.1119 + * -1 if failed, the new position if successful. If failed, it
1.1120 + * also sets *errorCodePtr to the error code.
1.1121 + *
1.1122 + * Side effects:
1.1123 + * Moves the location at which the channel will be accessed in
1.1124 + * future operations.
1.1125 + *
1.1126 + *----------------------------------------------------------------------
1.1127 + */
1.1128 +
1.1129 +static int
1.1130 +FileSeek(
1.1131 + ClientData instanceData, /* Unused. */
1.1132 + long offset, /* Offset to seek to. */
1.1133 + int mode, /* Relative to where should we seek? */
1.1134 + int *errorCodePtr) /* To store error code. */
1.1135 +{
1.1136 + FileState *fileState = (FileState *) instanceData;
1.1137 + IOParam pb;
1.1138 + OSErr err;
1.1139 +
1.1140 + *errorCodePtr = 0;
1.1141 + pb.ioCompletion = NULL;
1.1142 + pb.ioRefNum = fileState->fileRef;
1.1143 + if (mode == SEEK_SET) {
1.1144 + pb.ioPosMode = fsFromStart;
1.1145 + } else if (mode == SEEK_END) {
1.1146 + pb.ioPosMode = fsFromLEOF;
1.1147 + } else if (mode == SEEK_CUR) {
1.1148 + err = PBGetFPosSync((ParmBlkPtr) &pb);
1.1149 + if (pb.ioResult == noErr) {
1.1150 + if (offset == 0) {
1.1151 + return pb.ioPosOffset;
1.1152 + }
1.1153 + offset += pb.ioPosOffset;
1.1154 + }
1.1155 + pb.ioPosMode = fsFromStart;
1.1156 + }
1.1157 + pb.ioPosOffset = offset;
1.1158 + err = PBSetFPosSync((ParmBlkPtr) &pb);
1.1159 + if (pb.ioResult == noErr){
1.1160 + return pb.ioPosOffset;
1.1161 + } else if (pb.ioResult == eofErr) {
1.1162 + long currentEOF, newEOF;
1.1163 + long buffer, i, length;
1.1164 +
1.1165 + err = PBGetEOFSync((ParmBlkPtr) &pb);
1.1166 + currentEOF = (long) pb.ioMisc;
1.1167 + if (mode == SEEK_SET) {
1.1168 + newEOF = offset;
1.1169 + } else if (mode == SEEK_END) {
1.1170 + newEOF = offset + currentEOF;
1.1171 + } else if (mode == SEEK_CUR) {
1.1172 + err = PBGetFPosSync((ParmBlkPtr) &pb);
1.1173 + newEOF = offset + pb.ioPosOffset;
1.1174 + }
1.1175 +
1.1176 + /*
1.1177 + * Write 0's to the new EOF.
1.1178 + */
1.1179 + pb.ioPosOffset = 0;
1.1180 + pb.ioPosMode = fsFromLEOF;
1.1181 + err = PBGetFPosSync((ParmBlkPtr) &pb);
1.1182 + length = 1;
1.1183 + buffer = 0;
1.1184 + for (i = 0; i < (newEOF - currentEOF); i++) {
1.1185 + err = FSWrite(fileState->fileRef, &length, &buffer);
1.1186 + }
1.1187 + err = PBGetFPosSync((ParmBlkPtr) &pb);
1.1188 + if (pb.ioResult == noErr){
1.1189 + return pb.ioPosOffset;
1.1190 + }
1.1191 + }
1.1192 + *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1.1193 + return -1;
1.1194 +}
1.1195 +
1.1196 +/*
1.1197 + *----------------------------------------------------------------------
1.1198 + *
1.1199 + * CommonWatch --
1.1200 + *
1.1201 + * Initialize the notifier to watch handles from this channel.
1.1202 + *
1.1203 + * Results:
1.1204 + * None.
1.1205 + *
1.1206 + * Side effects:
1.1207 + * None.
1.1208 + *
1.1209 + *----------------------------------------------------------------------
1.1210 + */
1.1211 +
1.1212 +static void
1.1213 +CommonWatch(
1.1214 + ClientData instanceData, /* The file state. */
1.1215 + int mask) /* Events of interest; an OR-ed
1.1216 + * combination of TCL_READABLE,
1.1217 + * TCL_WRITABLE and TCL_EXCEPTION. */
1.1218 +{
1.1219 + FileState *infoPtr = (FileState *) instanceData;
1.1220 + Tcl_Time blockTime = { 0, 0 };
1.1221 +
1.1222 + infoPtr->watchMask = mask;
1.1223 + if (infoPtr->watchMask) {
1.1224 + Tcl_SetMaxBlockTime(&blockTime);
1.1225 + }
1.1226 +}
1.1227 +
1.1228 +/*
1.1229 + *----------------------------------------------------------------------
1.1230 + *
1.1231 + * FileThreadActionProc --
1.1232 + *
1.1233 + * Insert or remove any thread local refs to this channel.
1.1234 + *
1.1235 + * Results:
1.1236 + * None.
1.1237 + *
1.1238 + * Side effects:
1.1239 + * Changes thread local list of valid channels.
1.1240 + *
1.1241 + *----------------------------------------------------------------------
1.1242 + */
1.1243 +
1.1244 +static void
1.1245 +FileThreadActionProc (instanceData, action)
1.1246 + ClientData instanceData;
1.1247 + int action;
1.1248 +{
1.1249 + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1.1250 + FileState *infoPtr = (FileState *) instanceData;
1.1251 +
1.1252 + if (action == TCL_CHANNEL_THREAD_INSERT) {
1.1253 + infoPtr->nextPtr = tsdPtr->firstFilePtr;
1.1254 + tsdPtr->firstFilePtr = infoPtr;
1.1255 + } else {
1.1256 + FileState **nextPtrPtr;
1.1257 + int removed = 0;
1.1258 +
1.1259 + for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
1.1260 + nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
1.1261 + if ((*nextPtrPtr) == infoPtr) {
1.1262 + (*nextPtrPtr) = infoPtr->nextPtr;
1.1263 + removed = 1;
1.1264 + break;
1.1265 + }
1.1266 + }
1.1267 +
1.1268 + /*
1.1269 + * This could happen if the channel was created in one thread
1.1270 + * and then moved to another without updating the thread
1.1271 + * local data in each thread.
1.1272 + */
1.1273 +
1.1274 + if (!removed) {
1.1275 + panic("file info ptr not on thread channel list");
1.1276 + }
1.1277 + }
1.1278 +}