Changeset 78997


Ignore:
Timestamp:
Jun 19, 2012, 12:23:18 AM (6 years ago)
Author:
Jurko Gospodnetic
Message:

Refactored Boost Build's execnt.c module - commands to execute are now trimmed of all their leading and trailing spaces up front instead of 'whenever that becomes needed', removed redundant extra splitting of directly executed command strings into arguments. This fixes a bug caused by this extra splitting removing quotes around the executable (argument 0) - e.g. when executing the program 'C:\Program Files\dummy.exe' this would cause the program 'C:\Program.exe' to be executed instead if it exists.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tools/build/v2/engine/execnt.c

    r78996 r78997  
    6464int maxline();
    6565
    66 /* delete and argv list */
    67 static void free_argv( char const * * );
    68 /* convert a command string into arguments */
    69 static char const * * string_to_args( char const * );
    7066/* bump intr to note command interruption */
    7167static void onintr( int );
     68/* trim leading and trailing whitespace */
     69void string_new_trimmed( string * pResult, char const * command );
    7270/* is the command suitable for direct execution via CreateProcessA() */
    7371static long can_spawn( char const * );
     
    157155    test tests[] = {
    158156        { "x", 0 },
    159         { "x\n ", 0 },
    160157        { "x\ny", 1 },
    161158        { "x\n\n y", 1 },
     
    178175        BJAM_FREE( long_command );
    179176    }
    180 
    181     {
    182         /* Work around vc6 bug; it does not like escaped string literals inside
    183          * assert.
    184          */
    185         char const * * argv = string_to_args(" \"g++\" -c -I\"Foobar\"" );
    186         char const expected[] = "-c -I\"Foobar\"";
    187 
    188         assert( !strcmp( argv[ 0 ], "g++" ) );
    189         assert( !strcmp( argv[ 1 ], expected ) );
    190         free_argv( argv );
    191     }
    192177#endif
    193178}
     
    210195    int slot;
    211196    int raw_cmd = 0 ;
    212     char const * command = command_orig;
    213197    string command_local;
    214198
     
    223207    }
    224208
    225     /* Trim leading, -ending- white space */
    226     while ( *( command + 1 ) && isspace( *command ) )
    227         ++command;
     209    /* Trim all leading and trailing leading whitespace. */
     210    string_new_trimmed( &command_local, command_orig );
    228211
    229212    /* Check to see if we need to hack around the line-length limitation. Look
     
    240223         * batch file and the default shell if not.
    241224         */
    242         raw_cmd = can_spawn( command ) >= MAXLINE;
     225        raw_cmd = can_spawn( command_local.value ) >= MAXLINE;
    243226        shell = L0;
    244227    }
     
    284267            exit( EXITBAD );
    285268        }
    286         fputs( command, f );
     269        fputs( command_local.value, f );
    287270        fclose( f );
    288 
    289         command = cmdtab[ slot ].tempfile_bat;
    290271
    291272        if ( DEBUG_EXECCMD )
     
    298279    }
    299280
    300     /* Formulate argv; If shell was defined, be prepared for % and ! subs.
    301      * Otherwise, use stock cmd.exe.
     281    /* If we are running a command directly, we already have it prepared in
     282     * command_local. Now prepare the final command-string to execute in case we
     283     * are using a shell. If a custom shell was defined, be prepared for % and !
     284     * subs. Otherwise, use stock cmd.exe.
    302285     */
    303     {
    304         char const * argv_static[ MAXARGC + 1 ];  /* +1 for NULL */
    305         char const * * argv = argv_static;
     286    if ( !raw_cmd )
     287    {
     288        char const * command = cmdtab[ slot ].tempfile_bat;
     289        char const * argv[ MAXARGC + 1 ];  /* +1 for NULL */
    306290
    307291        if ( shell )
     
    337321            argv[ i ] = 0;
    338322        }
    339         else if ( raw_cmd )
    340         {
    341             argv = string_to_args( command );
    342         }
    343323        else
    344324        {
     
    359339        {
    360340            char const * * argp = argv;
    361             string_new( &command_local );
     341            string_truncate( &command_local, 0 );
    362342            string_append( &command_local, *(argp++) );
    363343            while ( *argp )
     
    367347            }
    368348        }
    369 
    370         if ( argv != argv_static )
    371             free_argv( argv );
    372349    }
    373350
     
    606583/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
    607584
    608 static void free_argv( char const * * args )
    609 {
    610     BJAM_FREE( (void *)args[ 0 ] );
    611     BJAM_FREE( (void *)args );
    612 }
    613 
    614 
    615585/*
    616586 * For more details on Windows cmd.exe shell command-line length limitations see
     
    639609
    640610/*
    641  * Convert a command string into arguments as used by Unix spawnvp() API. The
    642  * original code, inherited from ftjam, tried to break up every argument on the
    643  * command-line, dealing with quotes, but that is really a waste of time on
    644  * Win32, at least. It turns out that all you need to do is get the raw path to
    645  * the executable in the first argument to spawnvp(), and you can pass all the
    646  * rest of the command-line arguments to spawnvp() in one, un-processed string.
    647  *
    648  * New strategy: break the string in at most one place.
    649  */
    650 
    651 static char const * * string_to_args( char const * string )
    652 {
    653     int src_len;
    654     int in_quote;
    655     char * line;
    656     char const * src;
    657     char * dst;
    658     char const * * argv;
    659 
    660     /* Drop leading and trailing whitespace if any. */
    661     while ( isspace( *string ) )
    662         ++string;
    663 
    664     src_len = strlen( string );
    665     while ( ( src_len > 0 ) && isspace( string[ src_len - 1 ] ) )
    666         --src_len;
    667 
    668     /* Copy the input string into a buffer we can modify. */
    669     line = (char *)BJAM_MALLOC_ATOMIC( src_len + 1 );
    670     if ( !line )
    671         return 0;
    672 
    673     /* Allocate the argv array.
    674      *   element 0: stores the path to the executable
    675      *   element 1: stores the command-line arguments to the executable
    676      *   element 2: NULL terminator
    677      */
    678     argv = (char const * *)BJAM_MALLOC( 3 * sizeof( char const * ) );
    679     if ( !argv )
    680     {
    681         BJAM_FREE( line );
    682         return 0;
    683     }
    684 
    685     /* Strip quotes from the first command-line argument and find where it ends.
    686      * Quotes are illegal in Win32 pathnames, so we do not need to worry about
    687      * preserving escaped quotes here. Spaces can not be escaped in Win32, only
    688      * enclosed in quotes, so removing backslash escapes is also a non-issue.
    689      */
    690     in_quote = 0;
    691     for ( src = string, dst = line ; *src; ++src )
    692     {
    693         if ( *src == '"' )
    694             in_quote = !in_quote;
    695         else if ( !in_quote && isspace( *src ) )
    696             break;
    697         else
    698             *dst++ = *src;
    699     }
    700     *dst++ = 0;
    701     argv[ 0 ] = line;
    702 
    703     /* Skip whitespace in src. */
    704     while ( isspace( *src ) )
    705         ++src;
    706 
    707     argv[ 1 ] = dst;
    708 
    709     /* Copy the rest of the arguments verbatim. */
    710     src_len -= src - string;
    711 
    712     /* Use strncat() because it appends a trailing nul. */
    713     *dst = 0;
    714     strncat( dst, src, src_len );
    715 
    716     argv[ 2 ] = 0;
    717 
    718     return argv;
     611 * Creates and returns a new trimmed copy of the given command string. Returned
     612 * value needs to be released using string_free().
     613 */
     614
     615void string_new_trimmed( string * pResult, char const * command )
     616{
     617    int command_len;
     618    while ( isspace( *command ) )
     619        ++command;
     620    command_len = strlen( command );
     621    while ( ( command_len > 0 ) && isspace( command[ command_len - 1 ] ) )
     622        --command_len;
     623    string_new( pResult );
     624    string_append_range( pResult, command, command + command_len );
    719625}
    720626
     
    730636 * can_spawn() - If the command is suitable for execution via CreateProcessA(),
    731637 * return a number >= the number of characters it would occupy on the
    732  * command-line. Otherwise, return zero.
     638 * command-line. Otherwise, return zero. Expects the command string to have
     639 * already been trimmed of all leading and trailing whitespace.
    733640 */
    734641
    735642static long can_spawn( char const * command )
    736643{
    737     char const * p;
     644    char const * p = command;
    738645    char inquote = 0;
    739646
    740     /* Move to the first non-whitespace. */
    741     while ( isspace( *command ) )
    742         ++command;
    743 
    744     p = command;
     647    assert( !isspace( *command ) );
     648    assert( !command[0] || !isspace( command[ strlen(command) - 1 ] ) );
    745649
    746650    /* Look for newlines and unquoted I/O redirection. */
     
    751655        {
    752656        case '\n':
    753             /* Skip over any following spaces. */
    754             while ( isspace( *p ) )
    755                 ++p;
    756             /* Must use a .bat file if there is anything significant following
    757              * the newline.
     657            /* If our command contains newlines we can not execute it directly.
     658             * Note that there is no need to check for leading or trailing
     659             * newlines since we already assume the command string has been
     660             * trimmed prior to this call.
    758661             */
    759             if ( *p )
    760                 return 0;
    761             break;
     662            return 0;
    762663
    763664        case '"':
    764665        case '\'':
    765             if ( ( p > command ) && ( p[ -1 ] != '\\' ) )
     666            if ( ( p > command ) && ( p[-1] != '\\' ) )
    766667            {
    767668                if ( inquote == *p )
Note: See TracChangeset for help on using the changeset viewer.