diff options
Diffstat (limited to 'unix/tclUnixPipe.c')
| -rw-r--r-- | unix/tclUnixPipe.c | 987 | 
1 files changed, 573 insertions, 414 deletions
| diff --git a/unix/tclUnixPipe.c b/unix/tclUnixPipe.c index 0e26414..9c21b28 100644 --- a/unix/tclUnixPipe.c +++ b/unix/tclUnixPipe.c @@ -1,82 +1,89 @@ -/*  +/*   * tclUnixPipe.c --   * - *	This file implements the UNIX-specific exec pipeline functions, - *	the "pipe" channel driver, and the "pid" Tcl command. + *	This file implements the UNIX-specific exec pipeline functions, the + *	"pipe" channel driver, and the "pid" Tcl command.   *   * Copyright (c) 1991-1994 The Regents of the University of California.   * Copyright (c) 1994-1997 Sun Microsystems, Inc.   * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclUnixPipe.c,v 1.7 1999/12/12 02:27:20 hobbs Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #include "tclInt.h" -#include "tclPort.h" + +#ifdef USE_VFORK +#define fork vfork +#endif  /* - * The following macros convert between TclFile's and fd's.  The conversion + * The following macros convert between TclFile's and fd's. The conversion   * simple involves shifting fd's up by one to ensure that no valid fd is ever   * the same as NULL.   */ -#define MakeFile(fd) ((TclFile)(((int)fd)+1)) -#define GetFd(file) (((int)file)-1) +#define MakeFile(fd)	((TclFile) INT2PTR(((int) (fd)) + 1)) +#define GetFd(file)	(PTR2INT(file) - 1)  /*   * This structure describes per-instance state of a pipe based channel.   */  typedef struct PipeState { -    Tcl_Channel channel;/* Channel associated with this file. */ -    TclFile inFile;	/* Output from pipe. */ -    TclFile outFile;	/* Input to pipe. */ -    TclFile errorFile;	/* Error output from pipe. */ -    int numPids;	/* How many processes are attached to this pipe? */ -    Tcl_Pid *pidPtr;	/* The process IDs themselves. Allocated by -                         * the creator of the pipe. */ -    int isNonBlocking;	/* Nonzero when the pipe is in nonblocking mode. -                         * Used to decide whether to wait for the children -                         * at close time. */ +    Tcl_Channel channel;	/* Channel associated with this file. */ +    TclFile inFile;		/* Output from pipe. */ +    TclFile outFile;		/* Input to pipe. */ +    TclFile errorFile;		/* Error output from pipe. */ +    int numPids;		/* How many processes are attached to this +				 * pipe? */ +    Tcl_Pid *pidPtr;		/* The process IDs themselves. Allocated by +				 * the creator of the pipe. */ +    int isNonBlocking;		/* Nonzero when the pipe is in nonblocking +				 * mode. Used to decide whether to wait for +				 * the children at close time. */  } PipeState;  /* - * Declarations for local procedures defined in this file: + * Declarations for local functions defined in this file:   */ -static int	PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData, -		    int mode)); -static int	PipeCloseProc _ANSI_ARGS_((ClientData instanceData, -		    Tcl_Interp *interp)); -static int	PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData, -		    int direction, ClientData *handlePtr)); -static int	PipeInputProc _ANSI_ARGS_((ClientData instanceData, -		    char *buf, int toRead, int *errorCode)); -static int	PipeOutputProc _ANSI_ARGS_(( -		    ClientData instanceData, char *buf, int toWrite, -		    int *errorCode)); -static void	PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask)); -static void	RestoreSignals _ANSI_ARGS_((void)); -static int	SetupStdFile _ANSI_ARGS_((TclFile file, int type)); +static int		PipeBlockModeProc(ClientData instanceData, int mode); +static int		PipeClose2Proc(ClientData instanceData, +			    Tcl_Interp *interp, int flags); +static int		PipeGetHandleProc(ClientData instanceData, +			    int direction, ClientData *handlePtr); +static int		PipeInputProc(ClientData instanceData, char *buf, +			    int toRead, int *errorCode); +static int		PipeOutputProc(ClientData instanceData, +			    const char *buf, int toWrite, int *errorCode); +static void		PipeWatchProc(ClientData instanceData, int mask); +static void		RestoreSignals(void); +static int		SetupStdFile(TclFile file, int type);  /* - * This structure describes the channel type structure for command pipe - * based IO: + * This structure describes the channel type structure for command pipe based + * I/O:   */ -static Tcl_ChannelType pipeChannelType = { -    "pipe",				/* Type name. */ -    PipeBlockModeProc,			/* Set blocking/nonblocking mode.*/ -    PipeCloseProc,			/* Close proc. */ -    PipeInputProc,			/* Input proc. */ -    PipeOutputProc,			/* Output proc. */ -    NULL,				/* Seek proc. */ -    NULL,				/* Set option proc. */ -    NULL,				/* Get option proc. */ -    PipeWatchProc,			/* Initialize notifier. */ -    PipeGetHandleProc,			/* Get OS handles out of channel. */ +static const Tcl_ChannelType pipeChannelType = { +    "pipe",			/* Type name. */ +    TCL_CHANNEL_VERSION_5,	/* v5 channel */ +    TCL_CLOSE2PROC,		/* Close proc. */ +    PipeInputProc,		/* Input proc. */ +    PipeOutputProc,		/* Output proc. */ +    NULL,			/* Seek proc. */ +    NULL,			/* Set option proc. */ +    NULL,			/* Get option proc. */ +    PipeWatchProc,		/* Initialize notifier. */ +    PipeGetHandleProc,		/* Get OS handles out of channel. */ +    PipeClose2Proc,		/* close2proc. */ +    PipeBlockModeProc,		/* Set blocking or non-blocking mode.*/ +    NULL,			/* flush proc. */ +    NULL,			/* handler proc. */ +    NULL,			/* wide seek proc */ +    NULL,			/* thread action proc */ +    NULL			/* truncation */  };  /* @@ -96,18 +103,17 @@ static Tcl_ChannelType pipeChannelType = {   */  TclFile -TclpMakeFile(channel, direction) -    Tcl_Channel channel;	/* Channel to get file from. */ -    int direction;		/* Either TCL_READABLE or TCL_WRITABLE. */ +TclpMakeFile( +    Tcl_Channel channel,	/* Channel to get file from. */ +    int direction)		/* Either TCL_READABLE or TCL_WRITABLE. */  {      ClientData data; -    if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &data) -	    == TCL_OK) { -	return MakeFile((int)data); -    } else { -	return (TclFile) NULL; +    if (Tcl_GetChannelHandle(channel, direction, &data) != TCL_OK) { +	return NULL;      } + +    return MakeFile(PTR2INT(data));  }  /* @@ -115,7 +121,7 @@ TclpMakeFile(channel, direction)   *   * TclpOpenFile --   * - *	Open a file for use in a pipeline.   + *	Open a file for use in a pipeline.   *   * Results:   *	Returns a new TclFile handle or NULL on failure. @@ -127,32 +133,32 @@ TclpMakeFile(channel, direction)   */  TclFile -TclpOpenFile(fname, mode) -    CONST char *fname;		/* The name of the file to open. */ -    int mode;			/* In what mode to open the file? */ +TclpOpenFile( +    const char *fname,		/* The name of the file to open. */ +    int mode)			/* In what mode to open the file? */  {      int fd; -    char *native; +    const char *native;      Tcl_DString ds;      native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds); -    fd = open(native, mode, 0666);			/* INTL: Native. */ +    fd = TclOSopen(native, mode, 0666);			/* INTL: Native. */      Tcl_DStringFree(&ds);      if (fd != -1) { -        fcntl(fd, F_SETFD, FD_CLOEXEC); +	fcntl(fd, F_SETFD, FD_CLOEXEC);  	/* -	 * If the file is being opened for writing, seek to the end -	 * so we can append to any data already in the file. +	 * If the file is being opened for writing, seek to the end so we can +	 * append to any data already in the file.  	 */ -	if (mode & O_WRONLY) { -	    lseek(fd, 0, SEEK_END); +	if ((mode & O_WRONLY) && !(mode & O_APPEND)) { +	    TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END);  	}  	/* -	 * Increment the fd so it can't be 0, which would conflict with -	 * the NULL return for errors. +	 * Increment the fd so it can't be 0, which would conflict with the +	 * NULL return for errors.  	 */  	return MakeFile(fd); @@ -165,9 +171,9 @@ TclpOpenFile(fname, mode)   *   * TclpCreateTempFile --   * - *	This function creates a temporary file initialized with an - *	optional string, and returns a file handle with the file pointer - *	at the beginning of the file. + *	This function creates a temporary file initialized with an optional + *	string, and returns a file handle with the file pointer at the + *	beginning of the file.   *   * Results:   *	A handle to a file. @@ -179,26 +185,27 @@ TclpOpenFile(fname, mode)   */  TclFile -TclpCreateTempFile(contents) -    CONST char *contents;	/* String to write into temp file, or NULL. */ +TclpCreateTempFile( +    const char *contents)	/* String to write into temp file, or NULL. */  { -    char fileName[L_tmpnam]; -    int fd; +    int fd = TclUnixOpenTemporaryFile(NULL, NULL, NULL, NULL); -    tmpnam(fileName);					/* INTL: Native. */ -    fd = open(fileName, O_RDWR|O_CREAT|O_TRUNC, 0666);	/* INTL: Native. */      if (fd == -1) {  	return NULL;      }      fcntl(fd, F_SETFD, FD_CLOEXEC); -    unlink(fileName);					/* INTL: Native. */ -      if (contents != NULL) { -	if (write(fd, contents, strlen(contents)) == -1) { +	Tcl_DString dstring; +	char *native; + +	native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); +	if (write(fd, native, Tcl_DStringLength(&dstring)) == -1) {  	    close(fd); +	    Tcl_DStringFree(&dstring);  	    return NULL;  	} -	lseek(fd, 0, SEEK_SET); +	Tcl_DStringFree(&dstring); +	TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET);      }      return MakeFile(fd);  } @@ -206,25 +213,93 @@ TclpCreateTempFile(contents)  /*   *----------------------------------------------------------------------   * + * TclpTempFileName -- + * + *	This function returns unique filename. + * + * Results: + *	Returns a valid Tcl_Obj* with refCount 0, or NULL on failure. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TclpTempFileName(void) +{ +    Tcl_Obj *nameObj = Tcl_NewObj(); +    int fd; + +    Tcl_IncrRefCount(nameObj); +    fd = TclUnixOpenTemporaryFile(NULL, NULL, NULL, nameObj); +    if (fd == -1) { +	Tcl_DecrRefCount(nameObj); +	return NULL; +    } + +    fcntl(fd, F_SETFD, FD_CLOEXEC); +    TclpObjDeleteFile(nameObj); +    close(fd); +    return nameObj; +} + +/* + *---------------------------------------------------------------------------- + * + * TclpTempFileNameForLibrary -- + * + *	Constructs a file name in the native file system where a dynamically + *	loaded library may be placed. + * + * Results: + *	Returns the constructed file name. If an error occurs, returns NULL + *	and leaves an error message in the interpreter result. + * + * On Unix, it works to load a shared object from a file of any name, so this + * function is merely a thin wrapper around TclpTempFileName(). + *	 + *---------------------------------------------------------------------------- + */ + +Tcl_Obj * +TclpTempFileNameForLibrary( +    Tcl_Interp *interp,		/* Tcl interpreter. */ +    Tcl_Obj *path)		/* Path name of the library in the VFS. */ +{ +    Tcl_Obj *retval = TclpTempFileName(); + +    if (retval == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"couldn't create temporary file: %s", +		Tcl_PosixError(interp))); +    } +    return retval; +} + +/* + *---------------------------------------------------------------------- + *   * TclpCreatePipe --   * - *      Creates a pipe - simply calls the pipe() function. + *	Creates a pipe - simply calls the pipe() function.   *   * Results: - *      Returns 1 on success, 0 on failure.  + *	Returns 1 on success, 0 on failure.   *   * Side effects: - *      Creates a pipe. + *	Creates a pipe.   *   *----------------------------------------------------------------------   */  int -TclpCreatePipe(readPipe, writePipe) -    TclFile *readPipe;		/* Location to store file handle for -				 * read side of pipe. */ -    TclFile *writePipe;		/* Location to store file handle for -				 * write side of pipe. */ +TclpCreatePipe( +    TclFile *readPipe,		/* Location to store file handle for read side +				 * of pipe. */ +    TclFile *writePipe)		/* Location to store file handle for write +				 * side of pipe. */  {      int pipeIds[2]; @@ -257,19 +332,19 @@ TclpCreatePipe(readPipe, writePipe)   */  int -TclpCloseFile(file) -    TclFile file;	/* The file to close. */ +TclpCloseFile( +    TclFile file)	/* The file to close. */  {      int fd = GetFd(file);      /*       * Refuse to close the fds for stdin, stdout and stderr.       */ -     +      if ((fd == 0) || (fd == 1) || (fd == 2)) { -        return 0; +	return 0;      } -     +      Tcl_DeleteFileHandler(fd);      return close(fd);  } @@ -279,93 +354,114 @@ TclpCloseFile(file)   *   * TclpCreateProcess --   * - *	Create a child process that has the specified files as its  - *	standard input, output, and error.  The child process runs - *	asynchronously and runs with the same environment variables - *	as the creating process. + *	Create a child process that has the specified files as its standard + *	input, output, and error. The child process runs asynchronously and + *	runs with the same environment variables as the creating process.   * - *	The path is searched to find the specified executable.   + *	The path is searched to find the specified executable.   *   * Results: - *	The return value is TCL_ERROR and an error message is left in - *	the interp's result if there was a problem creating the child  - *	process.  Otherwise, the return value is TCL_OK and *pidPtr is - *	filled with the process id of the child process. - *  + *	The return value is TCL_ERROR and an error message is left in the + *	interp's result if there was a problem creating the child process. + *	Otherwise, the return value is TCL_OK and *pidPtr is filled with the + *	process id of the child process. + *   * Side effects:   *	A process is created. - *	 + *   *---------------------------------------------------------------------------   */      /* ARGSUSED */  int -TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,  -	pidPtr) -    Tcl_Interp *interp;		/* Interpreter in which to leave errors that +TclpCreateProcess( +    Tcl_Interp *interp,		/* Interpreter in which to leave errors that  				 * occurred when creating the child process.  				 * Error messages from the child process  				 * itself are sent to errorFile. */ -    int argc;			/* Number of arguments in following array. */ -    char **argv;		/* Array of argument strings in UTF-8. +    int argc,			/* Number of arguments in following array. */ +    const char **argv,		/* Array of argument strings in UTF-8.  				 * argv[0] contains the name of the executable  				 * translated using Tcl_TranslateFileName -				 * call).  Additional arguments have not been +				 * call). Additional arguments have not been  				 * converted. */ -    TclFile inputFile;		/* If non-NULL, gives the file to use as -				 * input for the child process.  If inputFile -				 * file is not readable or is NULL, the child -				 * will receive no standard input. */ -    TclFile outputFile;		/* If non-NULL, gives the file that -				 * receives output from the child process.  If +    TclFile inputFile,		/* If non-NULL, gives the file to use as input +				 * for the child process. If inputFile file is +				 * not readable or is NULL, the child will +				 * receive no standard input. */ +    TclFile outputFile,		/* If non-NULL, gives the file that receives +				 * output from the child process. If  				 * outputFile file is not writeable or is  				 * NULL, output from the child will be  				 * discarded. */ -    TclFile errorFile;		/* If non-NULL, gives the file that -				 * receives errors from the child process.  If -				 * errorFile file is not writeable or is NULL, -				 * errors from the child will be discarded. -				 * errorFile may be the same as outputFile. */ -    Tcl_Pid *pidPtr;		/* If this procedure is successful, pidPtr -				 * is filled with the process id of the child +    TclFile errorFile,		/* If non-NULL, gives the file that receives +				 * errors from the child process. If errorFile +				 * file is not writeable or is NULL, errors +				 * from the child will be discarded. errorFile +				 * may be the same as outputFile. */ +    Tcl_Pid *pidPtr)		/* If this function is successful, pidPtr is +				 * filled with the process id of the child  				 * process. */  {      TclFile errPipeIn, errPipeOut; -    int joinThisError, count, status, fd; +    int count, status, fd;      char errSpace[200 + TCL_INTEGER_SPACE];      Tcl_DString *dsArray;      char **newArgv;      int pid, i; -     +      errPipeIn = NULL;      errPipeOut = NULL;      pid = -1;      /* -     * Create a pipe that the child can use to return error -     * information if anything goes wrong. +     * Create a pipe that the child can use to return error information if +     * anything goes wrong.       */      if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) { -	Tcl_AppendResult(interp, "couldn't create pipe: ", -		Tcl_PosixError(interp), (char *) NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"couldn't create pipe: %s", Tcl_PosixError(interp)));  	goto error;      }      /* -     * We need to allocate and convert this before the fork -     * so it is properly deallocated later +     * We need to allocate and convert this before the fork so it is properly +     * deallocated later       */ -    dsArray = (Tcl_DString *) ckalloc(argc * sizeof(Tcl_DString)); -    newArgv = (char **) ckalloc((argc+1) * sizeof(char *)); + +    dsArray = TclStackAlloc(interp, argc * sizeof(Tcl_DString)); +    newArgv = TclStackAlloc(interp, (argc+1) * sizeof(char *));      newArgv[argc] = NULL;      for (i = 0; i < argc; i++) {  	newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]);      } -    joinThisError = (errorFile == outputFile); +#ifdef USE_VFORK +    /* +     * After vfork(), do not call code in the child that changes global state, +     * because it is using the parent's memory space at that point and writes +     * might corrupt the parent: so ensure standard channels are initialized +     * in the parent, otherwise SetupStdFile() might initialize them in the +     * child. +     */ + +    if (!inputFile) { +	Tcl_GetStdChannel(TCL_STDIN); +    } +    if (!outputFile) { +        Tcl_GetStdChannel(TCL_STDOUT); +    } +    if (!errorFile) { +        Tcl_GetStdChannel(TCL_STDERR); +    } +#endif +      pid = fork();      if (pid == 0) { +	size_t len; +	int joinThisError = errorFile && (errorFile == outputFile); +  	fd = GetFd(errPipeOut);  	/* @@ -376,11 +472,13 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,  		|| !SetupStdFile(outputFile, TCL_STDOUT)  		|| (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))  		|| (joinThisError && -			((dup2(1,2) == -1) || -			 (fcntl(2, F_SETFD, 0) != 0)))) { +			((dup2(1,2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) {  	    sprintf(errSpace, -		    "%dforked process couldn't set up input/output: ", errno); -	    write(fd, errSpace, (size_t) strlen(errSpace)); +		    "%dforked process couldn't set up input/output", errno); +	    len = strlen(errSpace); +	    if (len != (size_t) write(fd, errSpace, len)) { +		    Tcl_Panic("TclpCreateProcess: unable to write to errPipeOut"); +	    }  	    _exit(1);  	} @@ -390,30 +488,34 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,  	RestoreSignals();  	execvp(newArgv[0], newArgv);			/* INTL: Native. */ -	sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]); -	write(fd, errSpace, (size_t) strlen(errSpace)); +	sprintf(errSpace, "%dcouldn't execute \"%.150s\"", errno, argv[0]); +	len = strlen(errSpace); +	if (len != (size_t) write(fd, errSpace, len)) { +	    Tcl_Panic("TclpCreateProcess: unable to write to errPipeOut"); +	}  	_exit(1);      } -     +      /*       * Free the mem we used for the fork       */ +      for (i = 0; i < argc; i++) {  	Tcl_DStringFree(&dsArray[i]);      } -    ckfree((char *) dsArray); -    ckfree((char *) newArgv); +    TclStackFree(interp, newArgv); +    TclStackFree(interp, dsArray);      if (pid == -1) { -	Tcl_AppendResult(interp, "couldn't fork child process: ", -		Tcl_PosixError(interp), (char *) NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"couldn't fork child process: %s", Tcl_PosixError(interp)));  	goto error;      }      /* -     * Read back from the error pipe to see if the child started -     * up OK.  The info in the pipe (if any) consists of a decimal -     * errno value followed by an error message. +     * Read back from the error pipe to see if the child started up OK. The +     * info in the pipe (if any) consists of a decimal errno value followed by +     * an error message.       */      TclpCloseFile(errPipeOut); @@ -423,27 +525,30 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,      count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));      if (count > 0) {  	char *end; +  	errSpace[count] = 0;  	errno = strtol(errSpace, &end, 10); -	Tcl_AppendResult(interp, end, Tcl_PosixError(interp), -		(char *) NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf("%s: %s", +		end, Tcl_PosixError(interp)));  	goto error;      } -     +      TclpCloseFile(errPipeIn); -    *pidPtr = (Tcl_Pid) pid; +    *pidPtr = (Tcl_Pid) INT2PTR(pid);      return TCL_OK; -    error: +  error:      if (pid != -1) {  	/* -	 * Reap the child process now if an error occurred during its -	 * startup. +	 * Reap the child process now if an error occurred during its startup. +	 * We don't call this with WNOHANG because that can lead to defunct +	 * processes on an MP system. We shouldn't have to worry about hanging +	 * here, since this is the error case. [Bug: 6148]  	 */ -	Tcl_WaitPid((Tcl_Pid) pid, &status, WNOHANG); +	Tcl_WaitPid((Tcl_Pid) INT2PTR(pid), &status, 0);      } -     +      if (errPipeIn) {  	TclpCloseFile(errPipeIn);      } @@ -458,21 +563,21 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,   *   * RestoreSignals --   * - *      This procedure is invoked in a forked child process just before - *      exec-ing a new program to restore all signals to their default - *      settings. + *	This function is invoked in a forked child process just before + *	exec-ing a new program to restore all signals to their default + *	settings.   *   * Results: - *      None. + *	None.   *   * Side effects: - *      Signal settings get changed. + *	Signal settings get changed.   *   *----------------------------------------------------------------------   */ -  +  static void -RestoreSignals() +RestoreSignals(void)  {  #ifdef SIGABRT      signal(SIGABRT, SIG_DFL); @@ -532,10 +637,10 @@ RestoreSignals()   *   * SetupStdFile --   * - *	Set up stdio file handles for the child process, using the - *	current standard channels if no other files are specified. - *	If no standard channel is defined, or if no file is associated - *	with the channel, then the corresponding standard fd is closed. + *	Set up stdio file handles for the child process, using the current + *	standard channels if no other files are specified. If no standard + *	channel is defined, or if no file is associated with the channel, then + *	the corresponding standard fd is closed.   *   * Results:   *	Returns 1 on success, or 0 on failure. @@ -547,9 +652,9 @@ RestoreSignals()   */  static int -SetupStdFile(file, type) -    TclFile file;		/* File to dup, or NULL. */ -    int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */ +SetupStdFile( +    TclFile file,		/* File to dup, or NULL. */ +    int type)			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */  {      Tcl_Channel channel;      int fd; @@ -558,18 +663,18 @@ SetupStdFile(file, type)  				 * variables. */      switch (type) { -	case TCL_STDIN: -	    targetFd = 0; -	    direction = TCL_READABLE; -	    break; -	case TCL_STDOUT: -	    targetFd = 1; -	    direction = TCL_WRITABLE; -	    break; -	case TCL_STDERR: -	    targetFd = 2; -	    direction = TCL_WRITABLE; -	    break; +    case TCL_STDIN: +	targetFd = 0; +	direction = TCL_READABLE; +	break; +    case TCL_STDOUT: +	targetFd = 1; +	direction = TCL_WRITABLE; +	break; +    case TCL_STDERR: +	targetFd = 2; +	direction = TCL_WRITABLE; +	break;      }      if (!file) { @@ -585,22 +690,20 @@ SetupStdFile(file, type)  		return 0;  	    } -            /* -             * Must clear the close-on-exec flag for the target FD, since -             * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on -             * the target FD. -             */ -             -            fcntl(targetFd, F_SETFD, 0); -	} else { -	    int result; +	    /* +	     * Must clear the close-on-exec flag for the target FD, since some +	     * systems (e.g. Ultrix) do not clear the CLOEXEC flag on the +	     * target FD. +	     */ +	    fcntl(targetFd, F_SETFD, 0); +	} else {  	    /*  	     * Since we aren't dup'ing the file, we need to explicitly clear  	     * the close-on-exec flag.  	     */ -	    result = fcntl(fd, F_SETFD, 0); +	    fcntl(fd, F_SETFD, 0);  	}      } else {  	close(targetFd); @@ -613,9 +716,8 @@ SetupStdFile(file, type)   *   * TclpCreateCommandChannel --   * - *	This function is called by the generic IO level to perform - *	the platform specific channel initialization for a command - *	channel. + *	This function is called by the generic IO level to perform the + *	platform specific channel initialization for a command channel.   *   * Results:   *	Returns a new channel or NULL on failure. @@ -627,20 +729,20 @@ SetupStdFile(file, type)   */  Tcl_Channel -TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr) -    TclFile readFile;		/* If non-null, gives the file for reading. */ -    TclFile writeFile;		/* If non-null, gives the file for writing. */ -    TclFile errorFile;		/* If non-null, gives the file where errors +TclpCreateCommandChannel( +    TclFile readFile,		/* If non-null, gives the file for reading. */ +    TclFile writeFile,		/* If non-null, gives the file for writing. */ +    TclFile errorFile,		/* If non-null, gives the file where errors  				 * can be read. */ -    int numPids;		/* The number of pids in the pid array. */ -    Tcl_Pid *pidPtr;		/* An array of process identifiers. -                                 * Allocated by the caller, freed when -                                 * the channel is closed or the processes -                                 * are detached (in a background exec). */ +    int numPids,		/* The number of pids in the pid array. */ +    Tcl_Pid *pidPtr)		/* An array of process identifiers. Allocated +				 * by the caller, freed when the channel is +				 * closed or the processes are detached (in a +				 * background exec). */  {      char channelName[16 + TCL_INTEGER_SPACE];      int channelId; -    PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState)); +    PipeState *statePtr = ckalloc(sizeof(PipeState));      int mode;      statePtr->inFile = readFile; @@ -652,15 +754,14 @@ TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)      mode = 0;      if (readFile) { -        mode |= TCL_READABLE; +	mode |= TCL_READABLE;      }      if (writeFile) { -        mode |= TCL_WRITABLE; +	mode |= TCL_WRITABLE;      } -     +      /* -     * Use one of the fds associated with the channel as the -     * channel id. +     * Use one of the fds associated with the channel as the channel id.       */      if (readFile) { @@ -674,26 +775,69 @@ TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)      }      /* -     * For backward compatibility with previous versions of Tcl, we -     * use "file%d" as the base name for pipes even though it would -     * be more natural to use "pipe%d". +     * For backward compatibility with previous versions of Tcl, we use +     * "file%d" as the base name for pipes even though it would be more +     * natural to use "pipe%d".       */      sprintf(channelName, "file%d", channelId);      statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, -            (ClientData) statePtr, mode); +	    statePtr, mode);      return statePtr->channel;  }  /*   *----------------------------------------------------------------------   * + * Tcl_CreatePipe -- + * + *	System dependent interface to create a pipe for the [chan pipe] + *	command. Stolen from TclX. + * + * Results: + *	TCL_OK or TCL_ERROR. + * + * Side effects: + *	Registers two channels. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_CreatePipe( +    Tcl_Interp *interp,		/* Errors returned in result. */ +    Tcl_Channel *rchan,		/* Returned read side. */ +    Tcl_Channel *wchan,		/* Returned write side. */ +    int flags)			/* Reserved for future use. */ +{ +    int fileNums[2]; + +    if (pipe(fileNums) < 0) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf("pipe creation failed: %s", +		Tcl_PosixError(interp))); +	return TCL_ERROR; +    } + +    fcntl(fileNums[0], F_SETFD, FD_CLOEXEC); +    fcntl(fileNums[1], F_SETFD, FD_CLOEXEC); + +    *rchan = Tcl_MakeFileChannel(INT2PTR(fileNums[0]), TCL_READABLE); +    Tcl_RegisterChannel(interp, *rchan); +    *wchan = Tcl_MakeFileChannel(INT2PTR(fileNums[1]), TCL_WRITABLE); +    Tcl_RegisterChannel(interp, *wchan); + +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + *   * TclGetAndDetachPids --   * - *	This procedure is invoked in the generic implementation of a - *	background "exec" (An exec when invoked with a terminating "&") - *	to store a list of the PIDs for processes in a command pipeline - *	in the interp's result and to detach the processes. + *	This function is invoked in the generic implementation of a + *	background "exec" (an exec when invoked with a terminating "&") to + *	store a list of the PIDs for processes in a command pipeline in the + *	interp's result and to detach the processes.   *   * Results:   *	None. @@ -705,14 +849,14 @@ TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)   */  void -TclGetAndDetachPids(interp, chan) -    Tcl_Interp *interp; -    Tcl_Channel chan; +TclGetAndDetachPids( +    Tcl_Interp *interp,		/* Interpreter to append the PIDs to. */ +    Tcl_Channel chan)		/* Handle for the pipeline. */  {      PipeState *pipePtr; -    Tcl_ChannelType *chanTypePtr; +    const Tcl_ChannelType *chanTypePtr; +    Tcl_Obj *pidsObj;      int i; -    char buf[TCL_INTEGER_SPACE];      /*       * Punt if the channel is not a command channel. @@ -720,18 +864,20 @@ TclGetAndDetachPids(interp, chan)      chanTypePtr = Tcl_GetChannelType(chan);      if (chanTypePtr != &pipeChannelType) { -        return; +	return;      } -    pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); +    pipePtr = Tcl_GetChannelInstanceData(chan); +    TclNewObj(pidsObj);      for (i = 0; i < pipePtr->numPids; i++) { -        TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i])); -        Tcl_AppendElement(interp, buf); -        Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); +	Tcl_ListObjAppendElement(NULL, pidsObj, Tcl_NewIntObj( +		PTR2INT(pipePtr->pidPtr[i]))); +	Tcl_DetachPids(1, &pipePtr->pidPtr[i]);      } +    Tcl_SetObjResult(interp, pidsObj);      if (pipePtr->numPids > 0) { -        ckfree((char *) pipePtr->pidPtr); -        pipePtr->numPids = 0; +	ckfree(pipePtr->pidPtr); +	pipePtr->numPids = 0;      }  } @@ -740,8 +886,8 @@ TclGetAndDetachPids(interp, chan)   *   * PipeBlockModeProc --   * - *	Helper procedure to set blocking and nonblocking modes on a - *	pipe based channel. Invoked by generic IO level code. + *	Helper function to set blocking and nonblocking modes on a pipe based + *	channel. Invoked by generic IO level code.   *   * Results:   *	0 if successful, errno when failed. @@ -754,67 +900,22 @@ TclGetAndDetachPids(interp, chan)  	/* ARGSUSED */  static int -PipeBlockModeProc(instanceData, mode) -    ClientData instanceData;		/* Pipe state. */ -    int mode;				/* The mode to set. Can be one of -                                         * TCL_MODE_BLOCKING or -                                         * TCL_MODE_NONBLOCKING. */ +PipeBlockModeProc( +    ClientData instanceData,	/* Pipe state. */ +    int mode)			/* The mode to set. Can be one of +				 * TCL_MODE_BLOCKING or +				 * TCL_MODE_NONBLOCKING. */  { -    PipeState *psPtr = (PipeState *) instanceData; -    int curStatus; -    int fd; +    PipeState *psPtr = instanceData; -#ifndef	USE_FIONBIO     -    if (psPtr->inFile) { -        fd = GetFd(psPtr->inFile); -        curStatus = fcntl(fd, F_GETFL); -        if (mode == TCL_MODE_BLOCKING) { -            curStatus &= (~(O_NONBLOCK)); -        } else { -            curStatus |= O_NONBLOCK; -        } -        if (fcntl(fd, F_SETFL, curStatus) < 0) { -            return errno; -        } +    if (psPtr->inFile +	    && TclUnixSetBlockingMode(GetFd(psPtr->inFile), mode) < 0) { +	return errno;      } -    if (psPtr->outFile) { -        fd = GetFd(psPtr->outFile); -        curStatus = fcntl(fd, F_GETFL); -        if (mode == TCL_MODE_BLOCKING) { -            curStatus &= (~(O_NONBLOCK)); -        } else { -            curStatus |= O_NONBLOCK; -        } -        if (fcntl(fd, F_SETFL, curStatus) < 0) { -            return errno; -        } +    if (psPtr->outFile +	    && TclUnixSetBlockingMode(GetFd(psPtr->outFile), mode) < 0) { +	return errno;      } -#endif	/* !FIONBIO */ - -#ifdef	USE_FIONBIO -    if (psPtr->inFile) { -        fd = GetFd(psPtr->inFile); -        if (mode == TCL_MODE_BLOCKING) { -            curStatus = 0; -        } else { -            curStatus = 1; -        } -        if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) { -            return errno; -        } -    } -    if (psPtr->outFile != NULL) { -        fd = GetFd(psPtr->outFile); -        if (mode == TCL_MODE_BLOCKING) { -            curStatus = 0; -        } else { -            curStatus = 1; -        } -        if (ioctl(fd, (int) FIONBIO,  &curStatus) < 0) { -            return errno; -        } -    } -#endif	/* USE_FIONBIO */      psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING); @@ -824,11 +925,10 @@ PipeBlockModeProc(instanceData, mode)  /*   *----------------------------------------------------------------------   * - * PipeCloseProc -- + * PipeClose2Proc   * - *	This procedure is invoked by the generic IO level to perform - *	channel-type-specific cleanup when a command pipeline channel - *	is closed. + *	This function is invoked by the generic IO level to perform + *	pipeline-type-specific half or full-close.   *   * Results:   *	0 on success, errno otherwise. @@ -839,67 +939,79 @@ PipeBlockModeProc(instanceData, mode)   *----------------------------------------------------------------------   */ -	/* ARGSUSED */  static int -PipeCloseProc(instanceData, interp) -    ClientData instanceData;	/* The pipe to close. */ -    Tcl_Interp *interp;		/* For error reporting. */ +PipeClose2Proc( +    ClientData instanceData,	/* The pipe to close. */ +    Tcl_Interp *interp,		/* For error reporting. */ +    int flags)			/* Flags that indicate which side to close. */  { -    PipeState *pipePtr; +    PipeState *pipePtr = instanceData;      Tcl_Channel errChan;      int errorCode, result;      errorCode = 0;      result = 0; -    pipePtr = (PipeState *) instanceData; -    if (pipePtr->inFile) { + +    if (((!flags) || (flags & TCL_CLOSE_READ)) && (pipePtr->inFile != NULL)) {  	if (TclpCloseFile(pipePtr->inFile) < 0) {  	    errorCode = errno; +	} else { +	    pipePtr->inFile = NULL;  	}      } -    if (pipePtr->outFile) { -	if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) { +    if (((!flags) || (flags & TCL_CLOSE_WRITE)) && (pipePtr->outFile != NULL) +	    && (errorCode == 0)) { +	if (TclpCloseFile(pipePtr->outFile) < 0) {  	    errorCode = errno; +	} else { +	    pipePtr->outFile = NULL;  	}      } +     +    /* +     * If half-closing, stop here. +     */ + +    if (flags) { +	return errorCode; +    }      if (pipePtr->isNonBlocking || TclInExit()) { -      	/* -         * If the channel is non-blocking or Tcl is being cleaned up, just -         * detach the children PIDs, reap them (important if we are in a -         * dynamic load module), and discard the errorFile. -         */ -         -        Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr); -        Tcl_ReapDetachedProcs(); - -        if (pipePtr->errorFile) { +	 * If the channel is non-blocking or Tcl is being cleaned up, just +	 * detach the children PIDs, reap them (important if we are in a +	 * dynamic load module), and discard the errorFile. +	 */ + +	Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr); +	Tcl_ReapDetachedProcs(); + +	if (pipePtr->errorFile) {  	    TclpCloseFile(pipePtr->errorFile); -        } +	}      } else { -          	/* -         * Wrap the error file into a channel and give it to the cleanup -         * routine. -         */ +	 * Wrap the error file into a channel and give it to the cleanup +	 * routine. +	 */ -        if (pipePtr->errorFile) { +	if (pipePtr->errorFile) {  	    errChan = Tcl_MakeFileChannel( -		(ClientData) GetFd(pipePtr->errorFile), TCL_READABLE); -        } else { -            errChan = NULL; -        } -        result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, -                errChan); +		    INT2PTR(GetFd(pipePtr->errorFile)), +		    TCL_READABLE); +	} else { +	    errChan = NULL; +	} +	result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, +		errChan);      }      if (pipePtr->numPids != 0) { -        ckfree((char *) pipePtr->pidPtr); +	ckfree(pipePtr->pidPtr);      } -    ckfree((char *) pipePtr); +    ckfree(pipePtr);      if (errorCode == 0) { -        return result; +	return result;      }      return errorCode;  } @@ -909,8 +1021,8 @@ PipeCloseProc(instanceData, interp)   *   * PipeInputProc --   * - *	This procedure is invoked from the generic IO level to read - *	input from a command pipeline based channel. + *	This function is invoked from the generic IO level to read input from + *	a command pipeline based channel.   *   * Results:   *	The number of bytes read is returned or -1 on error. An output @@ -923,32 +1035,36 @@ PipeCloseProc(instanceData, interp)   */  static int -PipeInputProc(instanceData, buf, toRead, errorCodePtr) -    ClientData instanceData;		/* Pipe state. */ -    char *buf;				/* Where to store data read. */ -    int toRead;				/* How much space is available -                                         * in the buffer? */ -    int *errorCodePtr;			/* Where to store error code. */ +PipeInputProc( +    ClientData instanceData,	/* Pipe state. */ +    char *buf,			/* Where to store data read. */ +    int toRead,			/* How much space is available in the +				 * buffer? */ +    int *errorCodePtr)		/* Where to store error code. */  { -    PipeState *psPtr = (PipeState *) instanceData; -    int bytesRead;			/* How many bytes were actually -                                         * read from the input device? */ +    PipeState *psPtr = instanceData; +    int bytesRead;		/* How many bytes were actually read from the +				 * input device? */      *errorCodePtr = 0; -     +      /*       * Assume there is always enough input available. This will block       * appropriately, and read will unblock as soon as a short read is       * possible, if the channel is in blocking mode. If the channel is -     * nonblocking, the read will never block. +     * nonblocking, the read will never block. Some OSes can throw an +     * interrupt error, for which we should immediately retry. [Bug #415131]       */ -    bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead); -    if (bytesRead > -1) { -        return bytesRead; +    do { +	bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead); +    } while ((bytesRead < 0) && (errno == EINTR)); + +    if (bytesRead < 0) { +	*errorCodePtr = errno; +	return -1;      } -    *errorCodePtr = errno; -    return -1; +    return bytesRead;  }  /* @@ -956,13 +1072,12 @@ PipeInputProc(instanceData, buf, toRead, errorCodePtr)   *   * PipeOutputProc--   * - *	This procedure is invoked from the generic IO level to write - *	output to a command pipeline based channel. + *	This function is invoked from the generic IO level to write output to + *	a command pipeline based channel.   *   * Results: - *	The number of bytes written is returned or -1 on error. An - *	output argument	contains a POSIX error code if an error occurred, - *	or zero. + *	The number of bytes written is returned or -1 on error. An output + *	argument contains a POSIX error code if an error occurred, or zero.   *   * Side effects:   *	Writes output on the output device of the channel. @@ -971,22 +1086,31 @@ PipeInputProc(instanceData, buf, toRead, errorCodePtr)   */  static int -PipeOutputProc(instanceData, buf, toWrite, errorCodePtr) -    ClientData instanceData;		/* Pipe state. */ -    char *buf;				/* The data buffer. */ -    int toWrite;			/* How many bytes to write? */ -    int *errorCodePtr;			/* Where to store error code. */ +PipeOutputProc( +    ClientData instanceData,	/* Pipe state. */ +    const char *buf,		/* The data buffer. */ +    int toWrite,		/* How many bytes to write? */ +    int *errorCodePtr)		/* Where to store error code. */  { -    PipeState *psPtr = (PipeState *) instanceData; +    PipeState *psPtr = instanceData;      int written;      *errorCodePtr = 0; -    written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite); -    if (written > -1) { -        return written; + +    /* +     * Some OSes can throw an interrupt error, for which we should immediately +     * retry. [Bug #415131] +     */ + +    do { +	written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite); +    } while ((written < 0) && (errno == EINTR)); + +    if (written < 0) { +	*errorCodePtr = errno; +	return -1;      } -    *errorCodePtr = errno; -    return -1; +    return written;  }  /* @@ -1000,28 +1124,27 @@ PipeOutputProc(instanceData, buf, toWrite, errorCodePtr)   *	None.   *   * Side effects: - *	Sets up the notifier so that a future event on the channel will - *	be seen by Tcl. + *	Sets up the notifier so that a future event on the channel will be + *	seen by Tcl.   *   *----------------------------------------------------------------------   */  static void -PipeWatchProc(instanceData, mask) -    ClientData instanceData;		/* The pipe state. */ -    int mask;				/* Events of interest; an OR-ed -                                         * combination of TCL_READABLE, -                                         * TCL_WRITABEL and TCL_EXCEPTION. */ +PipeWatchProc( +    ClientData instanceData,	/* The pipe state. */ +    int mask)			/* Events of interest; an OR-ed combination of +				 * TCL_READABLE, TCL_WRITABLE and +				 * TCL_EXCEPTION. */  { -    PipeState *psPtr = (PipeState *) instanceData; +    PipeState *psPtr = instanceData;      int newmask;      if (psPtr->inFile) {  	newmask = mask & (TCL_READABLE | TCL_EXCEPTION);  	if (newmask) {  	    Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask, -		    (Tcl_FileProc *) Tcl_NotifyChannel, -		    (ClientData) psPtr->channel); +		    (Tcl_FileProc *) Tcl_NotifyChannel, psPtr->channel);  	} else {  	    Tcl_DeleteFileHandler(GetFd(psPtr->inFile));  	} @@ -1030,8 +1153,7 @@ PipeWatchProc(instanceData, mask)  	newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);  	if (newmask) {  	    Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask, -		    (Tcl_FileProc *) Tcl_NotifyChannel, -		    (ClientData) psPtr->channel); +		    (Tcl_FileProc *) Tcl_NotifyChannel, psPtr->channel);  	} else {  	    Tcl_DeleteFileHandler(GetFd(psPtr->outFile));  	} @@ -1043,12 +1165,12 @@ PipeWatchProc(instanceData, mask)   *   * PipeGetHandleProc --   * - *	Called from Tcl_GetChannelHandle to retrieve OS handles from - *	inside a command pipeline based channel. + *	Called from Tcl_GetChannelHandle to retrieve OS handles from inside a + *	command pipeline based channel.   *   * Results: - *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if - *	there is no handle for the specified direction.  + *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no + *	handle for the specified direction.   *   * Side effects:   *	None. @@ -1057,19 +1179,19 @@ PipeWatchProc(instanceData, mask)   */  static int -PipeGetHandleProc(instanceData, direction, handlePtr) -    ClientData instanceData;	/* The pipe state. */ -    int direction;		/* TCL_READABLE or TCL_WRITABLE */ -    ClientData *handlePtr;	/* Where to store the handle.  */ +PipeGetHandleProc( +    ClientData instanceData,	/* The pipe state. */ +    int direction,		/* TCL_READABLE or TCL_WRITABLE */ +    ClientData *handlePtr)	/* Where to store the handle. */  { -    PipeState *psPtr = (PipeState *) instanceData; +    PipeState *psPtr = instanceData;      if (direction == TCL_READABLE && psPtr->inFile) { -	*handlePtr = (ClientData) GetFd(psPtr->inFile); +	*handlePtr = INT2PTR(GetFd(psPtr->inFile));  	return TCL_OK;      }      if (direction == TCL_WRITABLE && psPtr->outFile) { -	*handlePtr = (ClientData) GetFd(psPtr->outFile); +	*handlePtr = INT2PTR(GetFd(psPtr->outFile));  	return TCL_OK;      }      return TCL_ERROR; @@ -1092,19 +1214,18 @@ PipeGetHandleProc(instanceData, direction, handlePtr)   */  Tcl_Pid -Tcl_WaitPid(pid, statPtr, options) -    Tcl_Pid pid; -    int *statPtr; -    int options; +Tcl_WaitPid( +    Tcl_Pid pid, +    int *statPtr, +    int options)  {      int result; -    pid_t real_pid; +    pid_t real_pid = (pid_t) PTR2INT(pid); -    real_pid = (pid_t) pid;      while (1) {  	result = (int) waitpid(real_pid, statPtr, options);  	if ((result != -1) || (errno != EINTR)) { -	    return (Tcl_Pid) result; +	    return (Tcl_Pid) INT2PTR(result);  	}      }  } @@ -1114,8 +1235,8 @@ Tcl_WaitPid(pid, statPtr, options)   *   * Tcl_PidObjCmd --   * - *	This procedure is invoked to process the "pid" Tcl command. - *	See the user documentation for details on what it does. + *	This function is invoked to process the "pid" Tcl command. See the + *	user documentation for details on what it does.   *   * Results:   *	A standard Tcl result. @@ -1128,39 +1249,77 @@ Tcl_WaitPid(pid, statPtr, options)  	/* ARGSUSED */  int -Tcl_PidObjCmd(dummy, interp, objc, objv) -    ClientData dummy;		/* Not used. */ -    Tcl_Interp *interp;		/* Current interpreter. */ -    int objc;			/* Number of arguments. */ -    Tcl_Obj *CONST *objv;	/* Argument strings. */ +Tcl_PidObjCmd( +    ClientData dummy,		/* Not used. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const *objv)	/* Argument strings. */  {      Tcl_Channel chan; -    Tcl_ChannelType *chanTypePtr;      PipeState *pipePtr;      int i; -    Tcl_Obj *resultPtr, *longObjPtr; +    Tcl_Obj *resultPtr;      if (objc > 2) {  	Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");  	return TCL_ERROR;      } +      if (objc == 1) { -	Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid()); +	Tcl_SetObjResult(interp, Tcl_NewLongObj((long) getpid()));      } else { -        chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); -        if (chan == (Tcl_Channel) NULL) { +	/* +	 * Get the channel and make sure that it refers to a pipe. +	 */ + +	chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); +	if (chan == NULL) {  	    return TCL_ERROR;  	} -	chanTypePtr = Tcl_GetChannelType(chan); -	if (chanTypePtr != &pipeChannelType) { +	if (Tcl_GetChannelType(chan) != &pipeChannelType) {  	    return TCL_OK;  	} -        pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); -	resultPtr = Tcl_GetObjResult(interp); -        for (i = 0; i < pipePtr->numPids; i++) { -	    longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i])); -	    Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr); + +	/* +	 * Extract the process IDs from the pipe structure. +	 */ + +	pipePtr = Tcl_GetChannelInstanceData(chan); +	resultPtr = Tcl_NewObj(); +	for (i = 0; i < pipePtr->numPids; i++) { +	    Tcl_ListObjAppendElement(NULL, resultPtr, +		    Tcl_NewIntObj(PTR2INT(TclpGetPid(pipePtr->pidPtr[i]))));  	} +	Tcl_SetObjResult(interp, resultPtr);      }      return TCL_OK;  } + +/* + *---------------------------------------------------------------------- + * + * TclpFinalizePipes -- + * + *	Cleans up the pipe subsystem from Tcl_FinalizeThread + * + * Results: + *	None. + * + * Notes: + *	This function carries out no operation on Unix. + * + *---------------------------------------------------------------------- + */ + +void +TclpFinalizePipes(void) +{ +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
