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