/* popen/pclose: * * simple MS-DOS piping scheme to imitate UNIX pipes * * $Log: popen.c,v $ * Revision 1.0.1 90/07/15 14:05:00 aas * fix assumption of trailing "/" in popen() * * Revision 1.0.0 88/08/03 20:08:00 ??? * original distribution: author unknown * found on simtel20 as pd1:popen.arc * */ #include #include #include #include #include #include #include #include "popen.h" extern char *getenv( char * ); #ifndef _NFILE # define _NFILE OPEN_MAX /* Number of open files */ #endif _NFILE #define READIT 1 /* Read pipe */ #define WRITEIT 2 /* Write pipe */ static char *prgname[ _NFILE ]; /* program name if write pipe */ static int pipetype[ _NFILE ]; /* 1=read 2=write */ static char *pipename[ _NFILE ]; /* pipe file name */ /* *------------------------------------------------------------------------ * stoupper: Convert string to uppercase (in place) *------------------------------------------------------------------------ */ static void stoupper( s ) char *s; { int c; for( ; (c = *s) != '\0'; ++s ) { if( islower( c ) ) *s = _toupper( c ); } } /* *------------------------------------------------------------------------ * strsave: Copy string into malloc'ed memory and return address *------------------------------------------------------------------------ */ static char * strsave( s ) char *s; { char *sp = malloc( strlen( s ) + 1 ); if( sp != (char *) NULL ) (void) strcpy( sp, s ); return( sp ); } /* *------------------------------------------------------------------------ * strfree: Returm strsave'd string memory *------------------------------------------------------------------------ */ static void strfree( s ) char *s; { if( s != (char *) NULL ) free( s ); } /* *------------------------------------------------------------------------ * run: Execute command via SHELL or COMSPEC *------------------------------------------------------------------------ */ static int run( command ) char *command; { jmp_buf panic; /* How to recover from errors */ int lineno; /* Line number where panic happened */ char *shell; /* Command processor */ char *s = (char *) NULL; /* Holds the command */ int s_is_malloced = 0; /* True if need to free 's' */ static char *command_com = "COMMAND.COM"; int status; /* Return codes */ char *shellpath; /* Full command processor path */ char *bp; /* Generic string pointer */ static char dash_c[ 3 ] = { '?', 'c', '\0' }; if( (lineno = setjmp( panic )) != 0 ) { int E = errno; #ifdef DEMO fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E ); #endif DEMO if( s_is_malloced && (s != (char *) NULL) ) strfree( s ); errno = E; return( -1 ); } if( (s = strsave( command )) == (char *) NULL ) longjmp( panic, __LINE__ ); /* Determine the command processor */ if( ((shell = getenv( "SHELL" )) == (char *) NULL) && ((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com; stoupper( shell ); shellpath = shell; /* Strip off any leading backslash directories */ shell = strrchr( shellpath, '\\' ); if( shell != (char *) NULL ) ++shell; else shell = shellpath; /* Strip off any leading slash directories */ bp = strrchr( shell, '/' ); if( bp != (char *) NULL ) shell = ++bp; if( strcmp( shell, command_com ) != 0 ) { /* MKS Shell needs quoted argument */ char *bp; if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL ) longjmp( panic, __LINE__ ); *bp++ = '\''; while( (*bp++ = *command++) != '\0' ); *(bp - 1) = '\''; *bp = '\0'; s_is_malloced = 1; } else s = command; dash_c[ 0 ] = getswitch(); /* Run the program */ #ifdef DEMO fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s ); #endif DEMO status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL ); if( s_is_malloced ) free( s ); return( status ); } /* *------------------------------------------------------------------------ * uniquepipe: returns a unique file name *------------------------------------------------------------------------ */ static char * uniquepipe() { static char name[ 14 ]; static short int num = 0; (void) sprintf( name, "pipe%04d.tmp", num++ ); return( name ); } /* *------------------------------------------------------------------------ * resetpipe: Private routine to cancel a pipe *------------------------------------------------------------------------ */ static void resetpipe( fd ) int fd; { char *bp; if( (fd >= 0) && (fd < _NFILE) ) { pipetype[ fd ] = 0; if( (bp = pipename[ fd ]) != (char *) NULL ) { (void) unlink( bp ); strfree( bp ); pipename[ fd ] = (char *) NULL; } if( (bp = prgname[ fd ]) != (char *) NULL ) { strfree( bp ); prgname[ fd ] = (char *) NULL; } } } /* *------------------------------------------------------------------------ * popen: open a pipe *------------------------------------------------------------------------ */ FILE *popen( prg, type ) char *prg; /* The command to be run */ char *type; /* "w" or "r" */ { FILE *p = (FILE *) NULL; /* Where we open the pipe */ int ostdin; /* Where our stdin is now */ int pipefd = -1; /* fileno( p ) -- for convenience */ char tmpfile[ BUFSIZ ]; /* Holds name of pipe file */ char *tmpdir; /* Points to directory prefix of pipe */ jmp_buf panic; /* Where to go if there's an error */ int lineno; /* Line number where panic happened */ char c; /* aas */ /* Find out where we should put temporary files */ if( (tmpdir = getenv( "TMPDIR" )) == (char *) NULL ) tmpdir = getenv( "TMP" ); if( tmpdir != (char *) NULL ) { /* Use temporary directory if available */ (void) strcpy( tmpfile, tmpdir ); if((c=tmpfile[strlen(tmpfile)-1])!='/' && c!='\\') /* aas */ (void) strcat( tmpfile, "/" ); } else *tmpfile = '\0'; /* Get a unique pipe file name */ (void) strcat( tmpfile, uniquepipe() ); if( (lineno = setjmp( panic )) != 0 ) { /* An error has occurred, so clean up */ int E = errno; #ifdef DEMO fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E ); #endif DEMO if( p != (FILE *) NULL ) (void) fclose( p ); resetpipe( pipefd ); errno = E; return( (FILE *) NULL ); } if( strcmp( type, "w" ) == 0 ) { /* for write style pipe, pclose handles program execution */ if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) { pipefd = fileno( p ); pipetype[ pipefd ] = WRITEIT; pipename[ pipefd ] = strsave( tmpfile ); prgname[ pipefd ] = strsave( prg ); if( !pipename[ pipefd ] || !prgname[ pipefd ] ) longjmp( panic, __LINE__ ); } } else if( strcmp( type, "r" ) == 0 ) { /* read pipe must create tmp file, set up stdout to point to the temp * file, and run the program. note that if the pipe file cannot be * opened, it'll return a condition indicating pipe failure, which is * fine. */ if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) { int ostdout; pipefd = fileno( p ); pipetype[ pipefd ]= READIT; if( (pipename[ pipefd ] = strsave( tmpfile )) == (char *) NULL ) longjmp( panic, __LINE__ ); /* Redirect stdin for the new command */ ostdout = dup( fileno( stdout ) ); if( dup2( fileno( stdout ), pipefd ) < 0 ) { int E = errno; (void) dup2( fileno( stdout ), ostdout ); errno = E; longjmp( panic, __LINE__ ); } if( run( prg ) != 0 ) longjmp( panic, __LINE__ ); if( dup2( fileno( stdout ), ostdout ) < 0 ) longjmp( panic, __LINE__ ); if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); if( (p = fopen( tmpfile, "r" )) == (FILE *) NULL ) longjmp( panic, __LINE__ ); } } else { /* screwy call or unsupported type */ errno = EINVFNC; longjmp( panic, __LINE__ ); } return( p ); } /* close a pipe */ int pclose( p ) FILE *p; { int pipefd = -1; /* Fildes where pipe is opened */ int ostdout; /* Where our stdout points now */ int ostdin; /* Where our stdin points now */ jmp_buf panic; /* Context to return to if error */ int lineno; /* Line number where panic happened */ if( (lineno = setjmp( panic )) != 0 ) { /* An error has occurred, so clean up and return */ int E = errno; #ifdef DEMO fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E ); #endif DEMO if( p != (FILE *) NULL ) (void) fclose( p ); resetpipe( pipefd ); errno = E; return( -1 ); } pipefd = fileno( p ); if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); switch( pipetype[ pipefd ] ) { case WRITEIT: /* open the temp file again as read, redirect stdin from that * file, run the program, then clean up. */ if( (p = fopen( pipename[ pipefd ],"r" )) == (FILE *) NULL ) longjmp( panic, __LINE__ ); ostdin = dup( fileno( stdin )); if( dup2( fileno( stdin ), fileno( p ) ) < 0 ) longjmp( panic, __LINE__ ); if( run( prgname[ pipefd ] ) != 0 ) longjmp( panic, __LINE__ ); if( dup2( fileno( stdin ), ostdin ) < 0 ) longjmp( panic, __LINE__ ); if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); resetpipe( pipefd ); break; case READIT: /* close the temp file and remove it */ resetpipe( pipefd ); break; default: errno = EINVFNC; longjmp( panic, __LINE__ ); /*NOTREACHED*/ } return( 0 ); } #ifdef DEMO int main( argc, argv ) int argc; char **argv; { FILE *pipe; char buf[ BUFSIZ ]; int n; *buf = '\0'; for( n = 1; n < argc; ++n ) { (void) strcat( buf, argv[ n ] ); (void) strcat( buf, " " ); } if( (pipe = popen( buf, "r" )) != (FILE *) NULL ) { while( fgets( buf, sizeof( buf ), pipe ) != (char *) NULL ) (void) fputs( buf, stdout ); if( pclose( pipe ) != 0 ) fprintf( stderr, "error closing pipe!\n" ); } else fprintf( stderr, "it didn't work!\n" ); exit( 0 ); /*NOTREACHED*/ } #endif DEMO