Example #1
0
int
make1( TARGET *t )
{
	memset( (char *)counts, 0, sizeof( *counts ) );

#ifdef OPT_SERIAL_OUTPUT_EXT
	exec_init();
#endif
	/* Recursively make the target and its dependents */

	make1a( t, (TARGET *)0 );

	/* Wait for any outstanding commands to finish running. */

	while( execwait() )
	    ;

#ifdef OPT_SERIAL_OUTPUT_EXT
	exec_done();
#endif
	/* Talk about it */

	if( DEBUG_MAKE && counts->failed )
	    printf( "*** failed updating %d target(s)...\n", counts->failed );

	if( DEBUG_MAKE && counts->skipped )
	    printf( "*** skipped %d target(s)...\n", counts->skipped );

	if( DEBUG_MAKE && counts->made )
	    printf( "*** updated %d target(s)...\n", counts->made );

	return counts->total != counts->made;
}
Example #2
0
int
make1( TARGET *t )
{
	state *pState;

	memset( (char *)counts, 0, sizeof( *counts ) );

	/* Recursively make the target and its dependents */
	push_state(&state_stack, t, NULL, T_STATE_MAKE1A);

	do
	{
		while((pState = current_state(&state_stack)) != NULL)
		{
            if (intr) 
                pop_state(&state_stack);

			switch(pState->curstate)
			{
			case T_STATE_MAKE1A:
				make1a(pState);
				break;
			case T_STATE_MAKE1ATAIL:
				make1atail(pState);
				break;
			case T_STATE_MAKE1B:
				make1b(pState);
				break;
			case T_STATE_MAKE1C:
				make1c(pState);
				break;
			case T_STATE_MAKE1D:
				make1d(pState);
				break;
			default:
				break;
			}
		}
	

	/* Wait for any outstanding commands to finish running. */
	} while( execwait() );

	clear_state_freelist();

	/* Talk about it */
	if( counts->failed )
	    printf( "...failed updating %d target%s...\n", counts->failed,
		        counts->failed > 1 ? "s" : "" );

	if( DEBUG_MAKE && counts->skipped )
	    printf( "...skipped %d target%s...\n", counts->skipped,
		        counts->skipped > 1 ? "s" : "" );

	if( DEBUG_MAKE && counts->made )
	    printf( "...updated %d target%s...\n", counts->made,
		        counts->made > 1 ? "s" : "" );

	return counts->total != counts->made;
}
Example #3
0
LIST *
builtin_shell(
	PARSE	*parse,
	LOL	*args,
	int	*jmp )
{
    LIST *l = lol_get( args, 0 );
    LIST *shell = var_get( "JAMSHELL" );	/* shell is per-target */

    LIST *output = L0;

    exec_init();
    for( ; l; l = list_next( l ) ) {
        execcmd( l->string, shell_done, &output, shell, 1 );
	execwait();
    }
    exec_done();

    return output;
}
Example #4
0
void
execcmd( 
	char *string,
	void (*func)( void *closure, int status ),
	void *closure,
	LIST *shell )
{
	int pid;
	int slot;
	char *argv[ MAXARGC + 1 ];	/* +1 for NULL */

# ifdef USE_EXECNT
	char *p;
# endif

	/* Find a slot in the running commands table for this one. */

	for( slot = 0; slot < MAXJOBS; slot++ )
	    if( !cmdtab[ slot ].pid )
		break;

	if( slot == MAXJOBS )
	{
	    printf( "no slots for child!\n" );
	    exit( EXITBAD );
	}

# ifdef USE_EXECNT
	if( !cmdtab[ slot ].tempfile )
	{
	    char *tempdir;

	    if( !( tempdir = getenv( "TEMP" ) ) &&
		!( tempdir = getenv( "TMP" ) ) )
		    tempdir = "\\temp";

	    cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 );

	    sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat", 
				tempdir, slot );
	}

	/* Trim leading, ending white space */

	while( isspace( *string ) )
		++string;

	p = strchr( string, '\n' );

	while( p && isspace( *p ) )
		++p;

	/* If multi line, or too long, or JAMSHELL is set, write to bat file. */
	/* Otherwise, exec directly. */
	/* Frankly, if it is a single long line I don't think the */
	/* command interpreter will do any better -- it will fail. */

	if( p && *p || strlen( string ) > MAXLINE || shell )
	{
	    FILE *f;

	    /* Write command to bat file. */

	    f = fopen( cmdtab[ slot ].tempfile, "w" );
	    fputs( string, f );
	    fclose( f );

	    string = cmdtab[ slot ].tempfile;
	}
