1/* 2 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. 3 * 4 * This file is part of Jam - see jam.c for Copyright information. 5 */ 6 7/* 8 * builtins.c - builtin jam rules 9 * 10 * External routines: 11 * 12 * load_builtin() - define builtin rules 13 * 14 * Internal routines: 15 * 16 * builtin_depends() - DEPENDS/INCLUDES rule 17 * builtin_echo() - ECHO rule 18 * builtin_exit() - EXIT rule 19 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule 20 * builtin_glob() - GLOB rule 21 * builtin_match() - MATCH rule 22 * 23 * 01/10/01 (seiwald) - split from compile.c 24 * 01/08/01 (seiwald) - new 'Glob' (file expansion) builtin 25 * 03/02/02 (seiwald) - new 'Match' (regexp match) builtin 26 * 04/03/02 (seiwald) - Glob matches only filename, not directory 27 * 10/22/02 (seiwald) - list_new() now does its own newstr()/copystr() 28 * 10/22/02 (seiwald) - working return/break/continue statements 29 * 11/04/02 (seiwald) - const-ing for string literals 30 * 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends 31 * 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET 32 */ 33 34# include "jam.h" 35 36# include "lists.h" 37# include "parse.h" 38# include "builtins.h" 39# include "rules.h" 40# include "filesys.h" 41# include "newstr.h" 42# include "regexp.h" 43# include "pathsys.h" 44 45/* 46 * compile_builtin() - define builtin rules 47 */ 48 49# define P0 (PARSE *)0 50# define C0 (char *)0 51 52LIST *builtin_depends( PARSE *parse, LOL *args, int *jmp ); 53LIST *builtin_echo( PARSE *parse, LOL *args, int *jmp ); 54LIST *builtin_exit( PARSE *parse, LOL *args, int *jmp ); 55LIST *builtin_flags( PARSE *parse, LOL *args, int *jmp ); 56LIST *builtin_glob( PARSE *parse, LOL *args, int *jmp ); 57LIST *builtin_match( PARSE *parse, LOL *args, int *jmp ); 58 59int glob( const char *s, const char *c ); 60 61void 62load_builtins() 63{ 64 bindrule( "Always" )->procedure = 65 bindrule( "ALWAYS" )->procedure = 66 parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED ); 67 68 bindrule( "Depends" )->procedure = 69 bindrule( "DEPENDS" )->procedure = 70 parse_make( builtin_depends, P0, P0, P0, C0, C0, 0 ); 71 72 bindrule( "echo" )->procedure = 73 bindrule( "Echo" )->procedure = 74 bindrule( "ECHO" )->procedure = 75 parse_make( builtin_echo, P0, P0, P0, C0, C0, 0 ); 76 77 bindrule( "exit" )->procedure = 78 bindrule( "Exit" )->procedure = 79 bindrule( "EXIT" )->procedure = 80 parse_make( builtin_exit, P0, P0, P0, C0, C0, 0 ); 81 82 bindrule( "Glob" )->procedure = 83 bindrule( "GLOB" )->procedure = 84 parse_make( builtin_glob, P0, P0, P0, C0, C0, 0 ); 85 86 bindrule( "Includes" )->procedure = 87 bindrule( "INCLUDES" )->procedure = 88 parse_make( builtin_depends, P0, P0, P0, C0, C0, 1 ); 89 90 bindrule( "Leaves" )->procedure = 91 bindrule( "LEAVES" )->procedure = 92 parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES ); 93 94 bindrule( "Match" )->procedure = 95 bindrule( "MATCH" )->procedure = 96 parse_make( builtin_match, P0, P0, P0, C0, C0, 0 ); 97 98 bindrule( "NoCare" )->procedure = 99 bindrule( "NOCARE" )->procedure = 100 parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE ); 101 102 bindrule( "NOTIME" )->procedure = 103 bindrule( "NotFile" )->procedure = 104 bindrule( "NOTFILE" )->procedure = 105 parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE ); 106 107 bindrule( "NoUpdate" )->procedure = 108 bindrule( "NOUPDATE" )->procedure = 109 parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE ); 110 111 bindrule( "Temporary" )->procedure = 112 bindrule( "TEMPORARY" )->procedure = 113 parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP ); 114} 115 116/* 117 * builtin_depends() - DEPENDS/INCLUDES rule 118 * 119 * The DEPENDS builtin rule appends each of the listed sources on the 120 * dependency list of each of the listed targets. It binds both the 121 * targets and sources as TARGETs. 122 */ 123 124LIST * 125builtin_depends( 126 PARSE *parse, 127 LOL *args, 128 int *jmp ) 129{ 130 LIST *targets = lol_get( args, 0 ); 131 LIST *sources = lol_get( args, 1 ); 132 LIST *l; 133 134 for( l = targets; l; l = list_next( l ) ) 135 { 136 TARGET *t = bindtarget( l->string ); 137 138 /* If doing INCLUDES, switch to the TARGET's include */ 139 /* TARGET, creating it if needed. The internal include */ 140 /* TARGET shares the name of its parent. */ 141 142 if( parse->num ) 143 { 144 if( !t->includes ) 145 t->includes = copytarget( t ); 146 t = t->includes; 147 } 148 149 t->depends = targetlist( t->depends, sources ); 150 } 151 152 return L0; 153} 154 155/* 156 * builtin_echo() - ECHO rule 157 * 158 * The ECHO builtin rule echoes the targets to the user. No other 159 * actions are taken. 160 */ 161 162LIST * 163builtin_echo( 164 PARSE *parse, 165 LOL *args, 166 int *jmp ) 167{ 168 list_print( lol_get( args, 0 ) ); 169 printf( "\n" ); 170 return L0; 171} 172 173/* 174 * builtin_exit() - EXIT rule 175 * 176 * The EXIT builtin rule echoes the targets to the user and exits 177 * the program with a failure status. 178 */ 179 180LIST * 181builtin_exit( 182 PARSE *parse, 183 LOL *args, 184 int *jmp ) 185{ 186 list_print( lol_get( args, 0 ) ); 187 printf( "\n" ); 188 exit( EXITBAD ); /* yeech */ 189 return L0; 190} 191 192/* 193 * builtin_flags() - NOCARE, NOTFILE, TEMPORARY rule 194 * 195 * Builtin_flags() marks the target with the appropriate flag, for use 196 * by make0(). It binds each target as a TARGET. 197 */ 198 199LIST * 200builtin_flags( 201 PARSE *parse, 202 LOL *args, 203 int *jmp ) 204{ 205 LIST *l = lol_get( args, 0 ); 206 207 for( ; l; l = list_next( l ) ) 208 bindtarget( l->string )->flags |= parse->num; 209 210 return L0; 211} 212 213/* 214 * builtin_globbing() - GLOB rule 215 */ 216 217struct globbing { 218 LIST *patterns; 219 LIST *results; 220} ; 221 222static void 223builtin_glob_back( 224 void *closure, 225 const char *file, 226 int status, 227 time_t time ) 228{ 229 struct globbing *globbing = (struct globbing *)closure; 230 LIST *l; 231 PATHNAME f; 232 char buf[ MAXJPATH ]; 233 234 /* Null out directory for matching. */ 235 /* We wish we had file_dirscan() pass up a PATHNAME. */ 236 237 path_parse( file, &f ); 238 f.f_dir.len = 0; 239 path_build( &f, buf, 0 ); 240 241 for( l = globbing->patterns; l; l = l->next ) 242 if( !glob( l->string, buf ) ) 243 { 244 globbing->results = list_new( globbing->results, file, 0 ); 245 break; 246 } 247} 248 249LIST * 250builtin_glob( 251 PARSE *parse, 252 LOL *args, 253 int *jmp ) 254{ 255 LIST *l = lol_get( args, 0 ); 256 LIST *r = lol_get( args, 1 ); 257 258 struct globbing globbing; 259 260 globbing.results = L0; 261 globbing.patterns = r; 262 263 for( ; l; l = list_next( l ) ) 264 file_dirscan( l->string, builtin_glob_back, &globbing ); 265 266 return globbing.results; 267} 268 269/* 270 * builtin_match() - MATCH rule, regexp matching 271 */ 272 273LIST * 274builtin_match( 275 PARSE *parse, 276 LOL *args, 277 int *jmp ) 278{ 279 LIST *l, *r; 280 LIST *result = 0; 281 282 /* For each pattern */ 283 284 for( l = lol_get( args, 0 ); l; l = l->next ) 285 { 286 regexp *re = regcomp( l->string ); 287 288 /* For each string to match against */ 289 290 for( r = lol_get( args, 1 ); r; r = r->next ) 291 if( regexec( re, r->string ) ) 292 { 293 int i, top; 294 295 /* Find highest parameter */ 296 297 for( top = NSUBEXP; top-- > 1; ) 298 if( re->startp[top] ) 299 break; 300 301 /* And add all parameters up to highest onto list. */ 302 /* Must have parameters to have results! */ 303 304 for( i = 1; i <= top; i++ ) 305 { 306 char buf[ MAXSYM ]; 307 int l = re->endp[i] - re->startp[i]; 308 if (l > MAXSYM) { 309 printf("MAXSYM is too low! NEed at least %d\n", l); 310 exit(-1); 311 } 312 memcpy( buf, re->startp[i], l ); 313 buf[ l ] = 0; 314 result = list_new( result, buf, 0 ); 315 } 316 } 317 318 free( (char *)re ); 319 } 320 321 return result; 322} 323