tclMacChan.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:33k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclMacChan.c
  3.  *
  4.  * Channel drivers for Macintosh channels for the
  5.  * console fds.
  6.  *
  7.  * Copyright (c) 1996-1997 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * RCS: @(#) $Id: tclMacChan.c,v 1.21.2.1 2005/01/27 22:53:34 andreas_kupries Exp $
  13.  */
  14. #include "tclInt.h"
  15. #include "tclPort.h"
  16. #include "tclMacInt.h"
  17. #include <Aliases.h>
  18. #include <Errors.h>
  19. #include <Files.h>
  20. #include <Gestalt.h>
  21. #include <Processes.h>
  22. #include <Strings.h>
  23. #include <FSpCompat.h>
  24. #include <MoreFiles.h>
  25. #include <MoreFilesExtras.h>
  26. #include "tclIO.h"
  27. #ifdef __MSL__
  28. #include <unix.mac.h>
  29. #define TCL_FILE_CREATOR (__getcreator(0))
  30. #else
  31. #define TCL_FILE_CREATOR 'MPW '
  32. #endif
  33. /*
  34.  * This structure describes per-instance state of a 
  35.  * macintosh file based channel.
  36.  */
  37. typedef struct FileState {
  38.     short fileRef; /* Macintosh file reference number. */
  39.     Tcl_Channel fileChan; /* Pointer to the channel for this file. */
  40.     int watchMask; /* OR'ed set of flags indicating which events
  41.       * are being watched. */
  42.     int appendMode; /* Flag to tell if in O_APPEND mode or not. */
  43.     int volumeRef; /* Flag to tell if in O_APPEND mode or not. */
  44.     int pending; /* 1 if message is pending on queue. */
  45.     struct FileState *nextPtr; /* Pointer to next registered file. */
  46. } FileState;
  47. typedef struct ThreadSpecificData {
  48.     int initialized; /* True after the thread initializes */
  49.     FileState *firstFilePtr; /* the head of the list of files managed
  50.  * that are being watched for file events. */
  51.     Tcl_Channel stdinChannel;
  52.     Tcl_Channel stdoutChannel; /* Note - these seem unused */
  53.     Tcl_Channel stderrChannel;
  54. } ThreadSpecificData;
  55. static Tcl_ThreadDataKey dataKey;
  56. /*
  57.  * The following structure is what is added to the Tcl event queue when
  58.  * file events are generated.
  59.  */
  60. typedef struct FileEvent {
  61.     Tcl_Event header; /* Information that is standard for
  62.  * all events. */
  63.     FileState *infoPtr; /* Pointer to file info structure.  Note
  64.  * that we still have to verify that the
  65.  * file exists before dereferencing this
  66.  * pointer. */
  67. } FileEvent;
  68. /*
  69.  * Static routines for this file:
  70.  */
  71. static int CommonGetHandle _ANSI_ARGS_((ClientData instanceData,
  72.             int direction, ClientData *handlePtr));
  73. static void CommonWatch _ANSI_ARGS_((ClientData instanceData,
  74.             int mask));
  75. static int FileBlockMode _ANSI_ARGS_((ClientData instanceData,
  76.     int mode));
  77. static void FileChannelExitHandler _ANSI_ARGS_((
  78.             ClientData clientData));
  79. static void FileCheckProc _ANSI_ARGS_((ClientData clientData,
  80.     int flags));
  81. static int FileClose _ANSI_ARGS_((ClientData instanceData,
  82.     Tcl_Interp *interp));
  83. static int FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  84.     int flags));
  85. static ThreadSpecificData *FileInit _ANSI_ARGS_((void));
  86. static int FileInput _ANSI_ARGS_((ClientData instanceData,
  87.     char *buf, int toRead, int *errorCode));
  88. static int FileOutput _ANSI_ARGS_((ClientData instanceData,
  89.     CONST char *buf, int toWrite, int *errorCode));
  90. static int FileSeek _ANSI_ARGS_((ClientData instanceData,
  91.     long offset, int mode, int *errorCode));
  92. static void FileSetupProc _ANSI_ARGS_((ClientData clientData,
  93.     int flags));
  94. static void             FileThreadActionProc _ANSI_ARGS_ ((
  95.    ClientData instanceData, int action));
  96. static Tcl_Channel OpenFileChannel _ANSI_ARGS_((CONST char *fileName, 
  97.     int mode, int permissions, int *errorCodePtr));
  98. static int StdIOBlockMode _ANSI_ARGS_((ClientData instanceData,
  99.     int mode));
  100. static int StdIOClose _ANSI_ARGS_((ClientData instanceData,
  101.     Tcl_Interp *interp));
  102. static int StdIOInput _ANSI_ARGS_((ClientData instanceData,
  103.     char *buf, int toRead, int *errorCode));
  104. static int StdIOOutput _ANSI_ARGS_((ClientData instanceData,
  105.     CONST char *buf, int toWrite, int *errorCode));
  106. static int StdIOSeek _ANSI_ARGS_((ClientData instanceData,
  107.     long offset, int mode, int *errorCode));
  108. static int StdReady _ANSI_ARGS_((ClientData instanceData,
  109.             int mask));
  110. /*
  111.  * This structure describes the channel type structure for file based IO:
  112.  */
  113. static Tcl_ChannelType consoleChannelType = {
  114.     "file", /* Type name. */
  115.     TCL_CHANNEL_VERSION_4, /* v4 channel */
  116.     StdIOClose, /* Close proc. */
  117.     StdIOInput, /* Input proc. */
  118.     StdIOOutput, /* Output proc. */
  119.     StdIOSeek, /* Seek proc. */
  120.     NULL, /* Set option proc. */
  121.     NULL, /* Get option proc. */
  122.     CommonWatch, /* Initialize notifier. */
  123.     CommonGetHandle /* Get OS handles out of channel. */
  124.     NULL, /* close2proc. */
  125.     StdIOBlockMode, /* Set blocking/nonblocking mode.*/
  126.     NULL, /* flush proc. */
  127.     NULL, /* handler proc. */
  128.     NULL, /* wide seek proc. */
  129.     NULL,         /* thread actions */
  130. };
  131. /*
  132.  * This variable describes the channel type structure for file based IO.
  133.  */
  134. static Tcl_ChannelType fileChannelType = {
  135.     "file", /* Type name. */
  136.     TCL_CHANNEL_VERSION_4, /* v4 channel */
  137.     FileClose, /* Close proc. */
  138.     FileInput, /* Input proc. */
  139.     FileOutput, /* Output proc. */
  140.     FileSeek, /* Seek proc. */
  141.     NULL, /* Set option proc. */
  142.     NULL, /* Get option proc. */
  143.     CommonWatch, /* Initialize notifier. */
  144.     CommonGetHandle /* Get OS handles out of channel. */
  145.     NULL, /* close2proc. */
  146.     FileBlockMode, /* Set blocking/nonblocking mode.*/
  147.     NULL, /* flush proc. */
  148.     NULL, /* handler proc. */
  149.     NULL, /* wide seek proc. */
  150.     FileThreadActionProc,       /* thread actions */
  151. };
  152. /*
  153.  * Hack to allow Mac Tk to override the TclGetStdChannels function.
  154.  */
  155.  
  156. typedef void (*TclGetStdChannelsProc) _ANSI_ARGS_((Tcl_Channel *stdinPtr,
  157. Tcl_Channel *stdoutPtr, Tcl_Channel *stderrPtr));
  158. TclGetStdChannelsProc getStdChannelsProc = NULL;
  159. /*
  160.  *----------------------------------------------------------------------
  161.  *
  162.  * FileInit --
  163.  *
  164.  * This function initializes the file channel event source.
  165.  *
  166.  * Results:
  167.  * None.
  168.  *
  169.  * Side effects:
  170.  * Creates a new event source.
  171.  *
  172.  *----------------------------------------------------------------------
  173.  */
  174. static ThreadSpecificData *
  175. FileInit()
  176. {
  177.     ThreadSpecificData *tsdPtr =
  178. (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
  179.     if (tsdPtr == NULL) {
  180. tsdPtr = TCL_TSD_INIT(&dataKey);
  181. tsdPtr->firstFilePtr = NULL;
  182. Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
  183. Tcl_CreateThreadExitHandler(FileChannelExitHandler, NULL);
  184.     }
  185.     return tsdPtr;
  186. }
  187. /*
  188.  *----------------------------------------------------------------------
  189.  *
  190.  * FileChannelExitHandler --
  191.  *
  192.  * This function is called to cleanup the channel driver before
  193.  * Tcl is unloaded.
  194.  *
  195.  * Results:
  196.  * None.
  197.  *
  198.  * Side effects:
  199.  * Destroys the communication window.
  200.  *
  201.  *----------------------------------------------------------------------
  202.  */
  203. static void
  204. FileChannelExitHandler(
  205.     ClientData clientData) /* Old window proc */
  206. {
  207.     Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
  208. }
  209. /*
  210.  *----------------------------------------------------------------------
  211.  *
  212.  * FileSetupProc --
  213.  *
  214.  * This procedure is invoked before Tcl_DoOneEvent blocks waiting
  215.  * for an event.
  216.  *
  217.  * Results:
  218.  * None.
  219.  *
  220.  * Side effects:
  221.  * Adjusts the block time if needed.
  222.  *
  223.  *----------------------------------------------------------------------
  224.  */
  225. void
  226. FileSetupProc(
  227.     ClientData data, /* Not used. */
  228.     int flags) /* Event flags as passed to Tcl_DoOneEvent. */
  229. {
  230.     FileState *infoPtr;
  231.     Tcl_Time blockTime = { 0, 0 };
  232.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  233.     if (!(flags & TCL_FILE_EVENTS)) {
  234. return;
  235.     }
  236.     
  237.     /*
  238.      * Check to see if there is a ready file.  If so, poll.
  239.      */
  240.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 
  241.     infoPtr = infoPtr->nextPtr) {
  242. if (infoPtr->watchMask) {
  243.     Tcl_SetMaxBlockTime(&blockTime);
  244.     break;
  245. }
  246.     }
  247. }
  248. /*
  249.  *----------------------------------------------------------------------
  250.  *
  251.  * FileCheckProc --
  252.  *
  253.  * This procedure is called by Tcl_DoOneEvent to check the file
  254.  * event source for events. 
  255.  *
  256.  * Results:
  257.  * None.
  258.  *
  259.  * Side effects:
  260.  * May queue an event.
  261.  *
  262.  *----------------------------------------------------------------------
  263.  */
  264. static void
  265. FileCheckProc(
  266.     ClientData data, /* Not used. */
  267.     int flags) /* Event flags as passed to Tcl_DoOneEvent. */
  268. {
  269.     FileEvent *evPtr;
  270.     FileState *infoPtr;
  271.     int sentMsg = 0;
  272.     Tcl_Time blockTime = { 0, 0 };
  273.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  274.     if (!(flags & TCL_FILE_EVENTS)) {
  275. return;
  276.     }
  277.     
  278.     /*
  279.      * Queue events for any ready files that don't already have events
  280.      * queued (caused by persistent states that won't generate WinSock
  281.      * events).
  282.      */
  283.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 
  284.     infoPtr = infoPtr->nextPtr) {
  285. if (infoPtr->watchMask && !infoPtr->pending) {
  286.     infoPtr->pending = 1;
  287.     evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
  288.     evPtr->header.proc = FileEventProc;
  289.     evPtr->infoPtr = infoPtr;
  290.     Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
  291. }
  292.     }
  293. }
  294. /*----------------------------------------------------------------------
  295.  *
  296.  * FileEventProc --
  297.  *
  298.  * This function is invoked by Tcl_ServiceEvent when a file event
  299.  * reaches the front of the event queue.  This procedure invokes
  300.  * Tcl_NotifyChannel on the file.
  301.  *
  302.  * Results:
  303.  * Returns 1 if the event was handled, meaning it should be removed
  304.  * from the queue.  Returns 0 if the event was not handled, meaning
  305.  * it should stay on the queue.  The only time the event isn't
  306.  * handled is if the TCL_FILE_EVENTS flag bit isn't set.
  307.  *
  308.  * Side effects:
  309.  * Whatever the notifier callback does.
  310.  *
  311.  *----------------------------------------------------------------------
  312.  */
  313. static int
  314. FileEventProc(
  315.     Tcl_Event *evPtr, /* Event to service. */
  316.     int flags) /* Flags that indicate what events to
  317.  * handle, such as TCL_FILE_EVENTS. */
  318. {
  319.     FileEvent *fileEvPtr = (FileEvent *)evPtr;
  320.     FileState *infoPtr;
  321.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  322.     if (!(flags & TCL_FILE_EVENTS)) {
  323. return 0;
  324.     }
  325.     /*
  326.      * Search through the list of watched files for the one whose handle
  327.      * matches the event.  We do this rather than simply dereferencing
  328.      * the handle in the event so that files can be deleted while the
  329.      * event is in the queue.
  330.      */
  331.     for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; 
  332.     infoPtr = infoPtr->nextPtr) {
  333. if (fileEvPtr->infoPtr == infoPtr) {
  334.     infoPtr->pending = 0;
  335.     Tcl_NotifyChannel(infoPtr->fileChan, infoPtr->watchMask);
  336.     break;
  337. }
  338.     }
  339.     return 1;
  340. }
  341. /*
  342.  *----------------------------------------------------------------------
  343.  *
  344.  * StdIOBlockMode --
  345.  *
  346.  * Set blocking or non-blocking mode on channel.
  347.  *
  348.  * Results:
  349.  * 0 if successful, errno when failed.
  350.  *
  351.  * Side effects:
  352.  * Sets the device into blocking or non-blocking mode.
  353.  *
  354.  *----------------------------------------------------------------------
  355.  */
  356. static int
  357. StdIOBlockMode(
  358.     ClientData instanceData, /* Unused. */
  359.     int mode) /* The mode to set. */
  360. {
  361.     /*
  362.      * Do not allow putting stdin, stdout or stderr into nonblocking mode.
  363.      */
  364.     
  365.     if (mode == TCL_MODE_NONBLOCKING) {
  366. return EFAULT;
  367.     }
  368.     
  369.     return 0;
  370. }
  371. /*
  372.  *----------------------------------------------------------------------
  373.  *
  374.  * StdIOClose --
  375.  *
  376.  * Closes the IO channel.
  377.  *
  378.  * Results:
  379.  * 0 if successful, the value of errno if failed.
  380.  *
  381.  * Side effects:
  382.  * Closes the physical channel
  383.  *
  384.  *----------------------------------------------------------------------
  385.  */
  386. static int
  387. StdIOClose(
  388.     ClientData instanceData, /* Unused. */
  389.     Tcl_Interp *interp) /* Unused. */
  390. {
  391.     int fd, errorCode = 0;
  392.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  393.     /*
  394.      * Invalidate the stdio cache if necessary.  Note that we assume that
  395.      * the stdio file and channel pointers will become invalid at the same
  396.      * time.
  397.      * Do not close standard channels while in thread-exit.
  398.      */
  399.     fd = (int) ((FileState*)instanceData)->fileRef;
  400.     if (!TclInThreadExit()) {
  401. if (fd == 0) {
  402.     tsdPtr->stdinChannel = NULL;
  403. } else if (fd == 1) {
  404.     tsdPtr->stdoutChannel = NULL;
  405. } else if (fd == 2) {
  406.     tsdPtr->stderrChannel = NULL;
  407. } else {
  408.     panic("recieved invalid std file");
  409. }
  410.     
  411. if (close(fd) < 0) {
  412.     errorCode = errno;
  413. }
  414.     }
  415.     return errorCode;
  416. }
  417. /*
  418.  *----------------------------------------------------------------------
  419.  *
  420.  * CommonGetHandle --
  421.  *
  422.  * Called from Tcl_GetChannelHandle to retrieve OS handles from inside
  423.  * a file based channel.
  424.  *
  425.  * Results:
  426.  * The appropriate handle or NULL if not present. 
  427.  *
  428.  * Side effects:
  429.  * None.
  430.  *
  431.  *----------------------------------------------------------------------
  432.  */
  433. static int
  434. CommonGetHandle(
  435.     ClientData instanceData, /* The file state. */
  436.     int direction, /* Which handle to retrieve? */
  437.     ClientData *handlePtr)
  438. {
  439.     if ((direction == TCL_READABLE) || (direction == TCL_WRITABLE)) {
  440. *handlePtr = (ClientData) ((FileState*)instanceData)->fileRef;
  441. return TCL_OK;
  442.     }
  443.     return TCL_ERROR;
  444. }
  445. /*
  446.  *----------------------------------------------------------------------
  447.  *
  448.  * StdIOInput --
  449.  *
  450.  * Reads input from the IO channel into the buffer given. Returns
  451.  * count of how many bytes were actually read, and an error indication.
  452.  *
  453.  * Results:
  454.  * A count of how many bytes were read is returned and an error
  455.  * indication is returned in an output argument.
  456.  *
  457.  * Side effects:
  458.  * Reads input from the actual channel.
  459.  *
  460.  *----------------------------------------------------------------------
  461.  */
  462. int
  463. StdIOInput(
  464.     ClientData instanceData, /* Unused. */
  465.     char *buf, /* Where to store data read. */
  466.     int bufSize, /* How much space is available
  467.                                          * in the buffer? */
  468.     int *errorCode) /* Where to store error code. */
  469. {
  470.     int fd;
  471.     int bytesRead; /* How many bytes were read? */
  472.     *errorCode = 0;
  473.     errno = 0;
  474.     fd = (int) ((FileState*)instanceData)->fileRef;
  475.     bytesRead = read(fd, buf, (size_t) bufSize);
  476.     if (bytesRead > -1) {
  477.         return bytesRead;
  478.     }
  479.     *errorCode = errno;
  480.     return -1;
  481. }
  482. /*
  483.  *----------------------------------------------------------------------
  484.  *
  485.  * StdIOOutput--
  486.  *
  487.  * Writes the given output on the IO channel. Returns count of how
  488.  * many characters were actually written, and an error indication.
  489.  *
  490.  * Results:
  491.  * A count of how many characters were written is returned and an
  492.  * error indication is returned in an output argument.
  493.  *
  494.  * Side effects:
  495.  * Writes output on the actual channel.
  496.  *
  497.  *----------------------------------------------------------------------
  498.  */
  499. static int
  500. StdIOOutput(
  501.     ClientData instanceData, /* Unused. */
  502.     CONST char *buf, /* The data buffer. */
  503.     int toWrite, /* How many bytes to write? */
  504.     int *errorCode) /* Where to store error code. */
  505. {
  506.     int written;
  507.     int fd;
  508.     *errorCode = 0;
  509.     errno = 0;
  510.     fd = (int) ((FileState*)instanceData)->fileRef;
  511.     written = write(fd, (void*)buf, (size_t) toWrite);
  512.     if (written > -1) {
  513.         return written;
  514.     }
  515.     *errorCode = errno;
  516.     return -1;
  517. }
  518. /*
  519.  *----------------------------------------------------------------------
  520.  *
  521.  * StdIOSeek --
  522.  *
  523.  * Seeks on an IO channel. Returns the new position.
  524.  *
  525.  * Results:
  526.  * -1 if failed, the new position if successful. If failed, it
  527.  * also sets *errorCodePtr to the error code.
  528.  *
  529.  * Side effects:
  530.  * Moves the location at which the channel will be accessed in
  531.  * future operations.
  532.  *
  533.  *----------------------------------------------------------------------
  534.  */
  535. static int
  536. StdIOSeek(
  537.     ClientData instanceData, /* Unused. */
  538.     long offset, /* Offset to seek to. */
  539.     int mode, /* Relative to where should we seek? */
  540.     int *errorCodePtr) /* To store error code. */
  541. {
  542.     int newLoc;
  543.     int fd;
  544.     *errorCodePtr = 0;
  545.     fd = (int) ((FileState*)instanceData)->fileRef;
  546.     newLoc = lseek(fd, offset, mode);
  547.     if (newLoc > -1) {
  548.         return newLoc;
  549.     }
  550.     *errorCodePtr = errno;
  551.     return -1;
  552. }
  553. /*
  554.  *----------------------------------------------------------------------
  555.  *
  556.  * Tcl_PidObjCmd --
  557.  *
  558.  *      This procedure is invoked to process the "pid" Tcl command.
  559.  *      See the user documentation for details on what it does.
  560.  *
  561.  * Results:
  562.  *      A standard Tcl result.
  563.  *
  564.  * Side effects:
  565.  *      See the user documentation.
  566.  *
  567.  *----------------------------------------------------------------------
  568.  */
  569.         /* ARGSUSED */
  570. int
  571. Tcl_PidObjCmd(dummy, interp, objc, objv)
  572.     ClientData dummy;           /* Not used. */
  573.     Tcl_Interp *interp;         /* Current interpreter. */
  574.     int objc;                   /* Number of arguments. */
  575.     Tcl_Obj *CONST *objv;       /* Argument strings. */
  576. {
  577.     ProcessSerialNumber psn;
  578.     char buf[20]; 
  579.     Tcl_Channel chan;
  580.     Tcl_Obj *resultPtr;
  581.     if (objc > 2) {
  582.         Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
  583.         return TCL_ERROR;
  584.     }
  585.     if (objc == 1) {
  586.         resultPtr = Tcl_GetObjResult(interp);
  587. GetCurrentProcess(&psn);
  588. sprintf(buf, "0x%08x%08x", psn.highLongOfPSN, psn.lowLongOfPSN);
  589.         Tcl_SetStringObj(resultPtr, buf, -1);
  590.     } else {
  591.         chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]),
  592.                 NULL);
  593.         if (chan == (Tcl_Channel) NULL) {
  594.             return TCL_ERROR;
  595.         } 
  596. /*
  597.  * We can't create pipelines on the Mac so
  598.  * this will always return an empty list.
  599.  */
  600.     }
  601.     
  602.     return TCL_OK;
  603. }
  604. /*
  605.  *----------------------------------------------------------------------
  606.  *
  607.  * TclpGetDefaultStdChannel --
  608.  *
  609.  * Constructs a channel for the specified standard OS handle.
  610.  *
  611.  * Results:
  612.  * Returns the specified default standard channel, or NULL.
  613.  *
  614.  * Side effects:
  615.  * May cause the creation of a standard channel and the underlying
  616.  * file.
  617.  *
  618.  *----------------------------------------------------------------------
  619.  */
  620. Tcl_Channel
  621. TclpGetDefaultStdChannel(
  622.     int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
  623. {
  624.     Tcl_Channel channel = NULL;
  625.     int fd = 0; /* Initializations needed to prevent */
  626.     int mode = 0; /* compiler warning (used before set). */
  627.     char *bufMode = NULL;
  628.     char channelName[16 + TCL_INTEGER_SPACE];
  629.     int channelPermissions;
  630.     FileState *fileState;
  631.     /*
  632.      * If the channels were not created yet, create them now and
  633.      * store them in the static variables.
  634.      */
  635.     switch (type) {
  636. case TCL_STDIN:
  637.     fd = 0;
  638.     channelPermissions = TCL_READABLE;
  639.     bufMode = "line";
  640.     break;
  641. case TCL_STDOUT:
  642.     fd = 1;
  643.     channelPermissions = TCL_WRITABLE;
  644.     bufMode = "line";
  645.     break;
  646. case TCL_STDERR:
  647.     fd = 2;
  648.     channelPermissions = TCL_WRITABLE;
  649.     bufMode = "none";
  650.     break;
  651. default:
  652.     panic("TclGetDefaultStdChannel: Unexpected channel type");
  653.     break;
  654.     }
  655.     sprintf(channelName, "console%d", (int) fd);
  656.     fileState = (FileState *) ckalloc((unsigned) sizeof(FileState));
  657.     channel = Tcl_CreateChannel(&consoleChannelType, channelName,
  658.     (ClientData) fileState, channelPermissions);
  659.     fileState->fileChan = channel;
  660.     fileState->fileRef = fd;
  661.     /*
  662.      * Set up the normal channel options for stdio handles.
  663.      */
  664.     Tcl_SetChannelOption(NULL, channel, "-translation", "cr");
  665.     Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
  666.     
  667.     return channel;
  668. }
  669. /*
  670.  *----------------------------------------------------------------------
  671.  *
  672.  * TclpOpenFileChannel --
  673.  *
  674.  * Open a File based channel on MacOS systems.
  675.  *
  676.  * Results:
  677.  * The new channel or NULL. If NULL, the output argument
  678.  * errorCodePtr is set to a POSIX error.
  679.  *
  680.  * Side effects:
  681.  * May open the channel and may cause creation of a file on the
  682.  * file system.
  683.  *
  684.  *----------------------------------------------------------------------
  685.  */
  686. Tcl_Channel
  687. TclpOpenFileChannel(
  688.     Tcl_Interp *interp, /* Interpreter for error reporting;
  689.                                          * can be NULL. */
  690.     Tcl_Obj *pathPtr, /* Name of file to open. */
  691.     int mode, /* POSIX open mode. */
  692.     int permissions) /* If the open involves creating a
  693.                                          * file, with what modes to create
  694.                                          * it? */
  695. {
  696.     Tcl_Channel chan;
  697.     CONST char *native;
  698.     int errorCode;
  699.     
  700.     native = Tcl_FSGetNativePath(pathPtr);
  701.     if (native == NULL) {
  702. return NULL;
  703.     }
  704.     chan = OpenFileChannel(native, mode, permissions, &errorCode);
  705.     if (chan == NULL) {
  706. Tcl_SetErrno(errorCode);
  707. if (interp != (Tcl_Interp *) NULL) {
  708.             Tcl_AppendResult(interp, "couldn't open "", 
  709.      Tcl_GetString(pathPtr), "": ",
  710.      Tcl_PosixError(interp), (char *) NULL);
  711.         }
  712. return NULL;
  713.     }
  714.     
  715.     return chan;
  716. }
  717. /*
  718.  *----------------------------------------------------------------------
  719.  *
  720.  * OpenFileChannel--
  721.  *
  722.  * Opens a Macintosh file and creates a Tcl channel to control it.
  723.  *
  724.  * Results:
  725.  * A Tcl channel.
  726.  *
  727.  * Side effects:
  728.  * Will open a Macintosh file.
  729.  *
  730.  *----------------------------------------------------------------------
  731.  */
  732. static Tcl_Channel
  733. OpenFileChannel(
  734.     CONST char *fileName, /* Name of file to open (native). */
  735.     int mode, /* Mode for opening file. */
  736.     int permissions, /* If the open involves creating a
  737.                                          * file, with what modes to create
  738.                                          * it? */
  739.     int *errorCodePtr) /* Where to store error code. */
  740. {
  741.     int channelPermissions;
  742.     Tcl_Channel chan;
  743.     char macPermision;
  744.     FSSpec fileSpec;
  745.     OSErr err;
  746.     short fileRef;
  747.     FileState *fileState;
  748.     char channelName[16 + TCL_INTEGER_SPACE];
  749.     ThreadSpecificData *tsdPtr;
  750.     
  751.     tsdPtr = FileInit();
  752.     /*
  753.      * Note we use fsRdWrShPerm instead of fsRdWrPerm which allows shared
  754.      * writes on a file.  This isn't common on a mac but is common with 
  755.      * Windows and UNIX and the feature is used by Tcl.
  756.      */
  757.     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  758. case O_RDWR:
  759.     channelPermissions = (TCL_READABLE | TCL_WRITABLE);
  760.     macPermision = fsRdWrShPerm;
  761.     break;
  762. case O_WRONLY:
  763.     /*
  764.      * Mac's fsRdPerm permission actually defaults to fsRdWrPerm because
  765.      * the Mac OS doesn't realy support write only access.  We explicitly
  766.      * set the permission fsRdWrShPerm so that we can have shared write
  767.      * access.
  768.      */
  769.     channelPermissions = TCL_WRITABLE;
  770.     macPermision = fsRdWrShPerm;
  771.     break;
  772. case O_RDONLY:
  773. default:
  774.     channelPermissions = TCL_READABLE;
  775.     macPermision = fsRdPerm;
  776.     break;
  777.     }
  778.      
  779.     err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec);
  780.     if ((err != noErr) && (err != fnfErr)) {
  781. *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
  782. Tcl_SetErrno(errno);
  783. return NULL;
  784.     }
  785.     if ((err == fnfErr) && (mode & O_CREAT)) {
  786. err = HCreate(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, TCL_FILE_CREATOR, 'TEXT');
  787. if (err != noErr) {
  788.     *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
  789.     Tcl_SetErrno(errno);
  790.     return NULL;
  791. }
  792.     } else if ((mode & O_CREAT) && (mode & O_EXCL)) {
  793.         *errorCodePtr = errno = EEXIST;
  794. Tcl_SetErrno(errno);
  795.         return NULL;
  796.     }
  797.     err = HOpenDF(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, macPermision, &fileRef);
  798.     if (err != noErr) {
  799. *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
  800. Tcl_SetErrno(errno);
  801. return NULL;
  802.     }
  803.     if (mode & O_TRUNC) {
  804. SetEOF(fileRef, 0);
  805.     }
  806.     
  807.     sprintf(channelName, "file%d", (int) fileRef);
  808.     fileState = (FileState *) ckalloc((unsigned) sizeof(FileState));
  809.     chan = Tcl_CreateChannel(&fileChannelType, channelName, 
  810. (ClientData) fileState, channelPermissions);
  811.     if (chan == (Tcl_Channel) NULL) {
  812. *errorCodePtr = errno = EFAULT;
  813. Tcl_SetErrno(errno);
  814. FSClose(fileRef);
  815. ckfree((char *) fileState);
  816.         return NULL;
  817.     }
  818.     fileState->fileChan = chan;
  819.     fileState->nextPtr = tsdPtr->firstFilePtr;
  820.     tsdPtr->firstFilePtr = fileState;
  821.     fileState->volumeRef = fileSpec.vRefNum;
  822.     fileState->fileRef = fileRef;
  823.     fileState->pending = 0;
  824.     fileState->watchMask = 0;
  825.     if (mode & O_APPEND) {
  826. fileState->appendMode = true;
  827.     } else {
  828. fileState->appendMode = false;
  829.     }
  830.         
  831.     if ((mode & O_APPEND) || (mode & O_APPEND)) {
  832.         if (Tcl_Seek(chan, 0, SEEK_END) < 0) {
  833.     *errorCodePtr = errno = EFAULT;
  834.     Tcl_SetErrno(errno);
  835.             Tcl_Close(NULL, chan);
  836.             FSClose(fileRef);
  837.             ckfree((char *) fileState);
  838.             return NULL;
  839.         }
  840.     }
  841.     
  842.     return chan;
  843. }
  844. /*
  845.  *----------------------------------------------------------------------
  846.  *
  847.  * Tcl_MakeFileChannel --
  848.  *
  849.  * Makes a Tcl_Channel from an existing OS level file handle.
  850.  *
  851.  * Results:
  852.  * The Tcl_Channel created around the preexisting OS level file handle.
  853.  *
  854.  * Side effects:
  855.  * None.
  856.  *
  857.  *----------------------------------------------------------------------
  858.  */
  859. Tcl_Channel
  860. Tcl_MakeFileChannel(handle, mode)
  861.     ClientData handle; /* OS level handle. */
  862.     int mode; /* ORed combination of TCL_READABLE and
  863.                                  * TCL_WRITABLE to indicate file mode. */
  864. {
  865.     /*
  866.      * Not implemented yet.
  867.      */
  868.     return NULL;
  869. }
  870. /*
  871.  *----------------------------------------------------------------------
  872.  *
  873.  * FileBlockMode --
  874.  *
  875.  * Set blocking or non-blocking mode on channel.  Macintosh files
  876.  * can never really be set to blocking or non-blocking modes.
  877.  * However, we don't generate an error - we just return success.
  878.  *
  879.  * Results:
  880.  * 0 if successful, errno when failed.
  881.  *
  882.  * Side effects:
  883.  * Sets the device into blocking or non-blocking mode.
  884.  *
  885.  *----------------------------------------------------------------------
  886.  */
  887. static int
  888. FileBlockMode(
  889.     ClientData instanceData, /* Unused. */
  890.     int mode) /* The mode to set. */
  891. {
  892.     return 0;
  893. }
  894. /*
  895.  *----------------------------------------------------------------------
  896.  *
  897.  * FileClose --
  898.  *
  899.  * Closes the IO channel.
  900.  *
  901.  * Results:
  902.  * 0 if successful, the value of errno if failed.
  903.  *
  904.  * Side effects:
  905.  * Closes the physical channel
  906.  *
  907.  *----------------------------------------------------------------------
  908.  */
  909. static int
  910. FileClose(
  911.     ClientData instanceData, /* Unused. */
  912.     Tcl_Interp *interp) /* Unused. */
  913. {
  914.     FileState *fileState = (FileState *) instanceData;
  915.     int errorCode = 0;
  916.     OSErr err;
  917.     err = FSClose(fileState->fileRef);
  918.     FlushVol(NULL, fileState->volumeRef);
  919.     if (err != noErr) {
  920. errorCode = errno = TclMacOSErrorToPosixError(err);
  921. panic("error during file close");
  922.     }
  923.     ckfree((char *) fileState);
  924.     Tcl_SetErrno(errorCode);
  925.     return errorCode;
  926. }
  927. /*
  928.  *----------------------------------------------------------------------
  929.  *
  930.  * FileInput --
  931.  *
  932.  * Reads input from the IO channel into the buffer given. Returns
  933.  * count of how many bytes were actually read, and an error indication.
  934.  *
  935.  * Results:
  936.  * A count of how many bytes were read is returned and an error
  937.  * indication is returned in an output argument.
  938.  *
  939.  * Side effects:
  940.  * Reads input from the actual channel.
  941.  *
  942.  *----------------------------------------------------------------------
  943.  */
  944. int
  945. FileInput(
  946.     ClientData instanceData, /* Unused. */
  947.     char *buffer, /* Where to store data read. */
  948.     int bufSize, /* How much space is available
  949.                                  * in the buffer? */
  950.     int *errorCodePtr) /* Where to store error code. */
  951. {
  952.     FileState *fileState = (FileState *) instanceData;
  953.     OSErr err;
  954.     long length = bufSize;
  955.     *errorCodePtr = 0;
  956.     errno = 0;
  957.     err = FSRead(fileState->fileRef, &length, buffer);
  958.     if ((err == noErr) || (err == eofErr)) {
  959. return length;
  960.     } else {
  961. switch (err) {
  962.     case ioErr:
  963. *errorCodePtr = errno = EIO;
  964.     case afpAccessDenied:
  965. *errorCodePtr = errno = EACCES;
  966.     default:
  967. *errorCodePtr = errno = EINVAL;
  968. }
  969.         return -1;
  970.     }
  971.     *errorCodePtr = errno;
  972.     return -1;
  973. }
  974. /*
  975.  *----------------------------------------------------------------------
  976.  *
  977.  * FileOutput--
  978.  *
  979.  * Writes the given output on the IO channel. Returns count of how
  980.  * many characters were actually written, and an error indication.
  981.  *
  982.  * Results:
  983.  * A count of how many characters were written is returned and an
  984.  * error indication is returned in an output argument.
  985.  *
  986.  * Side effects:
  987.  * Writes output on the actual channel.
  988.  *
  989.  *----------------------------------------------------------------------
  990.  */
  991. static int
  992. FileOutput(
  993.     ClientData instanceData, /* Unused. */
  994.     CONST char *buffer, /* The data buffer. */
  995.     int toWrite, /* How many bytes to write? */
  996.     int *errorCodePtr) /* Where to store error code. */
  997. {
  998.     FileState *fileState = (FileState *) instanceData;
  999.     long length = toWrite;
  1000.     OSErr err;
  1001.     *errorCodePtr = 0;
  1002.     errno = 0;
  1003.     
  1004.     if (fileState->appendMode == true) {
  1005. FileSeek(instanceData, 0, SEEK_END, errorCodePtr);
  1006. *errorCodePtr = 0;
  1007.     }
  1008.     
  1009.     err = FSWrite(fileState->fileRef, &length, buffer);
  1010.     if (err == noErr) {
  1011. err = FlushFile(fileState->fileRef);
  1012.     } else {
  1013. *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
  1014. return -1;
  1015.     }
  1016.     return length;
  1017. }
  1018. /*
  1019.  *----------------------------------------------------------------------
  1020.  *
  1021.  * FileSeek --
  1022.  *
  1023.  * Seeks on an IO channel. Returns the new position.
  1024.  *
  1025.  * Results:
  1026.  * -1 if failed, the new position if successful. If failed, it
  1027.  * also sets *errorCodePtr to the error code.
  1028.  *
  1029.  * Side effects:
  1030.  * Moves the location at which the channel will be accessed in
  1031.  * future operations.
  1032.  *
  1033.  *----------------------------------------------------------------------
  1034.  */
  1035. static int
  1036. FileSeek(
  1037.     ClientData instanceData, /* Unused. */
  1038.     long offset, /* Offset to seek to. */
  1039.     int mode, /* Relative to where should we seek? */
  1040.     int *errorCodePtr) /* To store error code. */
  1041. {
  1042.     FileState *fileState = (FileState *) instanceData;
  1043.     IOParam pb;
  1044.     OSErr err;
  1045.     *errorCodePtr = 0;
  1046.     pb.ioCompletion = NULL;
  1047.     pb.ioRefNum = fileState->fileRef;
  1048.     if (mode == SEEK_SET) {
  1049. pb.ioPosMode = fsFromStart;
  1050.     } else if (mode == SEEK_END) {
  1051. pb.ioPosMode = fsFromLEOF;
  1052.     } else if (mode == SEEK_CUR) {
  1053. err = PBGetFPosSync((ParmBlkPtr) &pb);
  1054. if (pb.ioResult == noErr) {
  1055.     if (offset == 0) {
  1056. return pb.ioPosOffset;
  1057.     }
  1058.     offset += pb.ioPosOffset;
  1059. }
  1060. pb.ioPosMode = fsFromStart;
  1061.     }
  1062.     pb.ioPosOffset = offset;
  1063.     err = PBSetFPosSync((ParmBlkPtr) &pb);
  1064.     if (pb.ioResult == noErr){
  1065. return pb.ioPosOffset;
  1066.     } else if (pb.ioResult == eofErr) {
  1067. long currentEOF, newEOF;
  1068. long buffer, i, length;
  1069. err = PBGetEOFSync((ParmBlkPtr) &pb);
  1070. currentEOF = (long) pb.ioMisc;
  1071. if (mode == SEEK_SET) {
  1072.     newEOF = offset;
  1073. } else if (mode == SEEK_END) {
  1074.     newEOF = offset + currentEOF;
  1075. } else if (mode == SEEK_CUR) {
  1076.     err = PBGetFPosSync((ParmBlkPtr) &pb);
  1077.     newEOF = offset + pb.ioPosOffset;
  1078. }
  1079. /*
  1080.  * Write 0's to the new EOF.
  1081.  */
  1082. pb.ioPosOffset = 0;
  1083. pb.ioPosMode = fsFromLEOF;
  1084. err = PBGetFPosSync((ParmBlkPtr) &pb);
  1085. length = 1;
  1086. buffer = 0;
  1087. for (i = 0; i < (newEOF - currentEOF); i++) {
  1088.     err = FSWrite(fileState->fileRef, &length, &buffer);
  1089. }
  1090. err = PBGetFPosSync((ParmBlkPtr) &pb);
  1091. if (pb.ioResult == noErr){
  1092.     return pb.ioPosOffset;
  1093. }
  1094.     }
  1095.     *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
  1096.     return -1;
  1097. }
  1098. /*
  1099.  *----------------------------------------------------------------------
  1100.  *
  1101.  * CommonWatch --
  1102.  *
  1103.  * Initialize the notifier to watch handles from this channel.
  1104.  *
  1105.  * Results:
  1106.  * None.
  1107.  *
  1108.  * Side effects:
  1109.  * None.
  1110.  *
  1111.  *----------------------------------------------------------------------
  1112.  */
  1113. static void
  1114. CommonWatch(
  1115.     ClientData instanceData, /* The file state. */
  1116.     int mask) /* Events of interest; an OR-ed
  1117.                                          * combination of TCL_READABLE,
  1118.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  1119. {
  1120.     FileState *infoPtr = (FileState *) instanceData;
  1121.     Tcl_Time blockTime = { 0, 0 };
  1122.     infoPtr->watchMask = mask;
  1123.     if (infoPtr->watchMask) {
  1124. Tcl_SetMaxBlockTime(&blockTime);
  1125.     }
  1126. }
  1127. /*
  1128.  *----------------------------------------------------------------------
  1129.  *
  1130.  * FileThreadActionProc --
  1131.  *
  1132.  * Insert or remove any thread local refs to this channel.
  1133.  *
  1134.  * Results:
  1135.  * None.
  1136.  *
  1137.  * Side effects:
  1138.  * Changes thread local list of valid channels.
  1139.  *
  1140.  *----------------------------------------------------------------------
  1141.  */
  1142. static void
  1143. FileThreadActionProc (instanceData, action)
  1144.      ClientData instanceData;
  1145.      int action;
  1146. {
  1147.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  1148.     FileState *infoPtr = (FileState *) instanceData;
  1149.     if (action == TCL_CHANNEL_THREAD_INSERT) {
  1150. infoPtr->nextPtr = tsdPtr->firstFilePtr;
  1151. tsdPtr->firstFilePtr = infoPtr;
  1152.     } else {
  1153. FileState **nextPtrPtr;
  1154. int removed = 0;
  1155. for (nextPtrPtr = &(tsdPtr->firstFilePtr); (*nextPtrPtr) != NULL;
  1156.      nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
  1157.     if ((*nextPtrPtr) == infoPtr) {
  1158.         (*nextPtrPtr) = infoPtr->nextPtr;
  1159. removed = 1;
  1160. break;
  1161.     }
  1162. }
  1163. /*
  1164.  * This could happen if the channel was created in one thread
  1165.  * and then moved to another without updating the thread
  1166.  * local data in each thread.
  1167.  */
  1168. if (!removed) {
  1169.     panic("file info ptr not on thread channel list");
  1170. }
  1171.     }
  1172. }