2005-12-29 19:40:48 +01:00
|
|
|
/*
|
|
|
|
* 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 )
|
|
|
|
{
|
2017-07-16 19:40:50 +02:00
|
|
|
LIST *l, *r;
|
|
|
|
LIST *result = 0;
|
2005-12-29 19:40:48 +01:00
|
|
|
|
2017-07-16 19:40:50 +02:00
|
|
|
/* For each pattern */
|
2005-12-29 19:40:48 +01:00
|
|
|
|
2017-07-16 19:40:50 +02:00
|
|
|
for( l = lol_get( args, 0 ); l; l = l->next )
|
|
|
|
{
|
|
|
|
regexp *re = regcomp( l->string );
|
2005-12-29 19:40:48 +01:00
|
|
|
|
2017-07-16 19:40:50 +02:00
|
|
|
/* For each string to match against */
|
2005-12-29 19:40:48 +01:00
|
|
|
|
2017-07-16 19:40:50 +02:00
|
|
|
for( r = lol_get( args, 1 ); r; r = r->next )
|
|
|
|
if( regexec( re, r->string ) )
|
2005-12-29 19:40:48 +01:00
|
|
|
{
|
|
|
|
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];
|
2017-07-16 19:40:50 +02:00
|
|
|
if (l > MAXSYM) {
|
|
|
|
printf("MAXSYM is too low! NEed at least %d\n", l);
|
|
|
|
exit(-1);
|
|
|
|
}
|
2005-12-29 19:40:48 +01:00
|
|
|
memcpy( buf, re->startp[i], l );
|
|
|
|
buf[ l ] = 0;
|
|
|
|
result = list_new( result, buf, 0 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-16 19:40:50 +02:00
|
|
|
free( (char *)re );
|
|
|
|
}
|
2005-12-29 19:40:48 +01:00
|
|
|
|
2017-07-16 19:40:50 +02:00
|
|
|
return result;
|
2005-12-29 19:40:48 +01:00
|
|
|
}
|