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