# endif

	/* Forumulate argv */
	/* If shell was defined, be prepared for % and ! subs. */
	/* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */

	if( shell )
	{
	    int i;
	    char jobno[4];
	    int gotpercent = 0;

	    sprintf( jobno, "%d", slot + 1 );

	    for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
	    {
		switch( shell->string[0] )
		{
		case '%':	argv[i] = string; gotpercent++; break;
		case '!':	argv[i] = jobno; break;
		default:	argv[i] = shell->string;
		}
		if( DEBUG_EXECCMD )
		    printf( "argv[%d] = '%s'\n", i, argv[i] );
	    }

	    if( !gotpercent )
		argv[i++] = string;

	    argv[i] = 0;
	}
	else
	{
# ifdef USE_EXECNT
	    argv[0] = "cmd.exe";
	    argv[1] = "/Q/C";		/* anything more is non-portable */
# else
	    argv[0] = "/bin/sh";
	    argv[1] = "-c";
# endif
	    argv[2] = string;
	    argv[3] = 0;
	}

	/* Catch interrupts whenever commands are running. */

	if( !cmdsrunning++ )
	    istat = signal( SIGINT, onintr );

	/* Start the command */

# ifdef USE_EXECNT
	if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
	{
	    perror( "spawn" );
	    exit( EXITBAD );
	}
# else
	if ((pid = vfork()) == 0) 
   	{
		execvp( argv[0], argv );
		_exit(127);
	}

	if( pid == -1 )
	{
	    perror( "vfork" );
	    exit( EXITBAD );
	}
