home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / SYSTEM / RBSETNV1.ZIP / POPEN.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-10-18  |  13.7 KB  |  437 lines

  1. /*
  2.  * popen/pclose: simple MS-DOS piping scheme to imitate UNIX pipes
  3.  *
  4.  *  [Obtained from SIMTEL20 as popen.arc - no author name on it
  5.  *   Contains a lot of debugging code, which I left in.]
  6.  *   #define DEBUG to get chatty output.
  7.  *
  8.  * Revision record:
  9.  *   08/10/90 RB
  10.  * - Added checks on length of generated command line before attempting to run
  11.  * - Set O_DENYNONE mode on stdin/stdout before running command
  12.  * - Modified naming the pipe file to include PID to guard against nested execution
  13.  *
  14.  *     03/12/90 RB
  15.  * - Changed strsave/strfree to standard strdup/free
  16.  * - Added #ifdef SWITCH to force switchar to / before running shell
  17.  * - Added set_popen_shell() and set_popen_exec() to select method
  18.  *
  19.  *     10/5/89 RB
  20.  * - Changed check for MKS ksh into explicit check for ksh, as opposed to
  21.  *   anything other than command.com (I use 4dos)
  22.  * - Check for trailing "/" or "\" on $TMP - do not add another
  23.  * - Added a simpler version of run() that does not need COMSPEC or getswitch()
  24.  *   and executes the program directly via spawnvp().  It will work for all but
  25.  *   .BAT files or internal commands
  26.  * - Added my own versions of dup() and dup2()
  27.  * - Changed function declarations to prototypes
  28.  * - Added close ostdout/ostdin (original left them open, using up handles)
  29.  */
  30.  
  31. #include <stdio.h>
  32. #include <ctype.h>
  33. #include <alloc.h>
  34. #include <string.h>
  35. #include <errno.h>
  36. #include <setjmp.h>
  37. #include <process.h>
  38. #include <io.h>
  39. #include <dos.h>
  40. #include <fcntl.h>
  41.  
  42. #include "popen.h"
  43.  
  44. extern char *getenv( char * );
  45. extern int getswitch(void);
  46. extern int setswitch(char);
  47.  
  48. #ifndef    _NFILE
  49. #define    _NFILE        OPEN_MAX        /* Number of open files */
  50. #endif    _NFILE
  51.  
  52. #define READIT        1                /* Read pipe */
  53. #define WRITEIT        2                /* Write pipe */
  54. #define MAXARGLINE    127                /* max length of an msdos command line */
  55. #define MAX_ARGS    64                /* max number of separate arguments */
  56. #define TRUE        1
  57. #define FALSE        0
  58.  
  59. static char *prgname[ _NFILE ];        /* program name if write pipe */
  60. static int pipetype[ _NFILE ];        /* 1=read 2=write */
  61. static char *pipename[ _NFILE ];    /* pipe file name */
  62. static int    use_shell = TRUE;        /* flag for type of exec */
  63.  
  64. /*
  65.  *------------------------------------------------------------------------
  66.  * run: Execute command via SHELL or COMSPEC.  This version will run .EXE
  67.  * .COM .BAT or internal commands of the shell program, also pipelines.
  68.  *------------------------------------------------------------------------
  69.  */
  70. static int
  71. runshell( char *command )
  72. {
  73.     jmp_buf panic;                        /* How to recover from errors */
  74.     int lineno;                            /* Line number where panic happened */
  75.     char *shell;                        /* Command processor */
  76.     char *s = (char *) NULL;            /* Holds the command */
  77.     int s_is_malloced = 0;                /* True if need to free 's' */
  78.     static char *command_com = "COMMAND.COM";
  79.     int status;                            /* Return codes */
  80.     char *shellpath;                    /* Full command processor path */
  81.     char *bp;                            /* Generic string pointer */
  82.     char saveswitch;                    /* Save the switch char */
  83.     static char dash_c[ 3 ] = { '?', 'c', '\0' };
  84.     if( (lineno = setjmp( panic )) != 0 ) {
  85.         int E = errno;
  86. #ifdef    DEBUG
  87.         fprintf( stderr, "RUN panic on line %d: %d\n", lineno, E );
  88. #endif    DEBUG
  89.         if( s_is_malloced && (s != (char *) NULL) ) free( s );
  90.         errno = E;
  91.         return( -1 );
  92.     }
  93.     if( (s = strdup( command )) == (char *) NULL ) longjmp( panic, __LINE__ );
  94.  
  95.     /* Determine the command processor - use SHELL and then COMSPEC */
  96.     if( ((shell = getenv( "SHELL" ))   == (char *) NULL) &&
  97.         ((shell = getenv( "COMSPEC" )) == (char *) NULL) ) shell = command_com;
  98.     strupr( shell );
  99.     shellpath = shell;
  100.  
  101.     /* Strip off any leading backslash directories */
  102.     shell = strrchr( shellpath, '\\' );
  103.     if( shell != (char *) NULL ) ++shell;
  104.     else                         shell = shellpath;
  105.  
  106.     /* Strip off any leading slash directories */
  107.     bp = strrchr( shell, '/'  );
  108.     if( bp != (char *) NULL ) shell = ++bp;
  109.     if( strstr( shell, "KSH" ) != NULL ) {
  110.         /* MKS Shell needs quoted argument */
  111.         char *bp;
  112.         if( (bp = s = malloc( strlen( command ) + 3 )) == (char *) NULL )
  113.              longjmp( panic, __LINE__ );
  114.         *bp++ = '\'';
  115.         while( (*bp++ = *command++) != '\0' );
  116.         *(bp - 1) = '\'';
  117.         *bp = '\0';
  118.         s_is_malloced = 1;
  119.     } else s = command;
  120.     saveswitch = dash_c[ 0 ] = (char) getswitch();
  121. #ifdef SWITCH  /* some shells will not work without '/' so force it here */
  122.     setswitch('/');
  123.     dash_c[ 0 ] = '/';
  124. #endif
  125.     /* Test the length of the generated command line - if too long, fail */
  126.     if (strlen(s) + strlen(shell) + 4 > MAXARGLINE) {
  127. #ifdef DEBUG
  128.         fprintf(stderr,"popen: command line too long\n");
  129. #endif DEBUG
  130.         errno = E2BIG;
  131.         return(-1);
  132.     }
  133.     /* Run the program */
  134. #ifdef    DEBUG
  135.     fprintf( stderr, "Running: (%s) %s %s %s\n", shellpath, shell, dash_c, s );
  136. #endif    DEBUG
  137.     status = spawnl( P_WAIT, shellpath, shell, dash_c, s, (char *) NULL );
  138.     if( s_is_malloced ) free( s );
  139. #ifdef SWITCH                          /* restore switchar if we changed it */
  140.     setswitch(saveswitch);
  141. #endif
  142.     return( status );
  143. }
  144.  
  145.  
  146. /*
  147.  *------------------------------------------------------------------------
  148.  * run: Execute command directly with spawnvp.  This version will only run
  149.  * single .EXE or .COM files but is much faster than using COMSPEC
  150.  *------------------------------------------------------------------------
  151.  */
  152. /*
  153.  * If we know something about the program to be run, we can execute it
  154.  * directly instead of calling the shell, thereby saving some time and memory
  155.  * This routine added by R. Brittain
  156.  */
  157. static int
  158. runexec( char *command )
  159. {
  160.     char *vals[MAX_ARGS];                /* array of pointers to the arguments */
  161.     char cmd[MAXARGLINE+1], *s;
  162.     int  cnt=0, totlen=0, status, i;
  163.  
  164.     /* Copy command and parse argument list. No support for \" in this version */
  165.     s = cmd;
  166.     strcpy(s,command);                    /* copy the argument - we will modify this */
  167.  
  168.     while (TRUE) {
  169.         s += strspn(s, " \t\n");        /* skip leading whitespace */
  170.         if (*s == '\0')
  171.              break;
  172.  
  173.         if (cnt >= MAX_ARGS) {
  174. #ifdef DEBUG
  175.             fprintf(stderr,"popen: too many arguments in call to exec\n");
  176. #endif DEBUG
  177.             return(-1);
  178.         }
  179.         if (*s == '"') {                /* open quote - look for closing quote */
  180.             vals[cnt] = ++s;            /* quotes are not included in argument */
  181.             if ((s = strchr(s, '"')) == NULL)
  182.                 break;
  183.             ++cnt;
  184.             *s++ = '\0';                /* terminate this argument */
  185.         } else {
  186.             vals[cnt++] = s;            /* start of a new argument */
  187.             s += strcspn(s, " \n\t");
  188.             if (*s == '\0')
  189.                 break;
  190.             else
  191.                 *s++ = '\0';
  192.         }
  193.     }
  194.     vals[cnt] = NULL;                    /* terminate array with a null pointer */
  195.     /*
  196.      * Test the final parsed arguments against maximum allowable command line
  197.      * before executing.  If too long, fail.
  198.      */
  199.     for (i=0; vals[i] != NULL; i++) {
  200.         totlen += strlen(vals[i]) + 1;
  201.         if (totlen > MAXARGLINE) {
  202. #ifdef DEBUG
  203.             fprintf(stderr,"popen: command line too long\n");
  204. #endif DEBUG
  205.             errno = E2BIG;
  206.             return(-1);
  207.         }
  208.     }
  209. #ifdef    DEBUG
  210.     fprintf( stderr, "Running: %s\n", command );
  211. #endif    DEBUG
  212.     status = spawnvp( P_WAIT, vals[0], vals);
  213.     return( status );
  214. }
  215.  
  216.  
  217. /*
  218.  *------------------------------------------------------------------------
  219.  * uniquepipe: returns a unique file name to use as a pipe
  220.  *------------------------------------------------------------------------
  221.  */
  222. static char *
  223. uniquepipe(void)
  224. {
  225.     static char name[ 14 ];
  226.     static short int num = 0;
  227.     (void) sprintf( name, "p%04x%03.3d.tmp", getpid(), num++ );
  228.     return( name );
  229. }
  230.  
  231. /*
  232.  *------------------------------------------------------------------------
  233.  * resetpipe: Private routine to cancel a pipe
  234.  *------------------------------------------------------------------------
  235.  */
  236. static void
  237. resetpipe( int fd )
  238. {
  239.     char *bp;
  240.     if( (fd >= 0) && (fd < _NFILE) ) {
  241.         pipetype[ fd ] = 0;
  242.         if( (bp = pipename[ fd ]) != (char *) NULL ) {
  243.             (void) unlink( bp );
  244.             free( bp );
  245.             pipename[ fd ] = (char *) NULL;
  246.         }
  247.         if( (bp = prgname[ fd ]) != (char *) NULL ) {
  248.             free( bp );
  249.             prgname[ fd ] = (char *) NULL;
  250.         }
  251.     }
  252. }
  253.  
  254. /*
  255.  *------------------------------------------------------------------------
  256.  * popen: open a pipe
  257.  *------------------------------------------------------------------------
  258.  */
  259. FILE *popen( char *prg, char *type )
  260. /* char *prg;            The command to be run */
  261. /* char *type;            "w" or "r" */
  262. {
  263.     FILE *p = (FILE *) NULL;            /* Where we open the pipe */
  264.     int pipefd = -1;                    /* fileno( p ) -- for convenience */
  265.     char tmpfile[ BUFSIZ ];                /* Holds name of pipe file */
  266.     char *tmpdir;                        /* Points to directory prefix of pipe */
  267.     jmp_buf panic;                        /* Where to go if there's an error */
  268.     int lineno;                            /* Line number where panic happened */
  269.  
  270.     /* test first for a null argument */
  271.     if (prg == (char *)NULL || *prg == '\0') {
  272.         errno = ENOENT;
  273.         return((FILE *)NULL);
  274.     }
  275.  
  276.     /* Find out where we should put temporary files */
  277.     if( (tmpdir = getenv( "TMPDIR" )) == (char *) NULL )
  278.         tmpdir = getenv( "TMP" );
  279.     if( tmpdir != (char *) NULL ) {
  280.         char c, *tmp;
  281.         /* Use temporary directory if available */
  282.         (void) strcpy( tmpfile, tmpdir );
  283.         /* if there is not a / or a \, add one */
  284.         for (tmp = tmpdir; *tmp != NULL; tmp++) ;
  285.         c = *(--tmp);
  286.         if (c != '/' && c != '\\') (void) strcat( tmpfile, "\\" );
  287.     } else *tmpfile = '\0';
  288.  
  289.     /* Get a unique pipe file name */
  290.     (void) strcat( tmpfile, uniquepipe() );
  291.     if( (lineno = setjmp( panic )) != 0 ) {
  292.         /* An error has occurred, so clean up */
  293.         int E = errno;
  294. #ifdef    DEBUG
  295.         fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E );
  296. #endif    DEBUG
  297.         if( p != (FILE *) NULL ) (void) fclose( p );
  298.         resetpipe( pipefd );
  299.         errno = E;
  300.         return( (FILE *) NULL );
  301.     }
  302.     if( strcmp( type, "w" ) == 0 ) {
  303.         /* for write style pipe, pclose handles program execution */
  304.         if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) {
  305.             pipefd = fileno( p );
  306.             pipetype[ pipefd ] = WRITEIT;
  307.             pipename[ pipefd ] = strdup( tmpfile );
  308.             prgname[ pipefd ]  = strdup( prg );
  309.             if( !pipename[ pipefd ] || !prgname[ pipefd ] ) longjmp( panic, __LINE__ );
  310. #ifdef    DEBUG
  311.             fprintf( stderr, "Popen: create file %s for writing\n", tmpfile);
  312. #endif    DEBUG
  313.         }
  314.     } else if( strcmp( type, "r" ) == 0 ) {
  315.         /*
  316.          * read pipe must create tmp file, set up stdout to point to the temp
  317.          * file, and run the program.  note that if the pipe file cannot be
  318.          * opened, it'll return a condition indicating pipe failure, which is
  319.          * fine.
  320.          */
  321.         if( (p = fopen( tmpfile, "w" )) != (FILE *) NULL ) {
  322.             int ostdout;
  323.             pipefd = fileno( p );
  324.             pipetype[ pipefd ]= READIT;
  325.             if( (pipename[ pipefd ] = strdup( tmpfile )) == (char *) NULL )
  326.                 longjmp( panic, __LINE__ );
  327.  
  328.             /* Redirect stdin for the new command */
  329.             ostdout = dup( fileno( stdout ) );
  330.             if( dup2( fileno( stdout ), pipefd ) < 0 ) {
  331.                 int E = errno;
  332.                 (void) dup2( fileno( stdout ), ostdout );
  333.                 errno = E;
  334.                 longjmp( panic, __LINE__ );
  335.             }
  336. #ifdef    DEBUG
  337.             fprintf( stderr, "Popen: create file %s for reading\n", tmpfile);
  338. #endif    DEBUG
  339.             if  (_osmajor > 2) {
  340.                 /* set the write and sharing mode on stdout */
  341.                 setmode( fileno( stdout ), O_WRONLY | O_DENYNONE);
  342.                 setmode( pipefd, O_NOINHERIT);
  343.             }
  344.             if (use_shell) {
  345.                 if (runshell( prg ) != 0 ) longjmp( panic, __LINE__ );
  346.             } else {
  347.                 if (runexec( prg ) != 0 ) longjmp( panic, __LINE__ );
  348.             }
  349. #ifdef    DEBUG
  350.             fprintf( stderr, "Popen: child process \"%s\" complete\n", prg);
  351. #endif    DEBUG
  352.             if( dup2( fileno( stdout ), ostdout ) < 0 ) longjmp( panic, __LINE__ );
  353.             if( fclose( p ) < 0 ) longjmp( panic, __LINE__ );
  354.             if( close( ostdout ) < 0 ) longjmp( panic, __LINE__ );
  355.             if( (p = fopen( tmpfile, "r" )) == (FILE *) NULL ) longjmp( panic, __LINE__ );
  356.         }
  357.     } else {
  358.         /* screwy call or unsupported type */
  359.         errno = EINVFNC;
  360.         longjmp( panic, __LINE__ );
  361.     }
  362.     return( p );
  363. }
  364.  
  365. /* close a pipe */
  366. int
  367. pclose( FILE *p )
  368. {
  369.     int pipefd = -1;                /* Fildes where pipe is opened */
  370.     int ostdin;                        /* Where our stdin points now */
  371.     jmp_buf panic;                    /* Context to return to if error */
  372.     int lineno;                        /* Line number where panic happened */
  373.  
  374.     if( (lineno = setjmp( panic )) != 0 ) {
  375.         /* An error has occurred, so clean up and return */
  376.         int E = errno;
  377. #ifdef    DEBUG
  378.         fprintf( stderr, "POPEN panic on line %d: %d\n", lineno, E );
  379. #endif    DEBUG
  380.         if( p != (FILE *) NULL ) (void) fclose( p );
  381.         resetpipe( pipefd );
  382.         errno = E;
  383.         return( -1 );
  384.     }
  385.     pipefd = fileno( p );
  386.     if( fclose( p ) < 0 ) longjmp( panic, __LINE__ );
  387.     switch( pipetype[ pipefd ] ) {
  388.     case WRITEIT:
  389.         /*
  390.          * open the temp file again as read, redirect stdin from that
  391.          * file, run the program, then clean up.
  392.          */
  393.         if ( (p = fopen( pipename[ pipefd ],"r" )) == (FILE *) NULL )
  394.             longjmp( panic,__LINE__);
  395.         ostdin = dup( fileno( stdin ));
  396.         if (dup2( fileno( stdin ), fileno( p )) < 0 ) longjmp( panic,__LINE__);
  397.         if( fclose( p ) < 0 ) longjmp( panic, __LINE__ );
  398.         if  (_osmajor > 2) {
  399.             /* set the write and sharing mode on stdout */
  400.             setmode( fileno( stdin ), O_RDONLY | O_DENYNONE);
  401.             setmode( pipefd, O_NOINHERIT);
  402.         }
  403.  
  404.         if (use_shell) {
  405.             if( runshell( prgname[ pipefd ] ) != 0 ) longjmp( panic, __LINE__ );
  406.         } else {
  407.             if( runexec( prgname[ pipefd ] ) != 0 ) longjmp( panic, __LINE__ );
  408.         }
  409. #ifdef    DEBUG
  410.         fprintf( stderr, "Popen: child process \"%s\" complete\n", prgname[pipefd]);
  411. #endif    DEBUG
  412.         if( dup2( fileno( stdin ), ostdin ) < 0 ) longjmp( panic, __LINE__ );
  413.         if( close( ostdin ) < 0 ) longjmp( panic, __LINE__ );
  414.         resetpipe( pipefd );
  415.         break;
  416.     case READIT:
  417.         /* close the temp file and remove it */
  418.         resetpipe( pipefd );
  419.         break;
  420.     default:
  421.         errno = EINVFNC;
  422.         longjmp( panic, __LINE__ );
  423.         /*NOTREACHED*/
  424.     }
  425.     return( 0 );
  426. }
  427.  
  428. void set_popen_shell()
  429. {
  430.     use_shell = TRUE;
  431. }
  432.  
  433. void set_popen_exec()
  434. {
  435.     use_shell = FALSE;
  436. }
  437.