2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-14 00:32:11 +00:00

Boost Jam code cleanup - added a new exec_check() exec*.c module platform specific functions and moved all command validation into them. MAXLINE macro is no longer needed globally for all platforms, and those platforms that do not have a fixed constant for their maximum command line length (e.g. Windows) may now hold that knowledge inside their exec*.c modules and not export it in any way. Windows execnt.c implementation still does some extra command-trimming that needs to be cleaned up.

[SVN r79078]
This commit is contained in:
Jurko Gospodnetić
2012-06-25 13:42:00 +00:00
parent 16b3c77d72
commit d23202eeff
6 changed files with 182 additions and 70 deletions

View File

@@ -70,6 +70,29 @@ void argv_from_shell( char const * * argv, LIST * shell, char const * command,
}
/* Returns whether the given command string contains lines longer than the given
* maximum.
*/
int check_cmd_for_too_long_lines( char const * command, int const max,
int * const error_length, int * const error_max_length )
{
while ( *command )
{
size_t const l = strcspn( command, "\n" );
if ( l > max )
{
*error_length = l;
*error_max_length = max;
return EXEC_CHECK_LINE_TOO_LONG;
}
command += l;
if ( *command )
++command;
}
return EXEC_CHECK_OK;
}
/* Checks whether the given shell list is actually a request to execute raw
* commands without an external shell.
*/

View File

