home *** CD-ROM | disk | FTP | other *** search
- /***
- *popen.c - initiate a pipe and a child command
- *
- * Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
- *
- *Purpose:
- * Defines _popen() and _pclose().
- *
- *******************************************************************************/
-
-
- #include <cruntime.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <malloc.h>
- #include <process.h>
- #include <io.h>
- #include <fcntl.h>
- #include <internal.h>
- #include <errno.h>
- #include <msdos.h>
- #include <mtdll.h>
- #include <oscalls.h>
- #include <tchar.h>
- #include <dbgint.h>
-
- /* size for pipe buffer
- */
- #define PSIZE 1024
-
- #define STDIN 0
- #define STDOUT 1
-
-
- /* definitions for table of stream pointer - process handle pairs. the table
- * is created, maintained and accessed by the idtab function. _popen and
- * _pclose gain access to table entries only by calling idtab. Note that the
- * table is expanded as necessary (by idtab) and free table entries are reused
- * (an entry is free if its stream field is NULL), but the table is never
- * contracted.
- */
-
- typedef struct {
- FILE *stream;
- int prochnd;
- } IDpair;
-
- /* number of entries in idpairs table
- */
- #ifndef _UNICODE
- unsigned __idtabsiz = 0;
- #else /* _UNICODE */
- extern unsigned __idtabsiz;
- #endif /* _UNICODE */
-
- /* pointer to first table entry
- */
- #ifndef _UNICODE
- IDpair *__idpairs = NULL;
- #else /* _UNICODE */
- extern IDpair *__idpairs;
- #endif /* _UNICODE */
-
- /* function to find specified table entries. also, creates and maintains
- * the table.
- */
- static IDpair * __cdecl idtab(FILE *);
-
-
- /***
- *FILE *_popen(cmdstring,type) - initiate a pipe and a child command
- *
- *Purpose:
- * Creates a pipe and asynchronously executes a child copy of the command
- * processor with cmdstring (see system()). If the type string contains
- * an 'r', the calling process can read child command's standard output
- * via the returned stream. If the type string contains a 'w', the calling
- * process can write to the child command's standard input via the
- * returned stream.
- *
- *Entry:
- * _TSCHAR *cmdstring - command to be executed
- * _TSCHAR *type - string of the form "r|w[b|t]", determines the mode
- * of the returned stream (i.e., read-only vs write-only,
- * binary vs text mode)
- *
- *Exit:
- * If successful, returns a stream associated with one end of the created
- * pipe (the other end of the pipe is associated with either the child
- * command's standard input or standard output).
- *
- * If an error occurs, NULL is returned.
- *
- *Exceptions:
- *
- *******************************************************************************/
-
- FILE * __cdecl _tpopen (
- const _TSCHAR *cmdstring,
- const _TSCHAR *type
- )
- {
-
- int phdls[2]; /* I/O handles for pipe */
- int ph_open[2]; /* flags, set if correspond phdls is open */
- int i1; /* index into phdls[] */
- int i2; /* index into phdls[] */
-
- int tm = 0; /* flag indicating text or binary mode */
-
- int stdhdl; /* either STDIN or STDOUT */
- HANDLE osfhndsv1; /* used to save _osfhnd(stdhdl) */
- long osfhndsv2; /* used to save _osfhnd(phdls[i2]) */
- char osfilesv1; /* used to save _osfile(stdhdl) */
- char osfilesv2; /* used to save _osfile(phdls[i2]) */
-
- HANDLE oldhnd; /* used to hold OS file handle values... */
- HANDLE newhnd; /* ...in calls to DuplicateHandle API */
-
- FILE *pstream; /* stream to be associated with pipe */
-
- HANDLE prochnd; /* handle for current process */
-
- _TSCHAR *cmdexe; /* pathname for the command processor */
- int childhnd; /* handle for child process (cmd.exe) */
-
- IDpair *locidpair; /* pointer to IDpair table entry */
-
-
- /* first check for errors in the arguments
- */
- if ( (cmdstring == NULL) || (type == NULL) || ((*type != 'w') &&
- (*type != _T('r'))) )
- goto error1;
-
- /* do the _pipe(). note that neither of the resulting handles will
- * be inheritable.
- */
-
- if ( *(type + 1) == _T('t') )
- tm = _O_TEXT;
- else if ( *(type + 1) == _T('b') )
- tm = _O_BINARY;
-
- tm |= _O_NOINHERIT;
-
- if ( _pipe( phdls, PSIZE, tm ) == -1 )
- goto error1;
-
- /* test *type and set stdhdl, i1 and i2 accordingly.
- */
- if ( *type == _T('w') ) {
- stdhdl = STDIN;
- i1 = 0;
- i2 = 1;
- }
- else {
- stdhdl = STDOUT;
- i1 = 1;
- i2 = 0;
- }
-
- /* the pipe now exists. the following steps must be carried out before
- * the child process (cmd.exe) may be spawned:
- *
- * 1. save a non-inheritable dup of stdhdl
- *
- * 2. force stdhdl to be a dup of ph[i1]. close both ph[i1] and
- * the original OS handle underlying stdhdl
- *
- * 3. associate a stdio-level stream with ph[i2].
- */
-
- /* set flags to indicate pipe handles are open. note, these are only
- * used for error recovery.
- */
- ph_open[ 0 ] = ph_open[ 1 ] = 1;
-
-
- /* get the process handle, it will be needed in some API calls
- */
- prochnd = GetCurrentProcess();
-
- /* MULTI-THREAD: ASSERT LOCK ON STDHDL HERE!!!!
- */
- _lock_fh( stdhdl );
-
- /* save a non-inheritable copy of stdhdl for later restoration.
- */
-
- oldhnd = (HANDLE)_osfhnd( stdhdl );
-
- if ( (oldhnd == INVALID_HANDLE_VALUE) ||
- !DuplicateHandle( prochnd,
- oldhnd,
- prochnd,
- &osfhndsv1,
- 0L,
- FALSE, /* non-inheritable */
- DUPLICATE_SAME_ACCESS )
- ) {
- goto error2;
- }
-
- osfilesv1 = _osfile( stdhdl );
-
- /* force stdhdl to an inheritable dup of phdls[i1] (i.e., force
- * STDIN to the pipe read handle or STDOUT to the pipe write handle)
- * and close phdls[i1] (so there won't be a stray open handle on the
- * pipe after a _pclose). also, clear ph_open[i1] flag so that error
- * recovery won't try to close it again.
- */
-
- if ( !DuplicateHandle( prochnd,
- (HANDLE)_osfhnd( phdls[i1] ),
- prochnd,
- &newhnd,
- 0L,
- TRUE, /* inheritable */
- DUPLICATE_SAME_ACCESS )
- ) {
- goto error3;
- }
-
- (void)CloseHandle( (HANDLE)_osfhnd(stdhdl) );
- _free_osfhnd( stdhdl );
-
- _set_osfhnd( stdhdl, (long)newhnd );
- _osfile( stdhdl ) = _osfile( phdls[i1] );
-
- (void)_close( phdls[i1] );
- ph_open[ i1 ] = 0;
-
-
- /* associate a stream with phdls[i2]. note that if there are no
- * errors, pstream is the return value to the caller.
- */
- if ( (pstream = _tfdopen( phdls[i2], type )) == NULL )
- goto error4;
-
- /* MULTI-THREAD: ASSERT LOCK ON IDPAIRS HERE!!!!
- */
- _mlock( _POPEN_LOCK );
-
- /* next, set locidpair to a free entry in the idpairs table.
- */
- if ( (locidpair = idtab( NULL )) == NULL )
- goto error5;
-
-
- /* temporarily change the osfhnd and osfile entries so that
- * the child doesn't get any entries for phdls[i2].
- */
- osfhndsv2 = _osfhnd( phdls[i2] );
- _osfhnd( phdls[i2] ) = (long)INVALID_HANDLE_VALUE;
- osfilesv2 = _osfile( phdls[i2] );
- _osfile( phdls[i2] ) = 0;
-
- /* spawn the child copy of cmd.exe. the algorithm is adapted from
- * SYSTEM.C, and behaves the same way.
- */
- if ( ((cmdexe = _tgetenv(_T("COMSPEC"))) == NULL) ||
- (((childhnd = _tspawnl( _P_NOWAIT,
- cmdexe,
- cmdexe,
- _T("/c"),
- cmdstring,
- NULL ))
- == -1) && ((errno == ENOENT) || (errno == EACCES))) ) {
- /*
- * either COMSPEC wasn't defined, or the spawn failed because
- * cmdexe wasn't found or was inaccessible. in either case, try to
- * spawn "cmd.exe" (Windows NT) or "command.com" (Windows 95) instead
- * Note that spawnlp is used here so that the path is searched.
- */
- cmdexe = ( _osver & 0x8000 ) ? _T("command.com") : _T("cmd.exe");
- childhnd = _tspawnlp( _P_NOWAIT,
- cmdexe,
- cmdexe,
- _T("/c"),
- cmdstring,
- NULL);
- }
-
- _osfhnd( phdls[i2] ) = osfhndsv2;
- _osfile( phdls[i2] ) = osfilesv2;
-
- /* check if the last (perhaps only) spawn attempt was successful
- */
- if ( childhnd == -1 )
- goto error6;
-
- /* restore stdhdl for the current process, set value of *locidpair,
- * close osfhndsv1 (note that CloseHandle must be used instead of close)
- * and return pstream to the caller
- */
-
- (void)DuplicateHandle( prochnd,
- osfhndsv1,
- prochnd,
- &newhnd,
- 0L,
- TRUE, /* inheritable */
- DUPLICATE_CLOSE_SOURCE | /* close osfhndsv1 */
- DUPLICATE_SAME_ACCESS );
-
- (void)CloseHandle( (HANDLE)_osfhnd(stdhdl) );
- _free_osfhnd( stdhdl );
-
- _set_osfhnd( stdhdl, (long)newhnd );
- _osfile(stdhdl) = osfilesv1;
-
- /* MULTI-THREAD: RELEASE LOCK ON STDHDL HERE!!!!
- */
- _unlock_fh( stdhdl );
-
- locidpair->prochnd = childhnd;
- locidpair->stream = pstream;
-
- /* MULTI-THREAD: RELEASE LOCK ON IDPAIRS HERE!!!!
- */
- _munlock( _POPEN_LOCK );
-
- /* all successful calls to _popen return to the caller via this return
- * statement!
- */
- return( pstream );
-
- /**
- * error handling code. all detected errors end up here, entering
- * via a goto one of the labels. note that the logic is currently
- * a straight fall-thru scheme (e.g., if entered at error5, the
- * code for error5, error4,...,error1 is all executed).
- **********************************************************************/
-
- error6: /* make sure locidpair is reusable
- */
- locidpair->stream = NULL;
-
- error5: /* close pstream (also, clear ph_open[i2] since the stream
- * close will also close the pipe handle)
- */
- (void)fclose( pstream );
- ph_open[ i2 ] = 0;
-
- /* MULTI-THREAD: RELEASE LOCK ON IDPAIRS HERE!!!!
- */
- _munlock(_POPEN_LOCK);
-
- error4: /* restore stdhdl
- */
-
- (void)DuplicateHandle( prochnd,
- osfhndsv1,
- prochnd,
- &newhnd,
- 0L,
- TRUE,
- DUPLICATE_SAME_ACCESS );
-
- (void)CloseHandle( (HANDLE)_osfhnd(stdhdl) );
- _free_osfhnd( stdhdl );
-
- _set_osfhnd( stdhdl, (long)newhnd );
- _osfile( stdhdl ) = osfilesv1;
-
- /* MULTI-THREAD: RELEASE LOCK ON STDHDL HERE!!!!
- */
- _unlock_fh( stdhdl );
-
- error3: /* close osfhndsv1
- */
-
- CloseHandle( osfhndsv1 );
-
- error2: /* close handles on pipe (if they are still open)
- */
- if ( ph_open[i1] )
- _close( phdls[i1] );
- if ( ph_open[i2] )
- _close( phdls[i2] );
-
- error1: /* return NULL to the caller indicating failure
- */
- return( NULL );
- }
-
- #ifndef _UNICODE
-
- /***
- *int _pclose(pstream) - wait on a child command and close the stream on the
- * associated pipe
- *
- *Purpose:
- * Closes pstream then waits on the associated child command. The
- * argument, pstream, must be the return value from a previous call to
- * _popen. _pclose first looks up the process handle of child command
- * started by that _popen and does a cwait on it. Then, it closes pstream
- * and returns the exit status of the child command to the caller.
- *
- *Entry:
- * FILE *pstream - file stream returned by a previous call to _popen
- *
- *Exit:
- * If successful, _pclose returns the exit status of the child command.
- * The format of the return value is that same as for cwait, except that
- * the low order and high order bytes are swapped.
- *
- * If an error occurs, -1 is returned.
- *
- *Exceptions:
- *
- *******************************************************************************/
-
- int __cdecl _pclose (
- FILE *pstream
- )
- {
- IDpair *locidpair; /* pointer to entry in idpairs table */
- int termstat; /* termination status word */
- int retval = -1; /* return value (to caller) */
-
- /* MULTI-THREAD: LOCK IDPAIRS HERE!!!!
- */
- _mlock(_POPEN_LOCK);
-
- if ( (pstream == NULL) || ((locidpair = idtab(pstream)) == NULL) )
- /* invalid pstream, exit with retval == -1
- */
- goto done;
-
- /* close pstream
- */
- (void)fclose(pstream);
-
- /* wait on the child (copy of the command processor) and all of its
- * children.
- */
- if ( (_cwait(&termstat, locidpair->prochnd, _WAIT_GRANDCHILD) != -1) ||
- (errno == EINTR) )
- retval = termstat;
-
- /* Mark the IDpairtable entry as free (note: prochnd was closed by the
- * preceding call to _cwait).
- */
- locidpair->stream = NULL;
- locidpair->prochnd = 0;
-
- /* only return path!
- */
- done:
- /* MULTI-THREAD: RELEASE LOCK ON IDPAIRS HERE!!!!
- */
- _munlock(_POPEN_LOCK);
- return(retval);
- }
-
- #endif /* _UNICODE */
-
- /***
- * static IDpair * idtab(FILE *pstream) - find an idpairs table entry
- *
- *Purpose:
- * Find an entry in the idpairs table. This function finds the entry the
- * idpairs table entry corresponding to pstream. In the case where pstream
- * is NULL, the entry being searched for is any free entry. In this case,
- * idtab will create the idpairs table if it doesn't exist, or expand it (by
- * exactly one entry) if there are no free entries.
- *
- * [MTHREAD NOTE: This routine assumes that the caller has acquired the
- * idpairs table lock.]
- *
- *Entry:
- * FILE *pstream - stream corresponding to table entry to be found (if NULL
- * then find any free table entry)
- *
- *Exit:
- * if successful, returns a pointer to the idpairs table entry. otherwise,
- * returns NULL.
- *
- *Exceptions:
- *
- *******************************************************************************/
-
- static IDpair * __cdecl idtab (
- FILE *pstream
- )
- {
-
- IDpair * pairptr; /* ptr to entry */
- IDpair * newptr; /* ptr to newly malloc'd memory */
-
-
- /* search the table. if table is empty, appropriate action should
- * fall out automatically.
- */
- for ( pairptr = __idpairs ; pairptr < (__idpairs+__idtabsiz) ; pairptr++ )
- if ( pairptr->stream == pstream )
- break;
-
- /* if we found an entry, return it.
- */
- if ( pairptr < (__idpairs + __idtabsiz) )
- return(pairptr);
-
- /* did not find an entry in the table. if pstream was NULL, then try
- * creating/expanding the table. otherwise, return NULL. note that
- * when the table is created or expanded, exactly one new entry is
- * produced. this must not be changed unless code is added to mark
- * the extra entries as being free (i.e., set their stream fields to
- * to NULL).
- */
- if ( (pstream != NULL) || ((newptr = (IDpair *)_realloc_crt((void *)__idpairs,
- (__idtabsiz + 1)*sizeof(IDpair))) == NULL) )
- /* either pstream was non-NULL or the attempt to create/expand
- * the table failed. in either case, return a NULL to indicate
- * failure.
- */
- return( NULL );
-
- __idpairs = newptr; /* new table ptr */
- pairptr = newptr + __idtabsiz; /* first new entry */
- __idtabsiz++; /* new table size */
-
- return( pairptr );
-
- }
-
-
-