Category : C++ Source Code
Archive   : VCCRT2.ZIP
Filename : POPEN.C

 
Output of file : POPEN.C contained in archive : VCCRT2.ZIP
/***
*popen.c - initiate a pipe and a child command
*
* Copyright (c) 1989-1992, Microsoft Corporation. All rights reserved.
*
*Purpose:
* Defines _popen() and _pclose().
*
*******************************************************************************/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/* size for pipe buffer
*/
#define PSIZE 1024

#define STDIN 0
#define STDOUT 1

/* constants used in setting the state of a handle
*/
#define NOINHERIT 0x0080
#define FHSMASK 0x7f88 /* 0111111110001000 in binary */

/* definitions for table of stream pointer - process ID number 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 procid;
} IDpair;

/* number of entries in idpairs table
*/
static unsigned idtabsiz = 0;

/* pointer to first table entry
*/
static IDpair *idpairs = NULL;

/* function to find specified table entries. also, creates and maintains
* the table.
*/
static IDpair * near pascal idtab(FILE *);

/* function to modify the inheritance bit for a handle
*/
static void near pascal setinherit(int, int);

/***
*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:
* char *cmdstring - command to be executed
* char *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 * _popen(cmdstring, type)
const char *cmdstring;
const char *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 */
int stddup = -1; /* a dup of either STDIN or STDOUT */
FILE *pstream; /* stream to be associated with pipe */
int childid; /* process id for child process (cmd.exe) */
IDpair *locidpair; /* pointer to IDpair table entry */
char osfsave1; /* used to save _osfile[stdhdl] */
char osfsave2; /* used to save _osfile[phdls[i2]] */
char *cmdexe; /* pathname for the command processor */

/* first check for errors in the arguments
*/
if ( (cmdstring == NULL) || (type == NULL) || ((*type != 'w') &&
(*type != 'r')) ) goto error1;

/* do the _pipe()
*/
if ( *(type + 1) == 't' )
tm = _O_TEXT;
else if ( *(type + 1) == 'b' )
tm = _O_BINARY;

if ( _pipe(phdls, PSIZE, tm) == -1 ) goto error1;

/* test *type and set stdhdl, i1 and i2 accordingly.
*/
if ( *type == 'w' ) {
stdhdl = STDIN;
i1 = 0;
i2 = 1;
}
else {
stdhdl = STDOUT;
i1 = 1;
i2 = 0;
}

/* set flags to indicate pipe handles are open. note, these are only
* used for error recovery.
*/
ph_open[0] = ph_open[1] = 1;

/* MULTI-THREAD: ASSERT LOCK ON STDHDL HERE!!!!
*/

/* save a copy of stdhdl in stddup for later restoration. also, save
* the corresponding _osfile[] entry.
*/
if ( DOSDUPHANDLE(stdhdl, (unsigned far *)&stddup) )
goto error2;

osfsave1 = _osfile[stdhdl];

/* force stdhdl to 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 ( DOSDUPHANDLE(phdls[i1], (unsigned far *)&stdhdl) )
goto error3;

_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 = _fdopen(phdls[i2], type)) == NULL )
goto error4;

/* MULTI-THREAD: ASSERT LOCK ON IDPAIRS HERE!!!!
*/

/* next, set locidpair to a free entry in the idpairs table.
*/
if ( (locidpair = idtab(NULL)) == NULL )
goto error5;

/* mark stddup and phdls[i2] as non-inheritable. also, temporarily,
* clear _osfile[phdls[i2]] entry so the child neither has, nor thinks
* it has, this extra handle on the pipe.
*/
setinherit(stddup, 1);
setinherit(phdls[i2], 1);
osfsave2 = _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 = getenv("COMSPEC")) == NULL) ||
(((childid = _spawnl(P_NOWAIT, cmdexe, cmdexe, "/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" instead. note that spawnlp is used
* here so that the path is searched.
*/
cmdexe = "cmd.exe";
childid = _spawnlp(P_NOWAIT, cmdexe, cmdexe, "/c", cmdstring,
NULL);
}

/* before checking the results, make stddup inheritable again and
* restore _osfile[phdls[i2]]
*/
setinherit(stddup, 0);
_osfile[phdls[i2]] = osfsave2;

/* check if the last (perhaps only) spawn attempt was successful
*/
if (childid == -1) goto error6;

/* restore stdhdl for the current process, set value of *locidpair,
* close stddup (note that DOSCLOSE must be used instead of close)
* and return pstream to the caller
*/
(void)DOSDUPHANDLE(stddup, (unsigned far *)&stdhdl);
_osfile[stdhdl] = osfsave1;

/* MULTI-THREAD: RELEASE LOCK ON STDHDL HERE!!!!
*/

(void)DOSCLOSE(stddup);
locidpair->procid = childid;
locidpair->stream = pstream;

/* MULTI-THREAD: RELEASE LOCK ON IDPAIRS HERE!!!!
*/

/* 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!!!!
*/

error4: /* restore stdhdl
*/
(void)DOSDUPHANDLE(stddup, (unsigned far *)&stdhdl);
_osfile[stdhdl] = osfsave1;

/* MULTI-THREAD: RELEASE LOCK ON STDHDL HERE!!!!
*/

error3: /* close stddup (note, DOSCLOSE must be used rather than close)
*/
(void)DOSCLOSE(stddup);

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);
}


/***
*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 ID 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 _pclose(pstream)
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!!!!
*/

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->procid, WAIT_GRANDCHILD) != -1) ||
(errno == EINTR) )
/* cwait has returned with termination info. swap the upper
* and lower bytes of the termination status word and use it
* for the return code.
*/
retval = _rotl(termstat,8);

/* Mark the IDpairtable entry as free
*/
locidpair->stream = NULL;

/* only return path!
*/
done:
/* MULTI-THREAD: RELEASE LOCK ON IDPAIRS HERE!!!!
*/
return(retval);
}


/***
* static IDpair * near pascal 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 * near pascal idtab(pstream)
FILE *pstream;
{

IDpair * pairptr; /* ptr to entry */
IDpair * newptr; /* ptr to newly malloc'd memory */


/* search the table. if table is empty, apropriate 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 NULL).
*/
if ( (pstream != NULL) || ((newptr = (IDpair *)realloc((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);

}


/***
* static void near pascal setinherit(int hdl, int noinherit) - change the
* inheritance property of a handle
*
*Purpose:
* Change the inheritance property (the InheritanceFlag value, in OS/2
* parlance) of the specified handle. That is, change whether or not the
* handle will be inherited by a child process.
*
*Entry:
* int hdl - file/device/pipe handle to modify
* int noinherit - if true (nonzero), make the handle non-inheritable
* otherwise, make the handle inheritable
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

static void near pascal setinherit(hdl, noinherit)
int hdl;
int noinherit;
{
unsigned hdlstate;

/* fetch the current state of the file handle and mask off the bits
* that must be zeroed for the DOSSETFHANDSTATE call
*/
(void)DOSQFHANDSTATE(hdl, (unsigned far *)&hdlstate);
hdlstate &= FHSMASK;

/* if noinherit is nonzero (true), mark the handle as non-inheritable.
* otherwise, mark the handle as inheritable.
*/
if (noinherit)
/* non-inheritable, set the InheritanceFlag bit
*/
hdlstate |= NOINHERIT;
else
/* inheritable, clear the InheritanceFlag bit
*/
hdlstate &= ~NOINHERIT;

/* set the state of the file handle
*/
(void)DOSSETFHANDSTATE(hdl, hdlstate);
}


  3 Responses to “Category : C++ Source Code
Archive   : VCCRT2.ZIP
Filename : POPEN.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/