reynolds@uunet.uu.net@hsvpmi.UUCP (03/06/88)
Submitted-By: "A. Nonymous" <reynolds@uunet.uu.net@hsvpmi.UUCP> Archive-Name: msdos-pipes Comp.sources.misc: Volume 2, Issue 68 Submitted-By: "A. Nonymous" <reynolds@uunet.uu.net@hsvpmi.UUCP> Archive-Name: msdos-pipes [I'm not so sure I see the point of this.... ++bsa] Here is an MS-DOS version of the UN*X popen(3S) routine, inspired by an off-the-cuff article from the net (no, I don't have the reference... :-). It works under TURBO-C, and is compatible with the MKS Toolkit (but it works fine without it). Compile with the -DDEMO switch to generate a demo program used like this: popen command .... to run 'command ...' from a read-mode pipe. If the shell environment variable SHELL is set, that program is used; otherwise the variable COMSPEC is used; failing all that, COMMAND.COM in the current working directory is used. Caveat programme... --------- Tom Reynolds voice: (205) 721-1200 x 303 Phoenix microsystems, inc. uucp: ..!uunet!ingr!hsvpmi!reynolds 991 Discovery Drive Huntsville, AL 35806 "First curse the darkness, then light the candle" o / o / o / o / o / o / o / ---- x ------- x ------- x ------- x ------- x ------- x ------- x ------- o \ o \ o \ o \ o \ o \ o \ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # getswitch.c # makefile # popen.c # popen.h # turboc.cfg # This archive created: Wed Mar 2 17:24:28 1988 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'getswitch.c'" '(274 characters)' if test -f 'getswitch.c' then echo shar: "will not over-write existing file 'getswitch.c'" else sed 's/^ X//' << \SHAR_EOF > 'getswitch.c' X#include <stdio.h> X#include <dos.h> X Xstatic char SW = 0; /* DOS switch character, either '-' or '/' */ X Xint Xgetswitch() X{ X if (SW == 0) { X /* get SW using dos call 0x37 */ X _AX = 0x3700; X geninterrupt(0x21); X SW = _DL; X } X return( SW & 0xFF ); X} SHAR_EOF if test 274 -ne "`wc -c < 'getswitch.c'`" then echo shar: "error transmitting 'getswitch.c'" '(should have been 274 characters)' fi fi echo shar: "extracting 'makefile'" '(226 characters)' if test -f 'makefile' then echo shar: "will not over-write existing file 'makefile'" else sed 's/^ X//' << \SHAR_EOF > 'makefile' Xpopen.exe: popen.obj getswitch.obj X cc -epopen popen.obj getswitch.obj X Xpopen.obj: popen.c X cc -c -DDEMO popen.c X Xclean: X rm -f *.obj core *.map X Xclobber: clean X rm -f *.exe install X Xinstall: X cc -c popen.c X @echo What next? SHAR_EOF if test 226 -ne "`wc -c < 'makefile'`" then echo shar: "error transmitting 'makefile'" '(should have been 226 characters)' fi fi echo shar: "extracting 'popen.c'" '(9895 characters)' if test -f 'popen.c' then echo shar: "will not over-write existing file 'popen.c'" else sed 's/^ X//' << \SHAR_EOF > 'popen.c' X/* popen/pclose: X * X * simple MS-DOS piping scheme to imitate UNIX pipes X */ X X#include <stdio.h> X#include <ctype.h> X#include <alloc.h> X#include <string.h> X#include <errno.h> X#include <setjmp.h> X#include <process.h> X X#include "popen.h" X Xextern char *getenv( char * ); X X#ifndef _NFILE X# define _NFILE OPEN_MAX /* Number of open files */ X#endif _NFILE X X#define READIT 1 /* Read pipe */ X#define WRITEIT 2 /* Write pipe */ X Xstatic char *prgname[ _NFILE ]; /* program name if write pipe */ Xstatic int pipetype[ _NFILE ]; /* 1=read 2=write */ Xstatic char *pipename[ _NFILE ]; /* pipe file name */ X X/* X *------------------------------------------------------------------------ X * stoupper: Convert string to uppercase (in place) X *------------------------------------------------------------------------ X */ X Xstatic void Xstoupper( s ) Xchar *s; X{ X int c; X for( ; (c = *s) != '\0'; ++s ) { X if( islower( c ) ) *s = _toupper( c ); X } X} X X/* X *------------------------------------------------------------------------ X * strsave: Copy string into malloc'ed memory and return address X *------------------------------------------------------------------------ X */ X Xstatic char * Xstrsave( s ) Xchar *s; X{ X char *sp = malloc( strlen( s ) + 1 ); X if( sp != (char *) NULL ) (void) strcpy( sp, s ); X return( sp ); X} X X/* X *------------------------------------------------------------------------ X * strfree: Returm strsave'd string memory X *------------------------------------------------------------------------ X */ X Xstatic void Xstrfree( s ) Xchar *s; X{ X if( s != (char *) NULL ) free( s ); X} X X/* X *------------------------------------------------------------------------ X * run: Execute command via SHELL or COMSPEC X *------------------------------------------------------------------------ X */ X Xstatic int Xrun( command ) Xchar *command; X{ X jmp_buf panic; /* How to recover from errors */ X int lineno; /* Line number where panic happened */ X char *shell; /* Command processor */ X char *s = (char *) NULL; /* Holds the command */ X int s_is_malloced = 0; /* True if need to free 's' */ X static char *command_com = "COMMAND.COM"; X int status; /* Return codes */ X char *shellpath; /* Full command processor path */ X char *bp; /* Generic string pointer */ X static char dash_c[ 3 ] = { '?', 'c', '\0' }; X if( (lineno = setjmp( panic )) != 0 ) { X int E = errno; X#ifdef DEMO X fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E ); X#endif DEMO X if( s_is_malloced && (s != (char *) NULL) ) strfree( s ); X errno = E; X return( -1 ); X } X if( (s = strsave( command )) == (char *) NULL ) longjmp( panic, __LINE__ ); X /* Determine the command processor */ X if( ((shell = getenv( "SHELL" )) == (char *) NULL) && X ((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com; X stoupper( shell ); X shellpath = shell; X /* Strip off any leading backslash directories */ X shell = strrchr( shellpath, '\\' ); X if( shell != (char *) NULL ) ++shell; X else shell = shellpath; X /* Strip off any leading slash directories */ X bp = strrchr( shell, '/' ); X if( bp != (char *) NULL ) shell = ++bp; X if( strcmp( shell, command_com ) != 0 ) { X /* MKS Shell needs quoted argument */ X char *bp; X if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL ) X longjmp( panic, __LINE__ ); X *bp++ = '\''; X while( (*bp++ = *command++) != '\0' ); X *(bp - 1) = '\''; X *bp = '\0'; X s_is_malloced = 1; X } else s = command; X dash_c[ 0 ] = getswitch(); X /* Run the program */ X#ifdef DEMO X fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s ); X#endif DEMO X status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL ); X if( s_is_malloced ) free( s ); X return( status ); X} X X/* X *------------------------------------------------------------------------ X * uniquepipe: returns a unique file name X *------------------------------------------------------------------------ X */ X Xstatic char * Xuniquepipe() X{ X static char name[ 14 ]; X static short int num = 0; X (void) sprintf( name, "pipe%05d.tmp", num++ ); X return( name ); X} X X/* X *------------------------------------------------------------------------ X * resetpipe: Private routine to cancel a pipe X *------------------------------------------------------------------------ X */ X Xstatic void Xresetpipe( fd ) Xint fd; X{ X char *bp; X if( (fd >= 0) && (fd < _NFILE) ) { X pipetype[ fd ] = 0; X if( (bp = pipename[ fd ]) != (char *) NULL ) { X (void) unlink( bp ); X strfree( bp ); X pipename[ fd ] = (char *) NULL; X } X if( (bp = prgname[ fd ]) != (char *) NULL ) { X strfree( bp ); X prgname[ fd ] = (char *) NULL; X } X } X} X X/* X *------------------------------------------------------------------------ X * popen: open a pipe X *------------------------------------------------------------------------ X */ X XFILE *popen( prg, type ) Xchar *prg; /* The command to be run */ Xchar *type; /* "w" or "r" */ X{ X FILE *p = (FILE *) NULL; /* Where we open the pipe */ X int ostdin; /* Where our stdin is now */ X int pipefd = -1; /* fileno( p ) -- for convenience */ X char tmpfile[ BUFSIZ ]; /* Holds name of pipe file */ X char *tmpdir; /* Points to directory prefix of pipe */ X jmp_buf panic; /* Where to go if there's an error */ X int lineno; /* Line number where panic happened */ X /* Find out where we should put temporary files */ X if( (tmpdir = getenv( "TMPDIR" )) == (char *) NULL ) X tmpdir = getenv( "TMP" ); X if( tmpdir != (char *) NULL ) { X /* Use temporary directory if available */ X (void) strcpy( tmpfile, tmpdir ); X (void) strcat( tmpfile, "/" ); X } else *tmpfile = '\0'; X /* Get a unique pipe file name */ X (void) strcat( tmpfile, uniquepipe() ); X if( (lineno = setjmp( panic )) != 0 ) { X /* An error has occurred, so clean up */ X int E = errno; X#ifdef DEMO X fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E ); X#endif DEMO X if( p != (FILE *) NULL ) (void) fclose( p ); X resetpipe( pipefd ); X errno = E; X return( (FILE *) NULL ); X } X if( strcmp( type, "w" ) == 0 ) { X /* for write style pipe, pclose handles program execution */ X if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) { X pipefd = fileno( p ); X pipetype[ pipefd ] = WRITEIT; X pipename[ pipefd ] = strsave( tmpfile ); X prgname[ pipefd ] = strsave( prg ); X if( !pipename[ pipefd ] || !prgname[ pipefd ] ) longjmp( panic, __LINE__ ); X } X } else if( strcmp( type, "r" ) == 0 ) { X /* read pipe must create tmp file, set up stdout to point to the temp X * file, and run the program. note that if the pipe file cannot be X * opened, it'll return a condition indicating pipe failure, which is X * fine. X */ X if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) { X int ostdout; X pipefd = fileno( p ); X pipetype[ pipefd ]= READIT; X if( (pipename[ pipefd ] = strsave( tmpfile )) == (char *) NULL ) X longjmp( panic, __LINE__ ); X /* Redirect stdin for the new command */ X ostdout = dup( fileno( stdout ) ); X if( dup2( fileno( stdout ), pipefd ) < 0 ) { X int E = errno; X (void) dup2( fileno( stdout ), ostdout ); X errno = E; X longjmp( panic, __LINE__ ); X } X if( run( prg ) != 0 ) longjmp( panic, __LINE__ ); X if( dup2( fileno( stdout ), ostdout ) < 0 ) longjmp( panic, __LINE__ ); X if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); X if( (p = fopen( tmpfile, "r" )) == (FILE *) NULL ) longjmp( panic, __LINE__ ); X } X } else { X /* screwy call or unsupported type */ X errno = EINVFNC; X longjmp( panic, __LINE__ ); X } X return( p ); X} X X/* close a pipe */ X Xint Xpclose( p ) XFILE *p; X{ X int pipefd = -1; /* Fildes where pipe is opened */ X int ostdout; /* Where our stdout points now */ X int ostdin; /* Where our stdin points now */ X jmp_buf panic; /* Context to return to if error */ X int lineno; /* Line number where panic happened */ X if( (lineno = setjmp( panic )) != 0 ) { X /* An error has occurred, so clean up and return */ X int E = errno; X#ifdef DEMO X fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E ); X#endif DEMO X if( p != (FILE *) NULL ) (void) fclose( p ); X resetpipe( pipefd ); X errno = E; X return( -1 ); X } X pipefd = fileno( p ); X if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); X switch( pipetype[ pipefd ] ) { X case WRITEIT: X /* open the temp file again as read, redirect stdin from that X * file, run the program, then clean up. X */ X if( (p = fopen( pipename[ pipefd ],"r" )) == (FILE *) NULL ) X longjmp( panic, __LINE__ ); X ostdin = dup( fileno( stdin )); X if( dup2( fileno( stdin ), fileno( p ) ) < 0 ) longjmp( panic, __LINE__ ); X if( run( prgname[ pipefd ] ) != 0 ) longjmp( panic, __LINE__ ); X if( dup2( fileno( stdin ), ostdin ) < 0 ) longjmp( panic, __LINE__ ); X if( fclose( p ) < 0 ) longjmp( panic, __LINE__ ); X resetpipe( pipefd ); X break; X case READIT: X /* close the temp file and remove it */ X resetpipe( pipefd ); X break; X default: X errno = EINVFNC; X longjmp( panic, __LINE__ ); X /*NOTREACHED*/ X } X return( 0 ); X} X X#ifdef DEMO Xint Xmain( argc, argv ) Xint argc; Xchar **argv; X{ X FILE *pipe; X char buf[ BUFSIZ ]; X int n; X *buf = '\0'; X for( n = 1; n < argc; ++n ) { X (void) strcat( buf, argv[ n ] ); X (void) strcat( buf, " " ); X } X if( (pipe = popen( buf, "r" )) != (FILE *) NULL ) { X while( fgets( buf, sizeof( buf ), pipe ) != (char *) NULL ) X (void) fputs( buf, stdout ); X if( pclose( pipe ) != 0 ) fprintf( stderr, "error closing pipe!\n" ); X } else fprintf( stderr, "it didn't work!\n" ); X exit( 0 ); X /*NOTREACHED*/ X} X#endif DEMO SHAR_EOF if test 9895 -ne "`wc -c < 'popen.c'`" then echo shar: "error transmitting 'popen.c'" '(should have been 9895 characters)' fi fi echo shar: "extracting 'popen.h'" '(67 characters)' if test -f 'popen.h' then echo shar: "will not over-write existing file 'popen.h'" else sed 's/^ X//' << \SHAR_EOF > 'popen.h' Xextern FILE *popen( char *, char * ); Xextern int pclose( FILE * ); SHAR_EOF if test 67 -ne "`wc -c < 'popen.h'`" then echo shar: "error transmitting 'popen.h'" '(should have been 67 characters)' fi fi echo shar: "extracting 'turboc.cfg'" '(76 characters)' if test -f 'turboc.cfg' then echo shar: "will not over-write existing file 'turboc.cfg'" else sed 's/^ X//' << \SHAR_EOF > 'turboc.cfg' X-LC:\TURBOC\LIB -IC:\TURBOC\INCLUDE -IC:\TURBOC\INCLUDE\SYS -ms -DTURBOC -M SHAR_EOF if test 76 -ne "`wc -c < 'turboc.cfg'`" then echo shar: "error transmitting 'turboc.cfg'" '(should have been 76 characters)' fi fi exit 0 # End of shell archive