513 lines
13 KiB
C
513 lines
13 KiB
C
|
/*
|
|||
|
* tclMacChan.c
|
|||
|
*
|
|||
|
* Channel drivers for Macintosh channels for the
|
|||
|
* console fds.
|
|||
|
*
|
|||
|
* Copyright (c) 1996 Sun Microsystems, Inc.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
* SCCS: @(#) tclMacChan.c 1.35 96/05/30 14:20:57
|
|||
|
*/
|
|||
|
|
|||
|
#include "tclInt.h"
|
|||
|
#include "tclPort.h"
|
|||
|
#include <Aliases.h>
|
|||
|
#include <Errors.h>
|
|||
|
#include <Files.h>
|
|||
|
#include <Gestalt.h>
|
|||
|
#include <Processes.h>
|
|||
|
#include <Strings.h>
|
|||
|
|
|||
|
/*
|
|||
|
* Static routines for this file:
|
|||
|
*/
|
|||
|
|
|||
|
static int StdIOBlockMode _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mode));
|
|||
|
static int StdIOClose _ANSI_ARGS_((ClientData instanceData,
|
|||
|
Tcl_Interp *interp));
|
|||
|
static Tcl_File StdGetFile _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int direction));
|
|||
|
static int StdIOInput _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *buf, int toRead, int *errorCode));
|
|||
|
static int StdIOOutput _ANSI_ARGS_((ClientData instanceData,
|
|||
|
char *buf, int toWrite, int *errorCode));
|
|||
|
static int StdIOSeek _ANSI_ARGS_((ClientData instanceData,
|
|||
|
long offset, int mode, int *errorCode));
|
|||
|
static int StdReady _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
static void StdWatch _ANSI_ARGS_((ClientData instanceData,
|
|||
|
int mask));
|
|||
|
|
|||
|
/*
|
|||
|
* This structure describes the channel type structure for file based IO:
|
|||
|
*/
|
|||
|
|
|||
|
static Tcl_ChannelType consoleChannelType = {
|
|||
|
"file", /* Type name. */
|
|||
|
StdIOBlockMode, /* Set blocking/nonblocking mode.*/
|
|||
|
StdIOClose, /* Close proc. */
|
|||
|
StdIOInput, /* Input proc. */
|
|||
|
StdIOOutput, /* Output proc. */
|
|||
|
StdIOSeek, /* Seek proc. */
|
|||
|
NULL, /* Set option proc. */
|
|||
|
NULL, /* Get option proc. */
|
|||
|
StdWatch, /* Initialize notifier. */
|
|||
|
StdReady, /* Are there events? */
|
|||
|
StdGetFile /* Get Tcl_Files out of channel. */
|
|||
|
};
|
|||
|
|
|||
|
/*
|
|||
|
* Hack to allow Mac Tk to override the TclGetStdChannels function.
|
|||
|
*/
|
|||
|
|
|||
|
typedef void (*TclGetStdChannelsProc) _ANSI_ARGS_((Tcl_Channel *stdinPtr,
|
|||
|
Tcl_Channel *stdoutPtr, Tcl_Channel *stderrPtr));
|
|||
|
|
|||
|
TclGetStdChannelsProc getStdChannelsProc = NULL;
|
|||
|
|
|||
|
/*
|
|||
|
* Static variables to hold channels for stdin, stdout and stderr.
|
|||
|
*/
|
|||
|
|
|||
|
static Tcl_Channel stdinChannel = NULL;
|
|||
|
static Tcl_Channel stdoutChannel = NULL;
|
|||
|
static Tcl_Channel stderrChannel = NULL;
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdIOBlockMode --
|
|||
|
*
|
|||
|
* Set blocking or non-blocking mode on channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* 0 if successful, errno when failed.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Sets the device into blocking or non-blocking mode.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
StdIOBlockMode(instanceData, mode)
|
|||
|
ClientData instanceData; /* Unused. */
|
|||
|
int mode; /* The mode to set. */
|
|||
|
{
|
|||
|
/*
|
|||
|
* Do not allow putting stdin, stdout or stderr into nonblocking mode.
|
|||
|
*/
|
|||
|
|
|||
|
if (mode == TCL_MODE_NONBLOCKING) {
|
|||
|
return EFAULT;
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdIOClose --
|
|||
|
*
|
|||
|
* Closes the IO channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* 0 if successful, the value of errno if failed.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Closes the physical channel
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
StdIOClose(instanceData, interp)
|
|||
|
ClientData instanceData; /* Unused. */
|
|||
|
Tcl_Interp *interp; /* Unused. */
|
|||
|
{
|
|||
|
int fd, errorCode = 0;
|
|||
|
|
|||
|
/*
|
|||
|
* Invalidate the stdio cache if necessary. Note that we assume that
|
|||
|
* the stdio file and channel pointers will become invalid at the same
|
|||
|
* time.
|
|||
|
*/
|
|||
|
|
|||
|
fd = (int) instanceData;
|
|||
|
if (fd == 0) {
|
|||
|
fd = 0;
|
|||
|
stdinChannel = NULL;
|
|||
|
} else if (fd == 1) {
|
|||
|
stdoutChannel = NULL;
|
|||
|
} else if (fd == 2) {
|
|||
|
stderrChannel = NULL;
|
|||
|
} else {
|
|||
|
panic("recieved invalid std file");
|
|||
|
}
|
|||
|
|
|||
|
if (close(fd) < 0) {
|
|||
|
errorCode = errno;
|
|||
|
}
|
|||
|
|
|||
|
return errorCode;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdGetFile --
|
|||
|
*
|
|||
|
* Called from Tcl_GetChannelFile to retrieve Tcl_Files from inside
|
|||
|
* a file based channel.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* The appropriate Tcl_File or NULL if not present.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static Tcl_File
|
|||
|
StdGetFile(instanceData, direction)
|
|||
|
ClientData instanceData; /* The file state. */
|
|||
|
int direction; /* Which Tcl_File to retrieve? */
|
|||
|
{
|
|||
|
if ((direction == TCL_READABLE) || (direction == TCL_WRITABLE)) {
|
|||
|
return (Tcl_File) instanceData;
|
|||
|
}
|
|||
|
return (Tcl_File) NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdIOInput --
|
|||
|
*
|
|||
|
* Reads input from the IO channel into the buffer given. Returns
|
|||
|
* count of how many bytes were actually read, and an error indication.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A count of how many bytes were read is returned and an error
|
|||
|
* indication is returned in an output argument.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Reads input from the actual channel.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
StdIOInput(instanceData, buf, bufSize, errorCode)
|
|||
|
ClientData instanceData; /* Unused. */
|
|||
|
char *buf; /* Where to store data read. */
|
|||
|
int bufSize; /* How much space is available
|
|||
|
* in the buffer? */
|
|||
|
int *errorCode; /* Where to store error code. */
|
|||
|
{
|
|||
|
int fd;
|
|||
|
int bytesRead; /* How many bytes were read? */
|
|||
|
|
|||
|
*errorCode = 0;
|
|||
|
errno = 0;
|
|||
|
fd = (int) instanceData;
|
|||
|
bytesRead = read(fd, buf, (size_t) bufSize);
|
|||
|
if (bytesRead > -1) {
|
|||
|
return bytesRead;
|
|||
|
}
|
|||
|
*errorCode = errno;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdIOOutput--
|
|||
|
*
|
|||
|
* Writes the given output on the IO channel. Returns count of how
|
|||
|
* many characters were actually written, and an error indication.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A count of how many characters were written is returned and an
|
|||
|
* error indication is returned in an output argument.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Writes output on the actual channel.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
StdIOOutput(instanceData, buf, toWrite, errorCode)
|
|||
|
ClientData instanceData; /* Unused. */
|
|||
|
char *buf; /* The data buffer. */
|
|||
|
int toWrite; /* How many bytes to write? */
|
|||
|
int *errorCode; /* Where to store error code. */
|
|||
|
{
|
|||
|
int written;
|
|||
|
int fd;
|
|||
|
|
|||
|
*errorCode = 0;
|
|||
|
errno = 0;
|
|||
|
fd = (int) instanceData;
|
|||
|
written = write(fd, buf, (size_t) toWrite);
|
|||
|
if (written > -1) {
|
|||
|
return written;
|
|||
|
}
|
|||
|
*errorCode = errno;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdReady --
|
|||
|
*
|
|||
|
* Called by the notifier to check whether events of interest are
|
|||
|
* present on the channel. On the Macintosh all files are always
|
|||
|
* considered to be readable and writeable.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns OR-ed combination of TCL_READABLE, TCL_WRITABLE and
|
|||
|
* TCL_EXCEPTION to indicate which events of interest are present.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
StdReady(instanceData, mask)
|
|||
|
ClientData instanceData; /* The file state. */
|
|||
|
int mask; /* Events of interest; an OR-ed
|
|||
|
* combination of TCL_READABLE,
|
|||
|
* TCL_WRITABLE and TCL_EXCEPTION. */
|
|||
|
{
|
|||
|
return (TCL_READABLE | TCL_WRITABLE);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdIOSeek --
|
|||
|
*
|
|||
|
* Seeks on an IO channel. Returns the new position.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* -1 if failed, the new position if successful. If failed, it
|
|||
|
* also sets *errorCodePtr to the error code.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Moves the location at which the channel will be accessed in
|
|||
|
* future operations.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
StdIOSeek(instanceData, offset, mode, errorCodePtr)
|
|||
|
ClientData instanceData; /* Unused. */
|
|||
|
long offset; /* Offset to seek to. */
|
|||
|
int mode; /* Relative to where
|
|||
|
* should we seek? */
|
|||
|
int *errorCodePtr; /* To store error code. */
|
|||
|
{
|
|||
|
int newLoc;
|
|||
|
int fd;
|
|||
|
|
|||
|
*errorCodePtr = 0;
|
|||
|
fd = (int) instanceData;
|
|||
|
newLoc = lseek(fd, offset, mode);
|
|||
|
if (newLoc > -1) {
|
|||
|
return newLoc;
|
|||
|
}
|
|||
|
*errorCodePtr = errno;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* StdWatch --
|
|||
|
*
|
|||
|
* Initialize the notifier to watch Tcl_Files from this channel.
|
|||
|
* This doesn't do anything on the Macintosh.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
StdWatch(instanceData, mask)
|
|||
|
ClientData instanceData; /* The file state. */
|
|||
|
int mask; /* Events of interest; an OR-ed
|
|||
|
* combination of TCL_READABLE,
|
|||
|
* TCL_WRITABLE and TCL_EXCEPTION. */
|
|||
|
{
|
|||
|
Tcl_Time timeout = { 0, 0 };
|
|||
|
|
|||
|
/*
|
|||
|
* Currently, files are always ready under the Macintosh,
|
|||
|
* so we just set a 0 timeout. Since there s no notification
|
|||
|
* scheme - we just set the timeout time to zero.
|
|||
|
*/
|
|||
|
|
|||
|
Tcl_SetMaxBlockTime(&timeout);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TclGetAndDetachPids --
|
|||
|
*
|
|||
|
* Stores a list of the command PIDs for a command channel in
|
|||
|
* interp->result and detaches the PIDs.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Modifies interp->result.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
TclGetAndDetachPids(interp, chan)
|
|||
|
Tcl_Interp *interp;
|
|||
|
Tcl_Channel chan;
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tcl_PidCmd --
|
|||
|
*
|
|||
|
* This procedure is invoked to process the "pid" Tcl command.
|
|||
|
* See the user documentation for details on what it does.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* A standard Tcl result.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* See the user documentation.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
Tcl_PidCmd(dummy, interp, argc, argv)
|
|||
|
ClientData dummy; /* Not used. */
|
|||
|
Tcl_Interp *interp; /* Current interpreter. */
|
|||
|
int argc; /* Number of arguments. */
|
|||
|
char **argv; /* Argument strings. */
|
|||
|
{
|
|||
|
ProcessSerialNumber psn;
|
|||
|
Tcl_Channel chan;
|
|||
|
|
|||
|
if (argc > 2) {
|
|||
|
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
|||
|
argv[0], " ?channelId?\"", (char *) NULL);
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
if (argc == 2) {
|
|||
|
chan = Tcl_GetChannel(interp, argv[1], NULL);
|
|||
|
if (chan == (Tcl_Channel) NULL) {
|
|||
|
return TCL_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* We can't create pipelines on the Mac so
|
|||
|
* this will always return an empty list.
|
|||
|
*/
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
GetCurrentProcess(&psn);
|
|||
|
sprintf(interp->result, "0x%08x%08x", psn.highLongOfPSN, psn.lowLongOfPSN);
|
|||
|
|
|||
|
return TCL_OK;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TclGetDefaultStdChannel --
|
|||
|
*
|
|||
|
* Constructs a channel for the specified standard OS handle.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns the specified default standard channel, or NULL.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* May cause the creation of a standard channel and the underlying
|
|||
|
* file.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
Tcl_Channel
|
|||
|
TclGetDefaultStdChannel(type)
|
|||
|
int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
|
|||
|
{
|
|||
|
Tcl_Channel channel = NULL;
|
|||
|
int fd = 0; /* Initializations needed to prevent */
|
|||
|
int mode = 0; /* compiler warning (used before set). */
|
|||
|
char *bufMode = NULL;
|
|||
|
char channelName[20];
|
|||
|
int channelPermissions;
|
|||
|
|
|||
|
/*
|
|||
|
* If the channels were not created yet, create them now and
|
|||
|
* store them in the static variables.
|
|||
|
*/
|
|||
|
|
|||
|
switch (type) {
|
|||
|
case TCL_STDIN:
|
|||
|
fd = 0;
|
|||
|
channelPermissions = TCL_READABLE;
|
|||
|
bufMode = "line";
|
|||
|
break;
|
|||
|
case TCL_STDOUT:
|
|||
|
fd = 1;
|
|||
|
channelPermissions = TCL_WRITABLE;
|
|||
|
bufMode = "line";
|
|||
|
break;
|
|||
|
case TCL_STDERR:
|
|||
|
fd = 2;
|
|||
|
channelPermissions = TCL_WRITABLE;
|
|||
|
bufMode = "none";
|
|||
|
break;
|
|||
|
default:
|
|||
|
panic("TclGetDefaultStdChannel: Unexpected channel type");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
sprintf(channelName, "console%d", (int) fd);
|
|||
|
channel = Tcl_CreateChannel(&consoleChannelType, channelName,
|
|||
|
(ClientData) fd, channelPermissions);
|
|||
|
/*
|
|||
|
* Set up the normal channel options for stdio handles.
|
|||
|
*/
|
|||
|
|
|||
|
Tcl_SetChannelOption(NULL, channel, "-translation", "cr");
|
|||
|
Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
|
|||
|
|
|||
|
return channel;
|
|||
|
}
|