home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / mac / tclMacChan.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  35.0 KB  |  1,357 lines  |  [TEXT/CWIE]

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