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