@@ -42,6 +42,19 @@ typedef void (* ExecCmdCallback)
#define EXEC_CMD_FAIL 1
#define EXEC_CMD_INTR 2
int exec_check
(
string * command,
LIST * * pShell,
int * error_length,
int * error_max_length
);
/* exec_check() return codes. */
#define EXEC_CHECK_OK 101
#define EXEC_CHECK_LINE_TOO_LONG 102
#define EXEC_CHECK_TOO_LONG 103
void exec_cmd
(
string const * command,
@@ -80,5 +93,10 @@ int interrupted( void );
*/
int is_raw_command_request( LIST * shell );
/* Utility worker for exec_check() checking whether all the given command lines
* are under the specified length limit.
*/
int check_cmd_for_too_long_lines( char const * command, int const max,
int * const error_length, int * const error_max_length );
#endif

View File

@@ -48,13 +48,13 @@
* Do not just set JAMSHELL to cmd.exe - it will not work!
*
* External routines:
* exec_check() - preprocess and validate the command.
* exec_cmd() - launch an async command execution.
* exec_wait() - wait for any of the async command processes to terminate.
*/
/* get the maximum shell command line length according to the OS */
int maxline();
static int maxline();
/* trim leading and trailing whitespace */
void string_new_trimmed( string * pResult, string const * source );
/* is the command suitable for direct execution via CreateProcessA() */
@@ -187,8 +187,76 @@ void execnt_unit_test()
}
/*
* exec_check() - preprocess and validate the command.
*/
int exec_check
(
string * command,
LIST * * pShell,
int * error_length,
int * error_max_length
)
{
/* Trim all leading and trailing leading whitespace. */
string cmd_local[ 1 ];
string_new_trimmed( cmd_local, command );
/* Check prerequisites for executing raw commands.
*
* JAMSHELL setting of "%", indicates that the command should be invoked
* directly if it satisfies all the spawnability criteria or using a batch
* file and the default shell if not.
*/
if ( is_raw_command_request( *pShell ) )
{
int const raw_cmd_length = can_spawn( cmd_local->value );
if ( raw_cmd_length < maxline() )
{
/* Fallback to default shell. */
list_free( *pShell );
*pShell = L0;
}
else if ( raw_cmd_length > MAX_RAW_COMMAND_LENGTH )
{
*error_length = raw_cmd_length;
*error_max_length = MAX_RAW_COMMAND_LENGTH;
string_free( cmd_local );
return EXEC_CHECK_TOO_LONG;
}
else
{
string_free( cmd_local );
return EXEC_CHECK_OK;
}
}
/* Now we know we are using an external shell. Note that there is no need to
* check for too long command strings when using an external shell since we
* use a command file and assume no one is going to set up a JAMSHELL format
* string longer than a few hundred bytes at most which should be well under
* the total command string limit. Should someone actually construct such a
* JAMSHELL value it will get reported as an 'invalid parameter'
* CreateProcessA() Windows API failure which seems like a good enough
* result for such intentional mischief.
*/
/* Check for too long command lines. */
{
int const result = check_cmd_for_too_long_lines( cmd_local->value,
maxline(), error_length, error_max_length );
string_free( cmd_local );
return result;
}
}
/*
* exec_cmd() - launch an async command execution.
*
* We assume exec_check() already verified that the given command can have its
* command string constructed as requested.
*/
void exec_cmd
@@ -202,7 +270,7 @@ void exec_cmd
)
{
int const slot = get_free_cmdtab_slot();
int is_raw_cmd = is_raw_command_request( shell );
int const is_raw_cmd = is_raw_command_request( shell );
string cmd_local[ 1 ];
/* Initialize default shell - anything more than /Q/C is non-portable. */
@@ -213,21 +281,6 @@ void exec_cmd
/* Trim all leading and trailing leading whitespace. */
string_new_trimmed( cmd_local, cmd_orig );
/* 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 ( is_raw_cmd )
{
/* Check to see if we need to hack around the line-length limitation.
* JAMSHELL setting of "%", indicates that the command should be invoked
* directly if it satisfies all the spawnability criteria or using a
* batch file and the default shell if not.
*/
is_raw_cmd = can_spawn( cmd_local->value ) >= MAXLINE;
shell = L0;
}
/* Specifying no shell means requesting the default shell. */
if ( list_empty( shell ) )
shell = default_shell;
@@ -295,14 +348,6 @@ void exec_cmd
string_copy( cmdtab[ slot ].command, cmd_orig->value );
}
if ( cmd_local->size > MAX_RAW_COMMAND_LENGTH )
{
printf( "Command line too long (%d characters). Maximum executable "
"command-line length is %d.", cmd_local->size,
MAX_RAW_COMMAND_LENGTH );
exit( EXITBAD );
}
/* Invoke the actual external process using the constructed command line. */
invoke_cmd( cmd_local->value, slot );
@@ -507,7 +552,7 @@ static int raw_maxline()
return 996; /* NT 3.5.1 */
}
int maxline()
static int maxline()
{
static result;
if ( !result ) result = raw_maxline();

View File

@@ -53,6 +53,7 @@
* Do not just set JAMSHELL to /bin/sh - it will not work!
*
* External routines:
* exec_check() - preprocess and validate the command.
* exec_cmd() - launch an async command execution.
* exec_wait() - wait for any of the async command processes to terminate.
*/
@@ -97,6 +98,25 @@ static struct
} cmdtab[ MAXJOBS ] = { { 0 } };
/*
* exec_check() - preprocess and validate the command.
*/
int exec_check
(
string * command,
LIST * * pShell,
int * error_length,
int * error_max_length
)
{
return is_raw_command_request( *pShell )
? EXEC_CHECK_OK
: check_cmd_for_too_long_lines( command->value, MAXLINE, error_length,
error_max_length );
}
/*
* exec_cmd() - launch an async command execution.
*/

View File

