home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 September / Simtel20_Sept92.cdr / msdos / turbo_c / popen.arc / POPEN.C < prev    next >
C/C++ Source or Header  |  1988-03-08  |  10KB  |  329 lines

  1. /* popen/pclose:
  2.  *
  3.  * simple MS-DOS piping scheme to imitate UNIX pipes
  4.  */
  5.  
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <alloc.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <setjmp.h>
  12. #include <process.h>
  13.  
  14. #include "popen.h"
  15.  
  16. extern char *getenv( char * );
  17.  
  18. #ifndef    _NFILE
  19. # define    _NFILE    OPEN_MAX    /* Number of open files */
  20. #endif    _NFILE
  21.  
  22. #define READIT    1            /* Read pipe */
  23. #define WRITEIT    2            /* Write pipe */
  24.  
  25. static char *prgname[ _NFILE ];        /* program name if write pipe */
  26. static int pipetype[ _NFILE ];        /* 1=read 2=write */
  27. static char *pipename[ _NFILE ];    /* pipe file name */
  28.  
  29. /*
  30.  *------------------------------------------------------------------------
  31.  * stoupper: Convert string to uppercase (in place)
  32.  *------------------------------------------------------------------------
  33.  */
  34.  
  35. static void
  36. stoupper( s )
  37. char *s;
  38. {
  39.    int c;
  40.    for( ; (c = *s) != '\0'; ++s ) {
  41.       if( islower( c ) ) *s = _toupper( c );
  42.    }
  43. }
  44.  
  45. /*
  46.  *------------------------------------------------------------------------
  47.  * strsave: Copy string into malloc'ed memory and return address
  48.  *------------------------------------------------------------------------
  49.  */
  50.  
  51. static char *
  52. strsave( s )
  53. char *s;
  54. {
  55.    char *sp = malloc( strlen( s ) + 1 );
  56.    if( sp != (char *) NULL ) (void) strcpy( sp, s );
  57.    return( sp );
  58. }
  59.  
  60. /*
  61.  *------------------------------------------------------------------------
  62.  * strfree: Returm strsave'd string memory
  63.  *------------------------------------------------------------------------
  64.  */
  65.  
  66. static void
  67. strfree( s )
  68. char *s;
  69. {
  70.    if( s != (char *) NULL ) free( s );
  71. }
  72.  
  73. /*
  74.  *------------------------------------------------------------------------
  75.  * run: Execute command via SHELL or COMSPEC
  76.  *------------------------------------------------------------------------
  77.  */
  78.  
  79. static int
  80. run( command )
  81. char *command;
  82. {
  83.    jmp_buf panic;            /* How to recover from errors */
  84.    int lineno;                /* Line number where panic happened */
  85.    char *shell;                /* Command processor */
  86.    char *s = (char *) NULL;        /* Holds the command */
  87.    int s_is_malloced = 0;        /* True if need to free 's' */
  88.    static char *command_com = "COMMAND.COM";
  89.    int status;                /* Return codes */
  90.    char *shellpath;            /* Full command processor path */
  91.    char *bp;                /* Generic string pointer */
  92.    static char dash_c[ 3 ] = { '?', 'c', '\0' };
  93.    if( (lineno = setjmp( panic )) != 0 ) {
  94.       int E = errno;
  95. #ifdef    DEMO
  96.       fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E );
  97. #endif    DEMO
  98.       if( s_is_malloced && (s != (char *) NULL) ) strfree( s );
  99.       errno = E;
  100.       return( -1 );
  101.    }
  102.    if( (s = strsave( command )) == (char *) NULL ) longjmp( panic, __LINE__ );
  103.    /* Determine the command processor */
  104.    if( ((shell = getenv( "SHELL" )) == (char *) NULL) &&
  105.        ((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com;
  106.    stoupper( shell );
  107.    shellpath = shell;
  108.    /* Strip off any leading backslash directories */
  109.    shell = strrchr( shellpath, '\\' );
  110.    if( shell != (char *) NULL ) ++shell;
  111.    else                         shell = shellpath;
  112.    /* Strip off any leading slash directories */
  113.    bp = strrchr( shell, '/'  );
  114.    if( bp != (char *) NULL ) shell = ++bp;
  115.    if( strcmp( shell, command_com ) != 0 ) {
  116.       /* MKS Shell needs quoted argument */
  117.       char *bp;
  118.       if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL ) 
  119.      longjmp( panic, __LINE__ );
  120.       *bp++ = '\'';
  121.       while( (*bp++ = *command++) != '\0' );
  122.       *(bp - 1) = '\'';
  123.       *bp = '\0';
  124.       s_is_malloced = 1;
  125.    } else s = command;
  126.    dash_c[ 0 ] = getswitch();
  127.    /* Run the program */
  128. #ifdef    DEMO
  129.    fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s );
  130. #endif    DEMO
  131.    status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL );
  132.    if( s_is_malloced ) free( s );
  133.    return( status );
  134. }
  135.  
  136. /* 
  137.  *------------------------------------------------------------------------
  138.  * uniquepipe: returns a unique file name 
  139.  *------------------------------------------------------------------------
  140.  */
  141.  
  142. static char *
  143. uniquepipe()
  144.    static char name[ 14 ];
  145.    static short int num = 0;
  146.    (void) sprintf( name, "pipe%05d.tmp", num++ );
  147.    return( name );
  148. }
  149.  
  150. /*
  151.  *------------------------------------------------------------------------
  152.  * resetpipe: Private routine to cancel a pipe
  153.  *------------------------------------------------------------------------
  154.  */
  155.  
  156. static void
  157. resetpipe( fd )
  158. int fd;
  159. {
  160.    char *bp;
  161.    if( (fd >= 0) && (fd < _NFILE) ) {
  162.       pipetype[ fd ] = 0;
  163.       if( (bp = pipename[ fd ]) != (char *) NULL ) {
  164.      (void) unlink( bp );
  165.      strfree( bp );
  166.      pipename[ fd ] = (char *) NULL;
  167.       }
  168.       if( (bp = prgname[ fd ]) != (char *) NULL ) {
  169.      strfree( bp );
  170.      prgname[ fd ] = (char *) NULL;
  171.       }
  172.    }
  173. }
  174.  
  175. /* 
  176.  *------------------------------------------------------------------------
  177.  * popen: open a pipe 
  178.  *------------------------------------------------------------------------
  179.  */
  180.  
  181. FILE *popen( prg, type )
  182. char *prg;            /* The command to be run */
  183. char *type;            /* "w" or "r" */
  184.    FILE *p = (FILE *) NULL;    /* Where we open the pipe */
  185.    int ostdin;            /* Where our stdin is now */
  186.    int pipefd = -1;        /* fileno( p ) -- for convenience */
  187.    char tmpfile[ BUFSIZ ];    /* Holds name of pipe file */
  188.    char *tmpdir;        /* Points to directory prefix of pipe */
  189.    jmp_buf panic;        /* Where to go if there's an error */
  190.    int lineno;            /* Line number where panic happened */
  191.    /* Find out where we should put temporary files */
  192.    if( (tmpdir = getenv( "TMPDIR" )) == (char *) NULL ) 
  193.       tmpdir = getenv( "TMP" );
  194.    if( tmpdir != (char *) NULL ) {
  195.       /* Use temporary directory if available */
  196.       (void) strcpy( tmpfile, tmpdir );
  197.       (void) strcat( tmpfile, "/" );
  198.    } else *tmpfile = '\0';
  199.    /* Get a unique pipe file name */
  200.    (void) strcat( tmpfile, uniquepipe() );
  201.    if( (lineno = setjmp( panic )) != 0 ) {
  202.       /* An error has occurred, so clean up */
  203.       int E = errno;
  204. #ifdef    DEMO
  205.       fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E );
  206. #endif    DEMO
  207.       if( p != (FILE *) NULL ) (void) fclose( p );
  208.       resetpipe( pipefd );
  209.       errno = E;
  210.       return( (FILE *) NULL );
  211.    }
  212.    if( strcmp( type, "w" ) == 0 ) {
  213.       /* for write style pipe, pclose handles program execution */
  214.       if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) {
  215.      pipefd = fileno( p );
  216.      pipetype[ pipefd ] = WRITEIT;
  217.      pipename[ pipefd ] = strsave( tmpfile );
  218.      prgname[ pipefd ]  = strsave( prg );
  219.      if( !pipename[ pipefd ] || !prgname[ pipefd ] ) longjmp( panic, __LINE__ );
  220.       }
  221.    } else if( strcmp( type, "r" ) == 0 ) {
  222.       /* read pipe must create tmp file, set up stdout to point to the temp
  223.       * file, and run the program.  note that if the pipe file cannot be
  224.       * opened, it'll return a condition indicating pipe failure, which is
  225.       * fine.
  226.       */
  227.       if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) {
  228.      int ostdout;
  229.      pipefd = fileno( p );
  230.      pipetype[ pipefd ]= READIT;
  231.      if( (pipename[ pipefd ] = strsave( tmpfile )) == (char *) NULL ) 
  232.         longjmp( panic, __LINE__ );
  233.      /* Redirect stdin for the new command */
  234.      ostdout = dup( fileno( stdout ) );
  235.      if( dup2( fileno( stdout ), pipefd ) < 0 ) {
  236.         int E = errno;
  237.         (void) dup2( fileno( stdout ), ostdout );
  238.         errno = E;
  239.         longjmp( panic, __LINE__ );
  240.      }
  241.      if( run( prg ) != 0 ) longjmp( panic, __LINE__ );
  242.      if( dup2( fileno( stdout ), ostdout ) < 0 ) longjmp( panic, __LINE__ );
  243.      if( fclose( p ) < 0 ) longjmp( panic, __LINE__ );
  244.      if( (p = fopen( tmpfile, "r" )) == (FILE *) NULL ) longjmp( panic, __LINE__ );
  245.       }
  246.    } else {
  247.       /* screwy call or unsupported type */
  248.       errno = EINVFNC;
  249.       longjmp( panic, __LINE__ );
  250.    }
  251.    return( p );
  252. }
  253.  
  254. /* close a pipe */
  255.  
  256. int
  257. pclose( p )
  258. FILE *p;
  259. {
  260.    int pipefd = -1;        /* Fildes where pipe is opened */
  261.    int ostdout;            /* Where our stdout points now */
  262.    int ostdin;            /* Where our stdin points now */
  263.    jmp_buf panic;        /* Context to return to if error */
  264.    int lineno;            /* Line number where panic happened */
  265.    if( (lineno = setjmp( panic )) != 0 ) {
  266.       /* An error has occurred, so clean up and return */
  267.       int E = errno;
  268. #ifdef    DEMO
  269.       fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E );
  270. #endif    DEMO
  271.       if( p != (FILE *) NULL ) (void) fclose( p );
  272.       resetpipe( pipefd );
  273.       errno = E;
  274.       return( -1 );
  275.    }
  276.    pipefd = fileno( p );
  277.    if( fclose( p ) < 0 ) longjmp( panic, __LINE__ );
  278.    switch( pipetype[ pipefd ] ) {
  279.       case WRITEIT:
  280.      /* open the temp file again as read, redirect stdin from that
  281.       * file, run the program, then clean up.
  282.       */
  283.       if( (p = fopen( pipename[ pipefd ],"r" )) == (FILE *) NULL ) 
  284.      longjmp( panic, __LINE__ );
  285.       ostdin = dup( fileno( stdin ));
  286.       if( dup2( fileno( stdin ), fileno( p ) ) < 0 ) longjmp( panic, __LINE__ );
  287.       if( run( prgname[ pipefd ] ) != 0 ) longjmp( panic, __LINE__ );
  288.       if( dup2( fileno( stdin ), ostdin ) < 0 ) longjmp( panic, __LINE__ );
  289.       if( fclose( p ) < 0 ) longjmp( panic, __LINE__ );
  290.       resetpipe( pipefd );
  291.       break;
  292.    case READIT:
  293.       /* close the temp file and remove it */
  294.       resetpipe( pipefd );
  295.       break;
  296.    default:
  297.       errno = EINVFNC;
  298.       longjmp( panic, __LINE__ );
  299.       /*NOTREACHED*/
  300.    }
  301.    return( 0 );
  302. }
  303.  
  304. #ifdef    DEMO
  305. int
  306. main( argc, argv )
  307. int argc;
  308. char **argv;
  309. {
  310.    FILE *pipe;
  311.    char buf[ BUFSIZ ];
  312.    int n;
  313.    *buf = '\0';
  314.    for( n = 1; n < argc; ++n ) {
  315.       (void) strcat( buf, argv[ n ] );
  316.       (void) strcat( buf, " " );
  317.    }
  318.    if( (pipe = popen( buf, "r" )) != (FILE *) NULL ) {
  319.       while( fgets( buf, sizeof( buf ), pipe ) != (char *) NULL ) 
  320.      (void) fputs( buf, stdout );
  321.       if( pclose( pipe ) != 0 ) fprintf( stderr, "error closing pipe!\n" );
  322.    } else fprintf( stderr, "it didn't work!\n" );
  323.    exit( 0 );
  324.    /*NOTREACHED*/
  325. }
  326. #endif    DEMO
  327.