# endif
	/* Save the operation for execwait() to find. */

	cmdtab[ slot ].pid = pid;
	cmdtab[ slot ].func = func;
	cmdtab[ slot ].closure = closure;

	/* Wait until we're under the limit of concurrent commands. */
	/* Don't trust globs.jobs alone. */

	while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
	    if( !execwait() )
		break;
}
Example #5
0
void
execcmd( 
	char *string,
	void (*func)( void *closure, int status, timing_info*, char *, char * ),
	void *closure,
	LIST *shell,
        char *action,
        char *target )
{
        static int initialized = 0;
        int out[2], err[2];
	int slot, len;
	char *argv[ MAXARGC + 1 ];	/* +1 for NULL */

	/* Find a slot in the running commands table for this one. */

	for( slot = 0; slot < MAXJOBS; slot++ )
	    if( !cmdtab[ slot ].pid )
		break;

	if( slot == MAXJOBS )
	{
	    printf( "no slots for child!\n" );
	    exit( EXITBAD );
	}

	/* Forumulate argv */
	/* If shell was defined, be prepared for % and ! subs. */
	/* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */

	if( shell )
	{
	    int i;
	    char jobno[4];
	    int gotpercent = 0;

	    sprintf( jobno, "%d", slot + 1 );

	    for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
	    {
		switch( shell->string[0] )
		{
		case '%':	argv[i] = string; gotpercent++; break;
		case '!':	argv[i] = jobno; break;
		default:	argv[i] = shell->string;
		}
		if( DEBUG_EXECCMD )
		    printf( "argv[%d] = '%s'\n", i, argv[i] );
	    }

	    if( !gotpercent )
		argv[i++] = string;

	    argv[i] = 0;
	}
	else
	{
	    argv[0] = "/bin/sh";
	    argv[1] = "-c";
	    argv[2] = string;
	    argv[3] = 0;
	}

	/* increment jobs running */
	++cmdsrunning;

        /* save off actual command string */
        cmdtab[ slot ].command = BJAM_MALLOC_ATOMIC(strlen(string)+1);
        strcpy(cmdtab[slot].command, string);

        /* initialize only once */
        if ( ! initialized ) {
          times(&old_time);
          initialized = 1;
        }

        /* create pipe from child to parent */

        if (pipe(out) < 0)
            exit(EXITBAD);

        fcntl(out[0], F_SETFL, O_NONBLOCK);
        fcntl(out[1], F_SETFL, O_NONBLOCK);

        if (pipe(err) < 0)
            exit(EXITBAD);

        fcntl(err[0], F_SETFL, O_NONBLOCK);
        fcntl(err[1], F_SETFL, O_NONBLOCK);

	/* Start the command */

        cmdtab[ slot ].start_dt = time(0);

        if (0 < globs.timeout) {
            /* 
             * handle hung processes by manually tracking elapsed 
             * time and signal process when time limit expires
             */
            struct tms buf;
            cmdtab[ slot ].start_time = times(&buf);

            /* make a global, only do this once */
            if (tps == 0) tps = sysconf(_SC_CLK_TCK);                
        }

	if ((cmdtab[slot].pid = vfork()) == 0) 
   	{
            int pid = getpid();

            close(out[0]);
            close(err[0]);

            dup2(out[1], STDOUT_FILENO);

            if (globs.pipe_action == 0)
            {
                dup2(out[1], STDERR_FILENO);
                close(err[1]);
            }
            else
                dup2(err[1], STDERR_FILENO);

            /* Make this process a process group leader
             * so that when we kill it, all child
             * processes of this process are terminated
             * as well.
             *
             * we use killpg(pid, SIGKILL) to kill the
             * process group leader and all its children.
             */
             if (0 < globs.timeout)
             {
                struct rlimit r_limit;
                r_limit.rlim_cur = globs.timeout;
                r_limit.rlim_max = globs.timeout;
                setrlimit(RLIMIT_CPU, &r_limit);
            }
            setpgid(pid,pid);
            execvp( argv[0], argv );
            perror( "execvp" );
            _exit(127);
        }
        else if( cmdtab[slot].pid == -1 )
	{
	    perror( "vfork" );
	    exit( EXITBAD );
	}

        setpgid(cmdtab[slot].pid, cmdtab[slot].pid);

        /* close write end of pipes */
        close(out[1]); 
        close(err[1]); 

        /* child writes stdout to out[1], parent reads from out[0] */
	cmdtab[slot].fd[OUT] = out[0];
	cmdtab[slot].stream[OUT] = fdopen(cmdtab[slot].fd[OUT], "rb");
        if (cmdtab[slot].stream[OUT] == NULL) {
            perror( "fdopen" );
            exit( EXITBAD );
        }

        /* child writes stderr to err[1], parent reads from err[0] */
        if (globs.pipe_action == 0)
        {
          close(err[0]);
        }
        else
        {
	    cmdtab[slot].fd[ERR] = err[0];
	    cmdtab[slot].stream[ERR] = fdopen(cmdtab[slot].fd[ERR], "rb");
            if (cmdtab[slot].stream[ERR] == NULL) {
                perror( "fdopen" );
                exit( EXITBAD );
            }
        }

        /* ensure enough room for rule and target name */

        if (action && target)
        {
            len = strlen(action) + 1;
            if (cmdtab[slot].action_length < len) 
            {
                BJAM_FREE(cmdtab[ slot ].action);
                cmdtab[ slot ].action = BJAM_MALLOC_ATOMIC(len);
                cmdtab[ slot ].action_length = len;
            }
            strcpy(cmdtab[ slot ].action, action);
            len = strlen(target) + 1;
            if (cmdtab[slot].target_length < len) 
            {
                BJAM_FREE(cmdtab[ slot ].target);
                cmdtab[ slot ].target = BJAM_MALLOC_ATOMIC(len);
                cmdtab[ slot ].target_length = len;
            }
            strcpy(cmdtab[ slot ].target, target);
        }
        else
        {
            BJAM_FREE(cmdtab[ slot ].action);
            BJAM_FREE(cmdtab[ slot ].target);
            cmdtab[ slot ].action = 0;
            cmdtab[ slot ].target = 0;
            cmdtab[ slot ].action_length = 0;
            cmdtab[ slot ].target_length = 0;
        }

	/* Save the operation for execwait() to find. */

	cmdtab[ slot ].func = func;
	cmdtab[ slot ].closure = closure;

	/* Wait until we're under the limit of concurrent commands. */
	/* Don't trust globs.jobs alone. */

        while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
            if( !execwait() )
                break;
}
Example #6
0
void
execcmd( 
	char *string,
	void (*func)( void *closure, int status, timing_info* ),
	void *closure,
	LIST *shell )
{
    int pid;
    int slot;
    int raw_cmd = 0 ;
    char *argv_static[ MAXARGC + 1 ];	/* +1 for NULL */
    char **argv = argv_static;
    char *p;

    /* Check to see if we need to hack around the line-length limitation. */
    /* Look for a JAMSHELL setting of "%", indicating that the command
     * should be invoked directly */
    if ( shell && !strcmp(shell->string,"%") && !list_next(shell) )
    {
        raw_cmd = 1;
        shell = 0;
    }

    if ( !is_win95_defined )
        set_is_win95();
          
    /* Find a slot in the running commands table for this one. */
    if ( is_win95 )
    {
        /* only synchronous spans are supported on Windows 95/98 */
        slot = 0;
    }
    else
    {
        for( slot = 0; slot < MAXJOBS; slot++ )
            if( !cmdtab[ slot ].pid )
                break;
    }
    if( slot == MAXJOBS )
    {
        printf( "no slots for child!\n" );
        exit( EXITBAD );
    }
  
    if( !cmdtab[ slot ].tempfile )
    {
        const char *tempdir = path_tmpdir();
        DWORD procID = GetCurrentProcessId();
  
        /* SVA - allocate 64 other just to be safe */
        cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 );
        if ( DEBUG_PROFILE )
            profile_memory( strlen( tempdir ) + 64 );
  
        sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat", 
                 tempdir, procID, slot );		
    }

    /* Trim leading, ending white space */

    while( isspace( *string ) )
        ++string;

    /* Write to .BAT file unless the line would be too long and it
     * meets the other spawnability criteria.
     */
    if( raw_cmd && can_spawn( string ) >= MAXLINE )
    {
        if( DEBUG_EXECCMD )
            printf("Executing raw command directly\n");        
    }
    else
    {
        FILE *f = 0;
        int tries = 0;
        raw_cmd = 0;
        
        /* Write command to bat file. For some reason this open can
           fails intermitently. But doing some retries works. Most likely
           this is due to a previously existing file of the same name that
           happens to be opened by an active virus scanner. Pointed out,
           and fix by Bronek Kozicki. */
        for (; !f && tries < 4; ++tries)
        {
            f = fopen( cmdtab[ slot ].tempfile, "w" );
            if ( !f && tries < 4 ) Sleep( 250 );
        }
        if (!f)
        {
            printf( "failed to write command file!\n" );
            exit( EXITBAD );
        }
        fputs( string, f );
        fclose( f );

        string = cmdtab[ slot ].tempfile;
        
        if( DEBUG_EXECCMD )
        {
            if (shell)
                printf("using user-specified shell: %s", shell->string);
            else
                printf("Executing through .bat file\n");
        }
    }

    /* Forumulate argv */
    /* If shell was defined, be prepared for % and ! subs. */
    /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */

    if( shell )
    {
        int i;
        char jobno[4];
        int gotpercent = 0;

        sprintf( jobno, "%d", slot + 1 );

        for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
        {
            switch( shell->string[0] )
            {
            case '%':	argv[i] = string; gotpercent++; break;
            case '!':	argv[i] = jobno; break;
            default:	argv[i] = shell->string;
            }
            if( DEBUG_EXECCMD )
                printf( "argv[%d] = '%s'\n", i, argv[i] );
        }

        if( !gotpercent )
            argv[i++] = string;

        argv[i] = 0;
    }
    else if (raw_cmd)
    {
        argv = string_to_args(string);
    }
    else
    {
        /* don't worry, this is ignored on Win95/98, see later.. */
        argv[0] = "cmd.exe";
        argv[1] = "/Q/C";		/* anything more is non-portable */
        argv[2] = string;
        argv[3] = 0;
    }

    /* Catch interrupts whenever commands are running. */

    if( !cmdsrunning++ )
        istat = signal( SIGINT, onintr );

    /* Start the command */

    /* on Win95, we only do a synchronous call */
    if ( is_win95 )
    {
        static const char* hard_coded[] =
            {
                "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir",
                "ren", "rename", "move", 0
            };
          
        const char**  keyword;
        int           len, spawn = 1;
        int           result;
        timing_info time = {0,0};
          
        for ( keyword = hard_coded; keyword[0]; keyword++ )
        {
            len = strlen( keyword[0] );
            if ( strnicmp( string, keyword[0], len ) == 0 &&
                 !isalnum(string[len]) )
            {
                /* this is one of the hard coded symbols, use 'system' to run */
                /* them.. except for "del"/"erase"                            */
                if ( keyword - hard_coded < 2 )
                    result = process_del( string );
                else
                    result = system( string );

                spawn  = 0;
                break;
            }
        }
          
        if (spawn)
        {
            char**  args;
            
            /* convert the string into an array of arguments */
            /* we need to take care of double quotes !!      */
            args = string_to_args( string );
            if ( args )
            {
#if 0
                char** arg;
                fprintf( stderr, "%s: ", args[0] );
                arg = args+1;
                while ( arg[0] )
                {
                    fprintf( stderr, " {%s}", arg[0] );
                    arg++;
                }
                fprintf( stderr, "\n" );
#endif              
                result = spawnvp( P_WAIT, args[0], args );
                record_times(result, &time);
                free_argv( args );
            }
            else
                result = 1;
        }
        func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time );
        return;
    }

    if( DEBUG_EXECCMD )
    {
        char **argp = argv;

        printf("Executing command");
        while(*argp != 0)
        {
            printf(" [%s]", *argp);
            argp++;
        }
        printf("\n");
    }

    /* the rest is for Windows NT only */
    /* spawn doesn't like quotes around the command name */
    if ( argv[0][0] == '"')
    {
        int l = strlen(argv[0]);

        /* Clobber any closing quote, shortening the string by one
         * element */
        if (argv[0][l-1] == '"')
            argv[0][l-1] = '\0';
        
        /* Move everything *including* the original terminating zero
         * back one place in memory, covering up the opening quote */
        memmove(argv[0],argv[0]+1,l);
    }
    if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
    {
        perror( "spawn" );
        exit( EXITBAD );
    }
    /* Save the operation for execwait() to find. */

    cmdtab[ slot ].pid = pid;
    cmdtab[ slot ].func = func;
    cmdtab[ slot ].closure = closure;

    /* Wait until we're under the limit of concurrent commands. */
    /* Don't trust globs.jobs alone.                            */

    while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
        if( !execwait() )
            break;
    
    if (argv != argv_static)
    {
        free_argv(argv);
    }
}
Example #7
0
void
execcmd(
    char *string,
    void (*func)( void *closure, int status ),
    void *closure,
    LIST *shell )
{
    int pid;
    int slot;
    int raw_cmd = 0 ;
    char *argv_static[ MAXARGC + 1 ];	/* +1 for NULL */
    char **argv = argv_static;
    char *p;

    /* Check to see if we need to hack around the line-length limitation. */
    /* Look for a JAMSHELL setting of "%", indicating that the command
     * should be invoked directly */
    if ( shell && !strcmp(shell->string,"%") && !list_next(shell) )
    {
        raw_cmd = 1;
        shell = 0;
    }

    if ( !is_win95_defined )
        set_is_win95();

    /* Find a slot in the running commands table for this one. */
    if ( is_win95 )
    {
        /* only synchronous spans are supported on Windows 95/98 */
        slot = 0;
    }
    else
    {
        for( slot = 0; slot < MAXJOBS; slot++ )
            if( !cmdtab[ slot ].pid )
                break;
    }
    if( slot == MAXJOBS )
    {
        printf( "no slots for child!\n" );
        exit( EXITBAD );
    }

    if( !cmdtab[ slot ].tempfile )
    {
        const char *tempdir;
        DWORD procID;

        tempdir = getTempDir();

        // SVA - allocate 64 other just to be safe
        cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 );

        procID = GetCurrentProcessId();

        sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat",
                 tempdir, procID, slot );
    }

    /* Trim leading, ending white space */

    while( isspace( *string ) )
        ++string;

    /* If multi line, or too long, or JAMSHELL is set, write to bat file. */
    /* Otherwise, exec directly. */
    /* Frankly, if it is a single long line I don't think the */
    /* command interpreter will do any better -- it will fail. */

    if( shell || !raw_cmd // && use_bat_file( string )
      )
    {
        FILE *f;

        /* Write command to bat file. */

        f = fopen( cmdtab[ slot ].tempfile, "w" );
        fputs( string, f );
        fclose( f );

        string = cmdtab[ slot ].tempfile;

        if( DEBUG_EXECCMD )
        {
            if (shell)
                printf("using user-specified shell: %s", shell->string);
            else
                printf("Executing through .bat file\n");
        }
    }
    else if( DEBUG_EXECCMD )
    {
        printf("Executing raw command directly\n");
    }

    /* Forumulate argv */
    /* If shell was defined, be prepared for % and ! subs. */
    /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */

    if( shell )
    {
        int i;
        char jobno[4];
        int gotpercent = 0;

        sprintf( jobno, "%d", slot + 1 );

        for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
        {
            switch( shell->string[0] )
            {
            case '%':
                argv[i] = string;
                gotpercent++;
                break;
            case '!':
                argv[i] = jobno;
                break;
            default:
                argv[i] = shell->string;
            }
            if( DEBUG_EXECCMD )
                printf( "argv[%d] = '%s'\n", i, argv[i] );
        }

        if( !gotpercent )
            argv[i++] = string;

        argv[i] = 0;
    }
    else if (raw_cmd)
    {
        int ignored;
        argv = string_to_args(string, &ignored);
    }
    else
    {
        /* don't worry, this is ignored on Win95/98, see later.. */
        argv[0] = "cmd.exe";
        argv[1] = "/Q/C";		/* anything more is non-portable */
        argv[2] = string;
        argv[3] = 0;
    }

    /* Catch interrupts whenever commands are running. */

    if( !cmdsrunning++ )
        istat = signal( SIGINT, onintr );

    /* Start the command */

    /* on Win95, we only do a synchronous call */
    if ( is_win95 )
    {
        static const char* hard_coded[] =
        {
            "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir",
            "ren", "rename", "move", 0
        };

        const char**  keyword;
        int           len, spawn = 1;
        int           result;

        for ( keyword = hard_coded; keyword[0]; keyword++ )
        {
            len = strlen( keyword[0] );
            if ( strnicmp( string, keyword[0], len ) == 0 &&
                    !isalnum(string[len]) )
            {
                /* this is one of the hard coded symbols, use 'system' to run */
                /* them.. except for "del"/"erase"                            */
                if ( keyword - hard_coded < 2 )
                    result = process_del( string );
                else
                    result = system( string );

                spawn  = 0;
                break;
            }
        }

        if (spawn)
        {
            char**  args;
            int     num_args;

            /* convert the string into an array of arguments */
            /* we need to take care of double quotes !!      */
            args = string_to_args( string, &num_args );
            if ( args )
            {
#if 0
                char** arg;
                fprintf( stderr, "%s: ", args[0] );
                arg = args+1;
                while ( arg[0] )
                {
                    fprintf( stderr, " {%s}", arg[0] );
                    arg++;
                }
                fprintf( stderr, "\n" );
#endif
                result = spawnvp( P_WAIT, args[0], args );
                free_args( args );
            }
            else
                result = 1;
        }
        func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK );
        return;
    }

    if( DEBUG_EXECCMD )
    {
        char **argp = argv;

        printf("Executing command");
        while(*argp != 0)
        {
            printf(" [%s]", *argp);
            argp++;
        }
        printf("\n");
    }

    /* the rest is for Windows NT only */
    /* spawn doesn't like quotes aroudn the command name */
    if ( argv[0][0] == '"')
    {
        int l = strlen(argv[0]);
        if (argv[0][l-1] == '"') argv[0][l-1] = '\0';
        strcpy(argv[0],argv[0]+1);
    }
    if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
    {
        perror( "spawn" );
        exit( EXITBAD );
    }
    /* Save the operation for execwait() to find. */

    cmdtab[ slot ].pid = pid;
    cmdtab[ slot ].func = func;
    cmdtab[ slot ].closure = closure;

    /* Wait until we're under the limit of concurrent commands. */
    /* Don't trust globs.jobs alone.                            */

    while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
        if( !execwait() )
            break;

    if (argv != argv_static)
    {
        free_args(argv);
    }
}