@@ -46,10 +46,7 @@
#define OSMINOR "OS=NT"
#define OS_NT
#define SPLITPATH ';'
/* Windows NT 3.51 only allows 996 chars per line, but we deal with the problem
* in "execnt.c".
*/
#define MAXLINE (maxline()) /* longest 'together' actions */
#define MAXLINE (undefined__see_execnt_c) /* max chars per command line */
#define USE_EXECNT
#define USE_PATHUNIX
#define PATH_DELIM '\\'
@@ -93,7 +90,7 @@
#define OSMINOR "OS=MINGW"
#define OS_NT
#define SPLITPATH ';'
#define MAXLINE 996 /* longest 'together' actions */
#define MAXLINE 996 /* max chars per command line */
#define USE_EXECUNIX
#define USE_PATHUNIX
#define PATH_DELIM '\\'
@@ -115,7 +112,7 @@
#ifdef _AIX
#define unix
#define MAXLINE 23552 /* 24k - 1k, longest 'together' actions */
#define MAXLINE 23552 /* 24k - 1k, max chars per command line */
#define OSMINOR "OS=AIX"
#define OS_AIX
#define NO_VFORK
@@ -219,7 +216,7 @@
#define OSMINOR "OS=QNX"
#define OS_QNX
#define NO_VFORK
#define MAXLINE 996
#define MAXLINE 996 /* max chars per command line */
#endif
#endif
#ifdef NeXT
@@ -397,7 +394,7 @@
*/
#ifndef MAXLINE
#define MAXLINE 102400 /* longest 'together' actions' */
#define MAXLINE 102400 /* max chars per command line */
#endif
#ifndef EXITOK

View File

@@ -1045,7 +1045,11 @@ static CMD * make1cmds( TARGET * t )
do
{
CMD * cmd;
int line_too_long = 0;
int cmd_check_result;
int cmd_error_length;
int cmd_error_max_length;
int retry = 0;
int accept_command = 0;
/* Build cmd: cmd_new() takes ownership of its lists. */
if ( list_empty( cmd_targets ) ) cmd_targets = list_copy( nt );
@@ -1053,31 +1057,49 @@ static CMD * make1cmds( TARGET * t )
cmd = cmd_new( rule, cmd_targets, list_sublist( ns, start,
chunk ), cmd_shell );
/* Check for too long command string lines. */
if ( !is_raw_command_request( cmd->shell ) )
cmd_check_result = exec_check( cmd->buf, &cmd->shell,
&cmd_error_length, &cmd_error_max_length );
if ( cmd_check_result == EXEC_CHECK_OK )
{
char * s = cmd->buf->value;
while ( *s )
{
size_t const l = strcspn( s, "\n" );
if ( l > MAXLINE )
{
line_too_long = 1;
break;
}
s += l;
if ( *s )
++s;
}
accept_command = 1;
}
else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) )
{
/* Too long but splittable. Reduce chunk size slowly and
* retry.
*/
assert( cmd_check_result == EXEC_CHECK_TOO_LONG ||
cmd_check_result == EXEC_CHECK_LINE_TOO_LONG );
chunk = chunk * 9 / 10;
retry = 1;
}
else
{
/* Too long and not splittable. */
char const * const error_message = cmd_check_result ==
EXEC_CHECK_TOO_LONG
? "is too long"
: "contains a line that is too long";
assert( cmd_check_result == EXEC_CHECK_TOO_LONG ||
cmd_check_result == EXEC_CHECK_LINE_TOO_LONG );
printf( "%s action %s (%d, max %d):\n", object_str(
rule->name ), error_message, cmd_error_length,
cmd_error_max_length );
/* Tell the user what did not fit. */
fputs( cmd->buf->value, stdout );
exit( EXITBAD );
}
if ( !line_too_long )
assert( !retry || !accept_command );
if ( accept_command )
{
/* Chain it up. */
if ( !cmds ) cmds = cmd;
else cmds->tail->next = cmd;
cmds->tail = cmd;
start += chunk;
/* Mark lists we need recreated for the next command since
* they got consumed by the cmd object.
@@ -1087,28 +1109,15 @@ static CMD * make1cmds( TARGET * t )
}
else
{
if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) )
{
/* Reduce chunk size slowly. */
chunk = chunk * 9 / 10;
}
else
{
/* Too long and not splittable. */
printf( "%s action is too long (max %d):\n", object_str(
rule->name ), MAXLINE );
/* Tell the user what did not fit. */
fputs( cmd->buf->value, stdout );
exit( EXITBAD );
}
/* We can reuse targets & shell lists for the next command.
/* We can reuse targets & shell lists for the next command
* if we do not let them die with this cmd object.
*/
cmd_release_targets_and_shell( cmd );
cmd_free( cmd );
}
if ( !retry )
start += chunk;
}
while ( start < length );
}