buildtools/jam/builtins.c
Adrien Destugues 259af3cf06 Increase MAXSYM and add sanity checks.
I had jam crash in strange ways because a stack-allocatted aray was
overflowing. Double the limit, and add sanity checks with exit and clear
error messages in case it happens again.
2017-07-16 19:40:50 +02:00

323 lines
7.1 KiB
C

/*
* Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
/*
* builtins.c - builtin jam rules
*
* External routines:
*
* load_builtin() - define builtin rules
*
* Internal routines:
*
* builtin_depends() - DEPENDS/INCLUDES rule
* builtin_echo() - ECHO rule
* builtin_exit() - EXIT rule
* builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
* builtin_glob() - GLOB rule
* builtin_match() - MATCH rule
*
* 01/10/01 (seiwald) - split from compile.c
* 01/08/01 (seiwald) - new 'Glob' (file expansion) builtin
* 03/02/02 (seiwald) - new 'Match' (regexp match) builtin
* 04/03/02 (seiwald) - Glob matches only filename, not directory
* 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr()
* 10/22/02 (seiwald) - working return/break/continue statements
* 11/04/02 (seiwald) - const-ing for string literals
* 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
* 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
*/
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "builtins.h"
# include "rules.h"
# include "filesys.h"
# include "newstr.h"
# include "regexp.h"
# include "pathsys.h"
/*
* compile_builtin() - define builtin rules
*/
# define P0 (PARSE *)0
# define C0 (char *)0
LIST *builtin_depends( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_echo( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_exit( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_flags( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_glob( PARSE *parse, LOL *args, int *jmp );
LIST *builtin_match( PARSE *parse, LOL *args, int *jmp );
int glob( const char *s, const char *c );
void
load_builtins()
{
bindrule( "Always" )->procedure =
bindrule( "ALWAYS" )->procedure =
parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED );
bindrule( "Depends" )->procedure =
bindrule( "DEPENDS" )->procedure =
parse_make( builtin_depends, P0, P0, P0, C0, C0, 0 );
bindrule( "echo" )->procedure =
bindrule( "Echo" )->procedure =
bindrule( "ECHO" )->procedure =
parse_make( builtin_echo, P0, P0, P0, C0, C0, 0 );
bindrule( "exit" )->procedure =
bindrule( "Exit" )->procedure =
bindrule( "EXIT" )->procedure =
parse_make( builtin_exit, P0, P0, P0, C0, C0, 0 );
bindrule( "Glob" )->procedure =
bindrule( "GLOB" )->procedure =
parse_make( builtin_glob, P0, P0, P0, C0, C0, 0 );
bindrule( "Includes" )->procedure =
bindrule( "INCLUDES" )->procedure =
parse_make( builtin_depends, P0, P0, P0, C0, C0, 1 );
bindrule( "Leaves" )->procedure =
bindrule( "LEAVES" )->procedure =
parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES );
bindrule( "Match" )->procedure =
bindrule( "MATCH" )->procedure =
parse_make( builtin_match, P0, P0, P0, C0, C0, 0 );
bindrule( "NoCare" )->procedure =
bindrule( "NOCARE" )->procedure =
parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE );
bindrule( "NOTIME" )->procedure =
bindrule( "NotFile" )->procedure =
bindrule( "NOTFILE" )->procedure =
parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE );
bindrule( "NoUpdate" )->procedure =
bindrule( "NOUPDATE" )->procedure =
parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE );
bindrule( "Temporary" )->procedure =
bindrule( "TEMPORARY" )->procedure =
parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP );
}
/*
* builtin_depends() - DEPENDS/INCLUDES rule
*
* The DEPENDS builtin rule appends each of the listed sources on the
* dependency list of each of the listed targets. It binds both the
* targets and sources as TARGETs.
*/
LIST *
builtin_depends(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *targets = lol_get( args, 0 );
LIST *sources = lol_get( args, 1 );
LIST *l;
for( l = targets; l; l = list_next( l ) )
{
TARGET *t = bindtarget( l->string );
/* If doing INCLUDES, switch to the TARGET's include */
/* TARGET, creating it if needed. The internal include */
/* TARGET shares the name of its parent. */
if( parse->num )
{
if( !t->includes )
t->includes = copytarget( t );
t = t->includes;
}
t->depends = targetlist( t->depends, sources );
}
return L0;
}
/*
* builtin_echo() - ECHO rule
*
* The ECHO builtin rule echoes the targets to the user. No other
* actions are taken.
*/
LIST *
builtin_echo(
PARSE *parse,
LOL *args,
int *jmp )
{
list_print( lol_get( args, 0 ) );
printf( "\n" );
return L0;
}
/*
* builtin_exit() - EXIT rule
*
* The EXIT builtin rule echoes the targets to the user and exits
* the program with a failure status.
*/
LIST *
builtin_exit(
PARSE *parse,
LOL *args,
int *jmp )
{
list_print( lol_get( args, 0 ) );
printf( "\n" );
exit( EXITBAD ); /* yeech */
return L0;
}
/*
* builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule
*
* Builtin_flags() marks the target with the appropriate flag, for use
* by make0(). It binds each target as a TARGET.
*/
LIST *
builtin_flags(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *l = lol_get( args, 0 );
for( ; l; l = list_next( l ) )
bindtarget( l->string )->flags |= parse->num;
return L0;
}
/*
* builtin_globbing() - GLOB rule
*/
struct globbing {
LIST *patterns;
LIST *results;
} ;
static void
builtin_glob_back(
void *closure,
const char *file,
int status,
time_t time )
{
struct globbing *globbing = (struct globbing *)closure;
LIST *l;
PATHNAME f;
char buf[ MAXJPATH ];
/* Null out directory for matching. */
/* We wish we had file_dirscan() pass up a PATHNAME. */
path_parse( file, &f );
f.f_dir.len = 0;
path_build( &f, buf, 0 );
for( l = globbing->patterns; l; l = l->next )
if( !glob( l->string, buf ) )
{
globbing->results = list_new( globbing->results, file, 0 );
break;
}
}
LIST *
builtin_glob(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *l = lol_get( args, 0 );
LIST *r = lol_get( args, 1 );
struct globbing globbing;
globbing.results = L0;
globbing.patterns = r;
for( ; l; l = list_next( l ) )
file_dirscan( l->string, builtin_glob_back, &globbing );
return globbing.results;
}
/*
* builtin_match() - MATCH rule, regexp matching
*/
LIST *
builtin_match(
PARSE *parse,
LOL *args,
int *jmp )
{
LIST *l, *r;
LIST *result = 0;
/* For each pattern */
for( l = lol_get( args, 0 ); l; l = l->next )
{
regexp *re = regcomp( l->string );
/* For each string to match against */
for( r = lol_get( args, 1 ); r; r = r->next )
if( regexec( re, r->string ) )
{
int i, top;
/* Find highest parameter */
for( top = NSUBEXP; top-- > 1; )
if( re->startp[top] )
break;
/* And add all parameters up to highest onto list. */
/* Must have parameters to have results! */
for( i = 1; i <= top; i++ )
{
char buf[ MAXSYM ];
int l = re->endp[i] - re->startp[i];
if (l > MAXSYM) {
printf("MAXSYM is too low! NEed at least %d\n", l);
exit(-1);
}
memcpy( buf, re->startp[i], l );
buf[ l ] = 0;
result = list_new( result, buf, 0 );
}
}
free( (char *)re );
}
return result;
}