os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/unix/tclUnixPipe.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* 
     2  * tclUnixPipe.c --
     3  *
     4  *	This file implements the UNIX-specific exec pipeline functions,
     5  *	the "pipe" channel driver, and the "pid" Tcl command.
     6  *
     7  * Copyright (c) 1991-1994 The Regents of the University of California.
     8  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
     9  * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.  
    10  *
    11  * See the file "license.terms" for information on usage and redistribution
    12  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    13  *
    14  * RCS: @(#) $Id: tclUnixPipe.c,v 1.23.2.7 2006/08/02 20:04:40 das Exp $
    15  */
    16 
    17 #include "tclInt.h"
    18 #include "tclPort.h"
    19 
    20 #if defined(__SYMBIAN32__)    
    21 #include "tclSymbianGlobals.h"
    22 #include <spawn.h>
    23 #define ADDPARAMTOCHILD 4
    24 #endif
    25 
    26 void TclPrint1(const char* aFmt, const char* aStr);
    27 void TclPrint2(const char* aFmt, const char* aStr, int aNum);
    28 void TclPrint3(const char* aFmt);
    29 
    30 
    31 #ifdef USE_VFORK
    32 #define fork vfork
    33 #endif
    34 
    35 /*
    36  * The following macros convert between TclFile's and fd's.  The conversion
    37  * simple involves shifting fd's up by one to ensure that no valid fd is ever
    38  * the same as NULL.
    39  */
    40 
    41 #define MakeFile(fd) ((TclFile)(((int)fd)+1))
    42 #define GetFd(file) (((int)file)-1)
    43 /*
    44  * This structure describes per-instance state of a pipe based channel.
    45  */
    46 
    47 typedef struct PipeState {
    48     Tcl_Channel channel;/* Channel associated with this file. */
    49     TclFile inFile;	/* Output from pipe. */
    50     TclFile outFile;	/* Input to pipe. */
    51     TclFile errorFile;	/* Error output from pipe. */
    52     int numPids;	/* How many processes are attached to this pipe? */
    53     Tcl_Pid *pidPtr;	/* The process IDs themselves. Allocated by
    54                          * the creator of the pipe. */
    55     int isNonBlocking;	/* Nonzero when the pipe is in nonblocking mode.
    56                          * Used to decide whether to wait for the children
    57                          * at close time. */
    58 } PipeState;
    59 
    60 /*
    61  * Declarations for local procedures defined in this file:
    62  */
    63 
    64 static int	PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData,
    65 		    int mode));
    66 static int	PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
    67 		    Tcl_Interp *interp));
    68 static int	PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData,
    69 		    int direction, ClientData *handlePtr));
    70 static int	PipeInputProc _ANSI_ARGS_((ClientData instanceData,
    71 		    char *buf, int toRead, int *errorCode));
    72 static int	PipeOutputProc _ANSI_ARGS_((
    73 		    ClientData instanceData, CONST char *buf, int toWrite,
    74 		    int *errorCode));
    75 static void	PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask));
    76 static void	RestoreSignals _ANSI_ARGS_((void));
    77 #ifndef __SYMBIAN32__
    78 static int	SetupStdFile _ANSI_ARGS_((TclFile file, int type));
    79 #endif
    80 /*
    81  * This structure describes the channel type structure for command pipe
    82  * based IO:
    83  */
    84 
    85 static Tcl_ChannelType pipeChannelType = {
    86     "pipe",			/* Type name. */
    87     TCL_CHANNEL_VERSION_4,	/* v4 channel */
    88     PipeCloseProc,		/* Close proc. */
    89     PipeInputProc,		/* Input proc. */
    90     PipeOutputProc,		/* Output proc. */
    91     NULL,			/* Seek proc. */
    92     NULL,			/* Set option proc. */
    93     NULL,			/* Get option proc. */
    94     PipeWatchProc,		/* Initialize notifier. */
    95     PipeGetHandleProc,		/* Get OS handles out of channel. */
    96     NULL,			/* close2proc. */
    97     PipeBlockModeProc,		/* Set blocking or non-blocking mode.*/
    98     NULL,			/* flush proc. */
    99     NULL,			/* handler proc. */
   100     NULL,                       /* wide seek proc */
   101     NULL,                       /* thread action proc */
   102 };
   103 
   104 /*
   105  *----------------------------------------------------------------------
   106  *
   107  * TclpMakeFile --
   108  *
   109  *	Make a TclFile from a channel.
   110  *
   111  * Results:
   112  *	Returns a new TclFile or NULL on failure.
   113  *
   114  * Side effects:
   115  *	None.
   116  *
   117  *----------------------------------------------------------------------
   118  */
   119 
   120 TclFile
   121 TclpMakeFile(channel, direction)
   122     Tcl_Channel channel;	/* Channel to get file from. */
   123     int direction;		/* Either TCL_READABLE or TCL_WRITABLE. */
   124 {
   125     ClientData data;
   126 
   127     if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &data)
   128 	    == TCL_OK) {
   129 	return MakeFile((int)data);
   130     } else {
   131 	return (TclFile) NULL;
   132     }
   133 }
   134 
   135 /*
   136  *----------------------------------------------------------------------
   137  *
   138  * TclpOpenFile --
   139  *
   140  *	Open a file for use in a pipeline.  
   141  *
   142  * Results:
   143  *	Returns a new TclFile handle or NULL on failure.
   144  *
   145  * Side effects:
   146  *	May cause a file to be created on the file system.
   147  *
   148  *----------------------------------------------------------------------
   149  */
   150 
   151 TclFile
   152 TclpOpenFile(fname, mode)
   153     CONST char *fname;		/* The name of the file to open. */
   154     int mode;			/* In what mode to open the file? */
   155 {
   156     int fd;
   157     CONST char *native;
   158     Tcl_DString ds;
   159 
   160     native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds);
   161     fd = TclOSopen(native, mode, 0666);			/* INTL: Native. */
   162     Tcl_DStringFree(&ds);
   163     if (fd != -1) {
   164         fcntl(fd, F_SETFD, FD_CLOEXEC);
   165 
   166 	/*
   167 	 * If the file is being opened for writing, seek to the end
   168 	 * so we can append to any data already in the file.
   169 	 */
   170 
   171 	if ((mode & O_WRONLY) && !(mode & O_APPEND)) {
   172 	    TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END);
   173 	}
   174 
   175 	/*
   176 	 * Increment the fd so it can't be 0, which would conflict with
   177 	 * the NULL return for errors.
   178 	 */
   179 
   180 	return MakeFile(fd);
   181     }
   182     return NULL;
   183 }
   184 
   185 /*
   186  *----------------------------------------------------------------------
   187  *
   188  * TclpCreateTempFile --
   189  *
   190  *	This function creates a temporary file initialized with an
   191  *	optional string, and returns a file handle with the file pointer
   192  *	at the beginning of the file.
   193  *
   194  * Results:
   195  *	A handle to a file.
   196  *
   197  * Side effects:
   198  *	None.
   199  *
   200  *----------------------------------------------------------------------
   201  */
   202 
   203 TclFile
   204 TclpCreateTempFile(contents)
   205     CONST char *contents;	/* String to write into temp file, or NULL. */
   206 {
   207     char fileName[L_tmpnam + 9];
   208     CONST char *native;
   209     Tcl_DString dstring;
   210     int fd;
   211 
   212     /*
   213      * We should also check against making more then TMP_MAX of these.
   214      */
   215 
   216     // Symbian doesn't have a default temp directory, so we use cwd (root of private data cage)
   217 #ifdef __SYMBIAN32__  
   218     if (getcwd(fileName, L_tmpnam) == NULL) 
   219     	{	
   220     	return NULL;
   221         }
   222 	/* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */
   223 	fileName[0] = 'c';
   224 #else
   225     strcpy(fileName, P_tmpdir);		/* INTL: Native. */
   226 #endif
   227     if (fileName[strlen(fileName) - 1] != '/') {
   228 #ifdef __SYMBIAN32__  
   229 	strcat(fileName, "\\");				
   230 #else
   231 	strcat(fileName, "/");				/* INTL: Native. */
   232 #endif
   233     }
   234     strcat(fileName, "tclXXXXXX");
   235     fd = mkstemp(fileName);				/* INTL: Native. */
   236     if (fd == -1) {
   237 	return NULL;
   238     }
   239     fcntl(fd, F_SETFD, FD_CLOEXEC);
   240 #ifdef __SYMBIAN32__  
   241     strcpy(tmpFileName, fileName);	
   242 #endif
   243     unlink(fileName);					/* INTL: Native. */
   244 
   245     if (contents != NULL) {
   246 	native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
   247 	if (write(fd, native, strlen(native)) == -1) {
   248 	    close(fd);
   249 	    Tcl_DStringFree(&dstring);
   250 	    return NULL;
   251 	}
   252 	Tcl_DStringFree(&dstring);
   253 	TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET);
   254     }
   255     return MakeFile(fd);
   256 }
   257 
   258 /*
   259  *----------------------------------------------------------------------
   260  *
   261  * TclpTempFileName --
   262  *
   263  *	This function returns unique filename.
   264  *
   265  * Results:
   266  *	Returns a valid Tcl_Obj* with refCount 0, or NULL on failure.
   267  *
   268  * Side effects:
   269  *	None.
   270  *
   271  *----------------------------------------------------------------------
   272  */
   273 
   274 Tcl_Obj* 
   275 TclpTempFileName()
   276 {
   277     char fileName[L_tmpnam + 9];
   278     Tcl_Obj *result = NULL;
   279     int fd;
   280 
   281     /*
   282      * We should also check against making more then TMP_MAX of these.
   283      */
   284 
   285     // Symbian doesn't have a default temp directory, so we use cwd (root of private data cage)
   286 #ifdef __SYMBIAN32__  
   287     if (getcwd(fileName, L_tmpnam) == NULL) 
   288     	{	
   289     	return NULL;
   290         }
   291 	/* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */
   292 	fileName[0] = 'c';
   293 #else
   294     strcpy(fileName, P_tmpdir);		/* INTL: Native. */
   295 #endif
   296     if (fileName[strlen(fileName) - 1] != '/') {
   297 #ifdef __SYMBIAN32__  
   298 	strcat(fileName, "\\");				
   299 #else
   300 	strcat(fileName, "/");				/* INTL: Native. */
   301 #endif
   302     }
   303     strcat(fileName, "tclXXXXXX");
   304     fd = mkstemp(fileName);		/* INTL: Native. */
   305     if (fd == -1) {
   306 	return NULL;
   307     }
   308     fcntl(fd, F_SETFD, FD_CLOEXEC);
   309     unlink(fileName);			/* INTL: Native. */
   310 
   311     result = TclpNativeToNormalized((ClientData) fileName);
   312     close (fd);
   313     return result;
   314 }
   315 
   316 /*
   317  *----------------------------------------------------------------------
   318  *
   319  * TclpCreatePipe --
   320  *
   321  *      Creates a pipe - simply calls the pipe() function.
   322  *
   323  * Results:
   324  *      Returns 1 on success, 0 on failure. 
   325  *
   326  * Side effects:
   327  *      Creates a pipe.
   328  *
   329  *----------------------------------------------------------------------
   330  */
   331 
   332 int
   333 TclpCreatePipe(readPipe, writePipe)
   334     TclFile *readPipe;		/* Location to store file handle for
   335 				 * read side of pipe. */
   336     TclFile *writePipe;		/* Location to store file handle for
   337 				 * write side of pipe. */
   338 {
   339     int pipeIds[2];
   340 
   341     if (pipe(pipeIds) != 0) {
   342 	return 0;
   343     }
   344 
   345     fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
   346     fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
   347 
   348     *readPipe = MakeFile(pipeIds[0]);
   349     *writePipe = MakeFile(pipeIds[1]);
   350     return 1;
   351 }
   352 
   353 /*
   354  *----------------------------------------------------------------------
   355  *
   356  * TclpCloseFile --
   357  *
   358  *	Implements a mechanism to close a UNIX file.
   359  *
   360  * Results:
   361  *	Returns 0 on success, or -1 on error, setting errno.
   362  *
   363  * Side effects:
   364  *	The file is closed.
   365  *
   366  *----------------------------------------------------------------------
   367  */
   368 
   369 int
   370 TclpCloseFile(file)
   371     TclFile file;	/* The file to close. */
   372 {
   373     int fd = GetFd(file);
   374 
   375     /*
   376      * Refuse to close the fds for stdin, stdout and stderr.
   377      */
   378     
   379     if ((fd == 0) || (fd == 1) || (fd == 2)) {
   380         return 0;
   381     }
   382     
   383     Tcl_DeleteFileHandler(fd);
   384     return close(fd);
   385 }
   386 
   387 /*
   388  *---------------------------------------------------------------------------
   389  *
   390  * TclpCreateProcess --
   391  *
   392  *	Create a child process that has the specified files as its 
   393  *	standard input, output, and error.  The child process runs
   394  *	asynchronously and runs with the same environment variables
   395  *	as the creating process.
   396  *
   397  *	The path is searched to find the specified executable.  
   398  *
   399  * Results:
   400  *	The return value is TCL_ERROR and an error message is left in
   401  *	the interp's result if there was a problem creating the child 
   402  *	process.  Otherwise, the return value is TCL_OK and *pidPtr is
   403  *	filled with the process id of the child process.
   404  * 
   405  * Side effects:
   406  *	A process is created.
   407  *	
   408  *---------------------------------------------------------------------------
   409  */
   410 
   411     /* ARGSUSED */
   412 int
   413 TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, 
   414 	pidPtr)
   415     Tcl_Interp *interp;		/* Interpreter in which to leave errors that
   416 				 * occurred when creating the child process.
   417 				 * Error messages from the child process
   418 				 * itself are sent to errorFile. */
   419     int argc;			/* Number of arguments in following array. */
   420     CONST char **argv;		/* Array of argument strings in UTF-8.
   421 				 * argv[0] contains the name of the executable
   422 				 * translated using Tcl_TranslateFileName
   423 				 * call).  Additional arguments have not been
   424 				 * converted. */
   425     TclFile inputFile;		/* If non-NULL, gives the file to use as
   426 				 * input for the child process.  If inputFile
   427 				 * file is not readable or is NULL, the child
   428 				 * will receive no standard input. */
   429     TclFile outputFile;		/* If non-NULL, gives the file that
   430 				 * receives output from the child process.  If
   431 				 * outputFile file is not writeable or is
   432 				 * NULL, output from the child will be
   433 				 * discarded. */
   434     TclFile errorFile;		/* If non-NULL, gives the file that
   435 				 * receives errors from the child process.  If
   436 				 * errorFile file is not writeable or is NULL,
   437 				 * errors from the child will be discarded.
   438 				 * errorFile may be the same as outputFile. */
   439     Tcl_Pid *pidPtr;		/* If this procedure is successful, pidPtr
   440 				 * is filled with the process id of the child
   441 				 * process. */
   442 {
   443     TclFile errPipeIn, errPipeOut;
   444     int count, status, fd;
   445     char errSpace[200 + TCL_INTEGER_SPACE];
   446     Tcl_DString *dsArray;
   447     char **newArgv = 0;
   448     int pid, i;
   449 #ifdef __SYMBIAN32__     
   450 	int fd1;
   451     int RetVal;
   452     char buf[256];
   453     int fifoResult;
   454     struct timeval tv;
   455     fd_set readfds;
   456 #endif    
   457     
   458     errPipeIn = NULL;
   459     errPipeOut = NULL;
   460     pid = -1;
   461 
   462     /*
   463      * Create a pipe that the child can use to return error
   464      * information if anything goes wrong.
   465      */
   466 #ifdef __SYMBIAN32__     
   467     // change the communication between parent and child process, it just report the failure of child process creation
   468 
   469     tmpnam(fifoFileName);
   470 	fifoResult = mkfifo(fifoFileName,S_IXGRP);
   471 	if(fifoResult == -1)
   472    	{
   473     	//fifo creation failure.
   474    		fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
   475     	goto error;
   476    	}
   477    	else
   478    	{
   479    		int ReadFifoFd  = open(fifoFileName,O_RDONLY | O_NONBLOCK);
   480  
   481        	if(ReadFifoFd  == -1)
   482        	{
   483        		 //Failed to open the Fifo
   484     	   	 printf("\n*** failure Fifo Open ***\n");
   485            	 goto error;
   486         }
   487         else
   488         {
   489         	 errPipeIn = MakeFile(ReadFifoFd);
   490         }
   491    	}
   492    	
   493     //set the stdin,stdout,stderr and pipeid to the child process. the fds are passed to the posix_spawn() in argv
   494 	argc = argc + ADDPARAMTOCHILD;  
   495    	
   496 #else   		
   497    	if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) {
   498 	Tcl_AppendResult(interp, "couldn't create pipe: ",
   499 		Tcl_PosixError(interp), (char *) NULL);
   500 	goto error;
   501     }
   502 #endif    
   503 
   504     /*
   505      * We need to allocate and convert this before the fork
   506      * so it is properly deallocated later
   507      */
   508     dsArray = (Tcl_DString *) ckalloc(argc * sizeof(Tcl_DString));
   509     newArgv = (char **) ckalloc((argc+1) * sizeof(char *));
   510     newArgv[argc] = NULL;
   511 #ifdef __SYMBIAN32__         
   512     for (i = 0; i < (argc-ADDPARAMTOCHILD); i++) {
   513 #else
   514     for (i = 0; i < argc; i++) {
   515 #endif    
   516 	newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]);
   517     }
   518   
   519 #ifdef __SYMBIAN32__           
   520 	fprintf(stderr,"\r\n");
   521     //set the stdin,stdout,stderr to the child process. the fds pass to the posix_spawn() in argv
   522     for (i = argc-ADDPARAMTOCHILD; i < argc; i++) 
   523     {
   524 	    if(i == (argc-ADDPARAMTOCHILD))
   525 	    	{
   526     		strcpy(buf,fifoFileName);
   527     		fprintf(stderr,"fifoFileName is %s\r\n", fifoFileName);
   528 	    	}	
   529 	    else if(i == (argc-ADDPARAMTOCHILD+1))
   530 	    	{
   531 	    	if (inputFile)
   532 	    		{
   533 	    		strcpy(buf,inFileName);
   534 	    		}
   535 	    	else
   536 	    		{
   537 	    		strcpy(buf,"STD");
   538 	    		}
   539 			fprintf(stderr, "inFileName is %s\r\n", inFileName);
   540 	    	}
   541     	else if(i == (argc-ADDPARAMTOCHILD+2))
   542     		{
   543 	    	if (outputFile)
   544 	    		{
   545 	    		strcpy(buf,outFileName);
   546 	    		}
   547 	    	else
   548 	    		{
   549 	    		strcpy(buf,"STD");
   550 	    		}
   551 			fprintf(stderr, "outFileName is %s\r\n", outFileName);
   552     		}
   553     	else if(i == (argc-ADDPARAMTOCHILD+3))
   554     		{
   555 	    	if (errorFile)
   556 	    		{
   557 	    		strcpy(buf,errFileName);
   558 	    		}
   559 	    	else
   560 	    		{
   561 	    		strcpy(buf,"STD");
   562 	    		}
   563     		fprintf(stderr, "errFileName is %s\r\n", errFileName);
   564     		}
   565 
   566 		newArgv[i] = Tcl_UtfToExternalDString(NULL, buf, -1, &dsArray[i]);
   567     }
   568 #endif    
   569 #ifdef USE_VFORK
   570     /*
   571      * After vfork(), do not call code in the child that changes global state,
   572      * because it is using the parent's memory space at that point and writes
   573      * might corrupt the parent: so ensure standard channels are initialized in
   574      * the parent, otherwise SetupStdFile() might initialize them in the child.
   575      */
   576     if (!inputFile) {
   577 	Tcl_GetStdChannel(TCL_STDIN);
   578     }
   579     if (!outputFile) {
   580         Tcl_GetStdChannel(TCL_STDOUT);
   581     }
   582     if (!errorFile) {
   583         Tcl_GetStdChannel(TCL_STDERR);
   584     }
   585 #endif
   586 	TclPrint1(" == TclpCreateProcess(), posix_spawn(), process %S\r\n", newArgv[0]);
   587 #ifdef __SYMBIAN32__           
   588     // change fork() using posix_spawn()
   589 	if (argc > 1)
   590 	{
   591     	RetVal= posix_spawn(&pid,newArgv[0],NULL,NULL,newArgv,NULL);  
   592 	}
   593     else
   594     {
   595     	RetVal= posix_spawn(&pid,newArgv[0],NULL,NULL,NULL,NULL);  
   596     }
   597     if (RetVal != 0)
   598     {
   599     	pid = -1;  //if error, the value of pid is unspecified.  ensure we still report the error.	
   600     }
   601 #else    
   602     pid = fork();
   603     if (pid == 0) {
   604 	int joinThisError = errorFile && (errorFile == outputFile);
   605 
   606 	fd = GetFd(errPipeOut);
   607 
   608 	/*
   609 	 *  Set up stdio file handles for the child process.
   610 	 */
   611 
   612 	if (!SetupStdFile(inputFile, TCL_STDIN)
   613 		|| !SetupStdFile(outputFile, TCL_STDOUT)
   614 		|| (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
   615 		|| (joinThisError &&
   616 			((dup2(1,2) == -1) ||
   617 			 (fcntl(2, F_SETFD, 0) != 0)))) {
   618 	    sprintf(errSpace, "forked process couldn't set up input/output, error: %d\r\n", errno);
   619 	    write(fd, errSpace, (size_t) strlen(errSpace));
   620 	    _exit(1);
   621 	}
   622 
   623 	/*
   624 	 * Close the input side of the error pipe.
   625 	 */
   626 
   627 	RestoreSignals();
   628 	execvp(newArgv[0], newArgv);			/* INTL: Native. */
   629 	sprintf(errSpace, "couldn't execute \"%.150s\", error: %d\r\n", argv[0], errno);
   630 	write(fd, errSpace, (size_t) strlen(errSpace));
   631 	_exit(1);
   632     }
   633 #endif
   634 	TclPrint2(" == TclpCreateProcess(), posix_spawn(), process %S, pid %d\r\n", newArgv[0], (int)pid);
   635     
   636     //
   637     // Free the mem we used for the fork
   638     //
   639     for (i = 0; i < argc; i++) {
   640 	Tcl_DStringFree(&dsArray[i]);
   641     }
   642     ckfree((char *) dsArray);
   643     ckfree((char *) newArgv);
   644 
   645     if (pid == -1) {
   646 #ifdef __SYMBIAN32__               
   647 	Tcl_AppendResult(interp, "couldn't posix_spawn child process: ",
   648 #else
   649 	Tcl_AppendResult(interp, "couldn't fork child process: ",
   650 #endif	
   651 		Tcl_PosixError(interp), (char *) NULL);
   652 	goto error;
   653     }
   654 
   655     /*
   656      * Read back from the error pipe to see if the child started
   657      * up OK.  The info in the pipe (if any) consists of a decimal
   658      * errno value followed by an error message.
   659      */
   660     //*
   661 #ifdef __SYMBIAN32__               
   662     TclpCloseFile(errPipeOut);
   663     errPipeOut = NULL;
   664 #endif
   665     fd = GetFd(errPipeIn);
   666 #ifdef __SYMBIAN32__ 
   667 	TclPrint3(" == TclpCreateProcess(), select()\r\n");
   668     tv.tv_sec = 3;
   669     tv.tv_usec = 0;    
   670     
   671     FD_ZERO(&readfds);
   672     FD_SET(fd, &readfds);
   673     // Use select to wait for child process
   674     i = select(fd+1, &readfds, NULL, NULL,&tv);    
   675     if ( i != -1 && FD_ISSET(fd, &readfds))    
   676         {
   677         count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
   678 	    if (count > 0) {
   679 		char *end;
   680 		errSpace[count] = 0;
   681 		errno = strtol(errSpace, &end, 10);
   682 		if (errno != 0)
   683 			{
   684 			Tcl_AppendResult(interp, end, Tcl_PosixError(interp),
   685 				(char *) NULL);
   686 			goto error;
   687 			}
   688 	    }
   689         }
   690     else
   691     	{
   692     	// Process not started properly
   693     	Tcl_AppendResult(interp, "couldn't read error info from child process: ",
   694     			Tcl_PosixError(interp), (char *) NULL);
   695     	goto error;
   696     	}
   697 #else
   698     count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
   699     if (count > 0) {
   700 	char *end;
   701 	errSpace[count] = 0;
   702 	errno = strtol(errSpace, &end, 10);
   703 	Tcl_AppendResult(interp, end, Tcl_PosixError(interp),
   704 		(char *) NULL);
   705 	goto error;
   706     }
   707 #endif    
   708     TclpCloseFile(errPipeIn);
   709 #ifdef __SYMBIAN32__ 
   710     unlink(fifoFileName);
   711 #endif    
   712     *pidPtr = (Tcl_Pid) pid;
   713 	TclPrint3(" == TclpCreateProcess(), TCL_OK\r\n");
   714     return TCL_OK;
   715 
   716     error:
   717 	TclPrint2(" == TclpCreateProcess(), TCL_ERROR,%S errno %d\r\n", "", errno);
   718     if (pid != -1) {
   719 	/*
   720 	 * Reap the child process now if an error occurred during its
   721 	 * startup.  We don't call this with WNOHANG because that can lead to
   722 	 * defunct processes on an MP system.   We shouldn't have to worry
   723 	 * about hanging here, since this is the error case.  [Bug: 6148]
   724 	 */
   725 
   726 	Tcl_WaitPid((Tcl_Pid) pid, &status, 0);
   727     }
   728 
   729     if (errPipeIn) {
   730 	TclpCloseFile(errPipeIn);
   731     }
   732     if (errPipeOut) {
   733 	TclpCloseFile(errPipeOut);
   734     }
   735 #ifdef __SYMBIAN32__ 
   736     unlink(fifoFileName);
   737 #endif    
   738 
   739     return TCL_ERROR;
   740 }
   741 
   742 /*
   743  *----------------------------------------------------------------------
   744  *
   745  * RestoreSignals --
   746  *
   747  *      This procedure is invoked in a forked child process just before
   748  *      exec-ing a new program to restore all signals to their default
   749  *      settings.
   750  *
   751  * Results:
   752  *      None.
   753  *
   754  * Side effects:
   755  *      Signal settings get changed.
   756  *
   757  *----------------------------------------------------------------------
   758  */
   759  
   760 static void
   761 RestoreSignals()
   762 {
   763 // Symbian & PIPS don't support signals.
   764 #ifndef __SYMBIAN32__               
   765 #ifdef SIGABRT
   766     signal(SIGABRT, SIG_DFL);
   767 #endif
   768 #ifdef SIGALRM
   769     signal(SIGALRM, SIG_DFL);
   770 #endif
   771 #ifdef SIGFPE
   772     signal(SIGFPE, SIG_DFL);
   773 #endif
   774 #ifdef SIGHUP
   775     signal(SIGHUP, SIG_DFL);
   776 #endif
   777 #ifdef SIGILL
   778     signal(SIGILL, SIG_DFL);
   779 #endif
   780 #ifdef SIGINT
   781     signal(SIGINT, SIG_DFL);
   782 #endif
   783 #ifdef SIGPIPE
   784     signal(SIGPIPE, SIG_DFL);
   785 #endif
   786 #ifdef SIGQUIT
   787     signal(SIGQUIT, SIG_DFL);
   788 #endif
   789 #ifdef SIGSEGV
   790     signal(SIGSEGV, SIG_DFL);
   791 #endif
   792 #ifdef SIGTERM
   793     signal(SIGTERM, SIG_DFL);
   794 #endif
   795 #ifdef SIGUSR1
   796     signal(SIGUSR1, SIG_DFL);
   797 #endif
   798 #ifdef SIGUSR2
   799     signal(SIGUSR2, SIG_DFL);
   800 #endif
   801 #ifdef SIGCHLD
   802     signal(SIGCHLD, SIG_DFL);
   803 #endif
   804 #ifdef SIGCONT
   805     signal(SIGCONT, SIG_DFL);
   806 #endif
   807 #ifdef SIGTSTP
   808     signal(SIGTSTP, SIG_DFL);
   809 #endif
   810 #ifdef SIGTTIN
   811     signal(SIGTTIN, SIG_DFL);
   812 #endif
   813 #ifdef SIGTTOU
   814     signal(SIGTTOU, SIG_DFL);
   815 #endif
   816 #endif
   817 }
   818 
   819 /*
   820  *----------------------------------------------------------------------
   821  *
   822  * SetupStdFile --
   823  *
   824  *	Set up stdio file handles for the child process, using the
   825  *	current standard channels if no other files are specified.
   826  *	If no standard channel is defined, or if no file is associated
   827  *	with the channel, then the corresponding standard fd is closed.
   828  *
   829  * Results:
   830  *	Returns 1 on success, or 0 on failure.
   831  *
   832  * Side effects:
   833  *	Replaces stdio fds.
   834  *
   835  *----------------------------------------------------------------------
   836  */
   837 #ifdef __SYMBIAN32__               
   838 int 
   839 #else
   840 static int
   841 #endif
   842 SetupStdFile(file, type)
   843     TclFile file;		/* File to dup, or NULL. */
   844     int type;			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
   845 {
   846     Tcl_Channel channel;
   847     int fd;
   848     int targetFd = 0;		/* Initializations here needed only to */
   849     int direction = 0;		/* prevent warnings about using uninitialized
   850 				 * variables. */
   851 
   852     switch (type) {
   853 	case TCL_STDIN:
   854 	    targetFd = 0;
   855 	    direction = TCL_READABLE;
   856 	    break;
   857 	case TCL_STDOUT:
   858 	    targetFd = 1;
   859 	    direction = TCL_WRITABLE;
   860 	    break;
   861 	case TCL_STDERR:
   862 	    targetFd = 2;
   863 	    direction = TCL_WRITABLE;
   864 	    break;
   865     }
   866 
   867     if (!file) {
   868 	channel = Tcl_GetStdChannel(type);
   869 	if (channel) {
   870 	    file = TclpMakeFile(channel, direction);
   871 	}
   872     }
   873     if (file) {
   874 	fd = GetFd(file);
   875 	if (fd != targetFd) {
   876 	    if (dup2(fd, targetFd) == -1) {
   877 		return 0;
   878 	    }
   879 
   880             /*
   881              * Must clear the close-on-exec flag for the target FD, since
   882              * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on
   883              * the target FD.
   884              */
   885             
   886             fcntl(targetFd, F_SETFD, 0);
   887 	} else {
   888 	    /*
   889 	     * Since we aren't dup'ing the file, we need to explicitly clear
   890 	     * the close-on-exec flag.
   891 	     */
   892 
   893 	   fcntl(fd, F_SETFD, 0);
   894 	}
   895     } else {
   896 	close(targetFd);
   897     }
   898     return 1;
   899 }
   900 
   901 /*
   902  *----------------------------------------------------------------------
   903  *
   904  * TclpCreateCommandChannel --
   905  *
   906  *	This function is called by the generic IO level to perform
   907  *	the platform specific channel initialization for a command
   908  *	channel.
   909  *
   910  * Results:
   911  *	Returns a new channel or NULL on failure.
   912  *
   913  * Side effects:
   914  *	Allocates a new channel.
   915  *
   916  *----------------------------------------------------------------------
   917  */
   918 
   919 Tcl_Channel
   920 TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
   921     TclFile readFile;		/* If non-null, gives the file for reading. */
   922     TclFile writeFile;		/* If non-null, gives the file for writing. */
   923     TclFile errorFile;		/* If non-null, gives the file where errors
   924 				 * can be read. */
   925     int numPids;		/* The number of pids in the pid array. */
   926     Tcl_Pid *pidPtr;		/* An array of process identifiers.
   927                                  * Allocated by the caller, freed when
   928                                  * the channel is closed or the processes
   929                                  * are detached (in a background exec). */
   930 {
   931     char channelName[16 + TCL_INTEGER_SPACE];
   932     int channelId;
   933     PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
   934     int mode;
   935 
   936     statePtr->inFile = readFile;
   937     statePtr->outFile = writeFile;
   938     statePtr->errorFile = errorFile;
   939     statePtr->numPids = numPids;
   940     statePtr->pidPtr = pidPtr;
   941     statePtr->isNonBlocking = 0;
   942 
   943     mode = 0;
   944     if (readFile) {
   945         mode |= TCL_READABLE;
   946     }
   947     if (writeFile) {
   948         mode |= TCL_WRITABLE;
   949     }
   950     
   951     /*
   952      * Use one of the fds associated with the channel as the
   953      * channel id.
   954      */
   955 
   956     if (readFile) {
   957 	channelId = GetFd(readFile);
   958     } else if (writeFile) {
   959 	channelId = GetFd(writeFile);
   960     } else if (errorFile) {
   961 	channelId = GetFd(errorFile);
   962     } else {
   963 	channelId = 0;
   964     }
   965 
   966     /*
   967      * For backward compatibility with previous versions of Tcl, we
   968      * use "file%d" as the base name for pipes even though it would
   969      * be more natural to use "pipe%d".
   970      */
   971 
   972     sprintf(channelName, "file%d", channelId);
   973     statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
   974             (ClientData) statePtr, mode);
   975     return statePtr->channel;
   976 }
   977 
   978 /*
   979  *----------------------------------------------------------------------
   980  *
   981  * TclGetAndDetachPids --
   982  *
   983  *	This procedure is invoked in the generic implementation of a
   984  *	background "exec" (An exec when invoked with a terminating "&")
   985  *	to store a list of the PIDs for processes in a command pipeline
   986  *	in the interp's result and to detach the processes.
   987  *
   988  * Results:
   989  *	None.
   990  *
   991  * Side effects:
   992  *	Modifies the interp's result. Detaches processes.
   993  *
   994  *----------------------------------------------------------------------
   995  */
   996 
   997 void
   998 TclGetAndDetachPids(interp, chan)
   999     Tcl_Interp *interp;
  1000     Tcl_Channel chan;
  1001 {
  1002     PipeState *pipePtr;
  1003     Tcl_ChannelType *chanTypePtr;
  1004     int i;
  1005     char buf[TCL_INTEGER_SPACE];
  1006 
  1007     /*
  1008      * Punt if the channel is not a command channel.
  1009      */
  1010 
  1011     chanTypePtr = Tcl_GetChannelType(chan);
  1012     if (chanTypePtr != &pipeChannelType) {
  1013         return;
  1014     }
  1015 
  1016     pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
  1017     for (i = 0; i < pipePtr->numPids; i++) {
  1018         TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i]));
  1019         Tcl_AppendElement(interp, buf);
  1020         Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
  1021     }
  1022     if (pipePtr->numPids > 0) {
  1023         ckfree((char *) pipePtr->pidPtr);
  1024         pipePtr->numPids = 0;
  1025     }
  1026 }
  1027 
  1028 /*
  1029  *----------------------------------------------------------------------
  1030  *
  1031  * PipeBlockModeProc --
  1032  *
  1033  *	Helper procedure to set blocking and nonblocking modes on a
  1034  *	pipe based channel. Invoked by generic IO level code.
  1035  *
  1036  * Results:
  1037  *	0 if successful, errno when failed.
  1038  *
  1039  * Side effects:
  1040  *	Sets the device into blocking or non-blocking mode.
  1041  *
  1042  *----------------------------------------------------------------------
  1043  */
  1044 
  1045 	/* ARGSUSED */
  1046 static int
  1047 PipeBlockModeProc(instanceData, mode)
  1048     ClientData instanceData;		/* Pipe state. */
  1049     int mode;				/* The mode to set. Can be one of
  1050                                          * TCL_MODE_BLOCKING or
  1051                                          * TCL_MODE_NONBLOCKING. */
  1052 {
  1053     PipeState *psPtr = (PipeState *) instanceData;
  1054     int curStatus;
  1055     int fd;
  1056 
  1057 #ifndef	USE_FIONBIO    
  1058     if (psPtr->inFile) {
  1059         fd = GetFd(psPtr->inFile);
  1060         curStatus = fcntl(fd, F_GETFL);
  1061         if (mode == TCL_MODE_BLOCKING) {
  1062             curStatus &= (~(O_NONBLOCK));
  1063         } else {
  1064             curStatus |= O_NONBLOCK;
  1065         }
  1066         if (fcntl(fd, F_SETFL, curStatus) < 0) {
  1067             return errno;
  1068         }
  1069     }
  1070     if (psPtr->outFile) {
  1071         fd = GetFd(psPtr->outFile);
  1072         curStatus = fcntl(fd, F_GETFL);
  1073         if (mode == TCL_MODE_BLOCKING) {
  1074             curStatus &= (~(O_NONBLOCK));
  1075         } else {
  1076             curStatus |= O_NONBLOCK;
  1077         }
  1078         if (fcntl(fd, F_SETFL, curStatus) < 0) {
  1079             return errno;
  1080         }
  1081     }
  1082 #endif	/* !FIONBIO */
  1083 
  1084 #ifdef	USE_FIONBIO
  1085     if (psPtr->inFile) {
  1086         fd = GetFd(psPtr->inFile);
  1087         if (mode == TCL_MODE_BLOCKING) {
  1088             curStatus = 0;
  1089         } else {
  1090             curStatus = 1;
  1091         }
  1092         if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
  1093             return errno;
  1094         }
  1095     }
  1096     if (psPtr->outFile != NULL) {
  1097         fd = GetFd(psPtr->outFile);
  1098         if (mode == TCL_MODE_BLOCKING) {
  1099             curStatus = 0;
  1100         } else {
  1101             curStatus = 1;
  1102         }
  1103         if (ioctl(fd, (int) FIONBIO,  &curStatus) < 0) {
  1104             return errno;
  1105         }
  1106     }
  1107 #endif	/* USE_FIONBIO */
  1108 
  1109     psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING);
  1110 
  1111     return 0;
  1112 }
  1113 
  1114 /*
  1115  *----------------------------------------------------------------------
  1116  *
  1117  * PipeCloseProc --
  1118  *
  1119  *	This procedure is invoked by the generic IO level to perform
  1120  *	channel-type-specific cleanup when a command pipeline channel
  1121  *	is closed.
  1122  *
  1123  * Results:
  1124  *	0 on success, errno otherwise.
  1125  *
  1126  * Side effects:
  1127  *	Closes the command pipeline channel.
  1128  *
  1129  *----------------------------------------------------------------------
  1130  */
  1131 
  1132 	/* ARGSUSED */
  1133 static int
  1134 PipeCloseProc(instanceData, interp)
  1135     ClientData instanceData;	/* The pipe to close. */
  1136     Tcl_Interp *interp;		/* For error reporting. */
  1137 {
  1138     PipeState *pipePtr;
  1139     Tcl_Channel errChan;
  1140     int errorCode, result;
  1141 
  1142     errorCode = 0;
  1143     result = 0;
  1144     pipePtr = (PipeState *) instanceData;
  1145     if (pipePtr->inFile) {
  1146 	if (TclpCloseFile(pipePtr->inFile) < 0) {
  1147 	    errorCode = errno;
  1148 	}
  1149     }
  1150     if (pipePtr->outFile) {
  1151 	if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
  1152 	    errorCode = errno;
  1153 	}
  1154     }
  1155 
  1156     if (pipePtr->isNonBlocking || TclInExit()) {
  1157     
  1158 	/*
  1159          * If the channel is non-blocking or Tcl is being cleaned up, just
  1160          * detach the children PIDs, reap them (important if we are in a
  1161          * dynamic load module), and discard the errorFile.
  1162          */
  1163         
  1164         Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
  1165         Tcl_ReapDetachedProcs();
  1166 
  1167         if (pipePtr->errorFile) {
  1168 	    TclpCloseFile(pipePtr->errorFile);
  1169         }
  1170     } else {
  1171         
  1172 	/*
  1173          * Wrap the error file into a channel and give it to the cleanup
  1174          * routine.
  1175          */
  1176 
  1177         if (pipePtr->errorFile) {
  1178 	    errChan = Tcl_MakeFileChannel(
  1179 		(ClientData) GetFd(pipePtr->errorFile), TCL_READABLE);
  1180         } else {
  1181             errChan = NULL;
  1182         }
  1183         result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
  1184                 errChan);
  1185     }
  1186 
  1187     if (pipePtr->numPids != 0) {
  1188         ckfree((char *) pipePtr->pidPtr);
  1189     }
  1190     ckfree((char *) pipePtr);
  1191     if (errorCode == 0) {
  1192         return result;
  1193     }
  1194     return errorCode;
  1195 }
  1196 
  1197 /*
  1198  *----------------------------------------------------------------------
  1199  *
  1200  * PipeInputProc --
  1201  *
  1202  *	This procedure is invoked from the generic IO level to read
  1203  *	input from a command pipeline based channel.
  1204  *
  1205  * Results:
  1206  *	The number of bytes read is returned or -1 on error. An output
  1207  *	argument contains a POSIX error code if an error occurs, or zero.
  1208  *
  1209  * Side effects:
  1210  *	Reads input from the input device of the channel.
  1211  *
  1212  *----------------------------------------------------------------------
  1213  */
  1214 
  1215 static int
  1216 PipeInputProc(instanceData, buf, toRead, errorCodePtr)
  1217     ClientData instanceData;		/* Pipe state. */
  1218     char *buf;				/* Where to store data read. */
  1219     int toRead;				/* How much space is available
  1220                                          * in the buffer? */
  1221     int *errorCodePtr;			/* Where to store error code. */
  1222 {
  1223     PipeState *psPtr = (PipeState *) instanceData;
  1224     int bytesRead;			/* How many bytes were actually
  1225                                          * read from the input device? */
  1226 
  1227     *errorCodePtr = 0;
  1228     
  1229     /*
  1230      * Assume there is always enough input available. This will block
  1231      * appropriately, and read will unblock as soon as a short read is
  1232      * possible, if the channel is in blocking mode. If the channel is
  1233      * nonblocking, the read will never block.
  1234      * Some OSes can throw an interrupt error, for which we should
  1235      * immediately retry. [Bug #415131]
  1236      */
  1237 
  1238     do {
  1239 	bytesRead = read (GetFd(psPtr->inFile), buf, (size_t) toRead);
  1240     } while ((bytesRead < 0) && (errno == EINTR));
  1241 
  1242     if (bytesRead < 0) {
  1243 	*errorCodePtr = errno;
  1244 	return -1;
  1245     } else {
  1246 	return bytesRead;
  1247     }
  1248 }
  1249 
  1250 /*
  1251  *----------------------------------------------------------------------
  1252  *
  1253  * PipeOutputProc--
  1254  *
  1255  *	This procedure is invoked from the generic IO level to write
  1256  *	output to a command pipeline based channel.
  1257  *
  1258  * Results:
  1259  *	The number of bytes written is returned or -1 on error. An
  1260  *	output argument	contains a POSIX error code if an error occurred,
  1261  *	or zero.
  1262  *
  1263  * Side effects:
  1264  *	Writes output on the output device of the channel.
  1265  *
  1266  *----------------------------------------------------------------------
  1267  */
  1268 
  1269 static int
  1270 PipeOutputProc(instanceData, buf, toWrite, errorCodePtr)
  1271     ClientData instanceData;		/* Pipe state. */
  1272     CONST char *buf;			/* The data buffer. */
  1273     int toWrite;			/* How many bytes to write? */
  1274     int *errorCodePtr;			/* Where to store error code. */
  1275 {
  1276     PipeState *psPtr = (PipeState *) instanceData;
  1277     int written;
  1278 
  1279     *errorCodePtr = 0;
  1280 
  1281     /*
  1282      * Some OSes can throw an interrupt error, for which we should
  1283      * immediately retry. [Bug #415131]
  1284      */
  1285 
  1286     do {
  1287 	written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
  1288     } while ((written < 0) && (errno == EINTR));
  1289 
  1290     if (written < 0) {
  1291 	*errorCodePtr = errno;
  1292 	return -1;
  1293     } else {
  1294 	return written;
  1295     }
  1296 }
  1297 
  1298 /*
  1299  *----------------------------------------------------------------------
  1300  *
  1301  * PipeWatchProc --
  1302  *
  1303  *	Initialize the notifier to watch the fds from this channel.
  1304  *
  1305  * Results:
  1306  *	None.
  1307  *
  1308  * Side effects:
  1309  *	Sets up the notifier so that a future event on the channel will
  1310  *	be seen by Tcl.
  1311  *
  1312  *----------------------------------------------------------------------
  1313  */
  1314 
  1315 static void
  1316 PipeWatchProc(instanceData, mask)
  1317     ClientData instanceData;		/* The pipe state. */
  1318     int mask;				/* Events of interest; an OR-ed
  1319                                          * combination of TCL_READABLE,
  1320                                          * TCL_WRITABEL and TCL_EXCEPTION. */
  1321 {
  1322     PipeState *psPtr = (PipeState *) instanceData;
  1323     int newmask;
  1324 
  1325     if (psPtr->inFile) {
  1326 	newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
  1327 	if (newmask) {
  1328 	    Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
  1329 		    (Tcl_FileProc *) Tcl_NotifyChannel,
  1330 		    (ClientData) psPtr->channel);
  1331 	} else {
  1332 	    Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
  1333 	}
  1334     }
  1335     if (psPtr->outFile) {
  1336 	newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
  1337 	if (newmask) {
  1338 	    Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
  1339 		    (Tcl_FileProc *) Tcl_NotifyChannel,
  1340 		    (ClientData) psPtr->channel);
  1341 	} else {
  1342 	    Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
  1343 	}
  1344     }
  1345 }
  1346 
  1347 /*
  1348  *----------------------------------------------------------------------
  1349  *
  1350  * PipeGetHandleProc --
  1351  *
  1352  *	Called from Tcl_GetChannelHandle to retrieve OS handles from
  1353  *	inside a command pipeline based channel.
  1354  *
  1355  * Results:
  1356  *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  1357  *	there is no handle for the specified direction. 
  1358  *
  1359  * Side effects:
  1360  *	None.
  1361  *
  1362  *----------------------------------------------------------------------
  1363  */
  1364 
  1365 static int
  1366 PipeGetHandleProc(instanceData, direction, handlePtr)
  1367     ClientData instanceData;	/* The pipe state. */
  1368     int direction;		/* TCL_READABLE or TCL_WRITABLE */
  1369     ClientData *handlePtr;	/* Where to store the handle.  */
  1370 {
  1371     PipeState *psPtr = (PipeState *) instanceData;
  1372 
  1373     if (direction == TCL_READABLE && psPtr->inFile) {
  1374 	*handlePtr = (ClientData) GetFd(psPtr->inFile);
  1375 	return TCL_OK;
  1376     }
  1377     if (direction == TCL_WRITABLE && psPtr->outFile) {
  1378 	*handlePtr = (ClientData) GetFd(psPtr->outFile);
  1379 	return TCL_OK;
  1380     }
  1381     return TCL_ERROR;
  1382 }
  1383 
  1384 /*
  1385  *----------------------------------------------------------------------
  1386  *
  1387  * Tcl_WaitPid --
  1388  *
  1389  *	Implements the waitpid system call on Unix systems.
  1390  *
  1391  * Results:
  1392  *	Result of calling waitpid.
  1393  *
  1394  * Side effects:
  1395  *	Waits for a process to terminate.
  1396  *
  1397  *----------------------------------------------------------------------
  1398  */
  1399 
  1400 EXPORT_C Tcl_Pid
  1401 Tcl_WaitPid(pid, statPtr, options)
  1402     Tcl_Pid pid;
  1403     int *statPtr;
  1404     int options;
  1405 {
  1406     int result;
  1407     pid_t real_pid;
  1408 
  1409     real_pid = (pid_t) pid;
  1410     while (1) {
  1411 	result = (int) waitpid(real_pid, statPtr, options);
  1412 	if ((result != -1) || (errno != EINTR)) {
  1413 	    return (Tcl_Pid) result;
  1414 	}
  1415     }
  1416 }
  1417 
  1418 /*
  1419  *----------------------------------------------------------------------
  1420  *
  1421  * Tcl_PidObjCmd --
  1422  *
  1423  *	This procedure is invoked to process the "pid" Tcl command.
  1424  *	See the user documentation for details on what it does.
  1425  *
  1426  * Results:
  1427  *	A standard Tcl result.
  1428  *
  1429  * Side effects:
  1430  *	See the user documentation.
  1431  *
  1432  *----------------------------------------------------------------------
  1433  */
  1434 
  1435 	/* ARGSUSED */
  1436 int
  1437 Tcl_PidObjCmd(dummy, interp, objc, objv)
  1438     ClientData dummy;		/* Not used. */
  1439     Tcl_Interp *interp;		/* Current interpreter. */
  1440     int objc;			/* Number of arguments. */
  1441     Tcl_Obj *CONST *objv;	/* Argument strings. */
  1442 {
  1443     Tcl_Channel chan;
  1444     Tcl_ChannelType *chanTypePtr;
  1445     PipeState *pipePtr;
  1446     int i;
  1447     Tcl_Obj *resultPtr, *longObjPtr;
  1448 
  1449     if (objc > 2) {
  1450 	Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
  1451 	return TCL_ERROR;
  1452     }
  1453     if (objc == 1) {
  1454 	Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid());
  1455     } else {
  1456         chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
  1457         if (chan == (Tcl_Channel) NULL) {
  1458 	    return TCL_ERROR;
  1459 	}
  1460 	chanTypePtr = Tcl_GetChannelType(chan);
  1461 	if (chanTypePtr != &pipeChannelType) {
  1462 	    return TCL_OK;
  1463 	}
  1464         pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
  1465 	resultPtr = Tcl_GetObjResult(interp);
  1466         for (i = 0; i < pipePtr->numPids; i++) {
  1467 	    longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
  1468 	    Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
  1469 	}
  1470     }
  1471     return TCL_OK;
  1472 }
  1473 
  1474 /*
  1475  *----------------------------------------------------------------------
  1476  *
  1477  * TclpFinalizePipes --
  1478  *
  1479  *	Cleans up the pipe subsystem from Tcl_FinalizeThread
  1480  *
  1481  * Results:
  1482  *	None.
  1483  *
  1484  * This procedure carries out no operation on Unix.
  1485  *
  1486  *----------------------------------------------------------------------
  1487  */
  1488 
  1489 void
  1490 TclpFinalizePipes()
  1491 {
  1492 }
  1493