1/*
2 * Copyright 1993, 1995 Christopher Seiwald.
3 *
4 * This file is part of Jam - see jam.c for Copyright information.
5 */
6
7/*
8 * rules.c - access to RULEs, TARGETs, and ACTIONs
9 *
10 * External routines:
11 *
12 *    bindrule() - return pointer to RULE, creating it if necessary
13 *    bindtarget() - return pointer to TARGET, creating it if necessary
14 *    copytarget() - make a new target with the old target's name
15 *    touchtarget() - mark a target to simulate being new
16 *    targetlist() - turn list of target names into a TARGET chain
17 *    targetentry() - add a TARGET to a chain of TARGETS
18 *    targetchain() - append two TARGET chains
19 *    actionlist() - append to an ACTION chain
20 *    addsettings() - add a deferred "set" command to a target
21 *    copysettings() - copy a settings list for temp use
22 *    pushsettings() - set all target specific variables
23 *    popsettings() - reset target specific variables to their pre-push values
24 *    freesettings() - delete a settings list
25 *    donerules() - free RULE and TARGET tables
26 *
27 * 04/12/94 (seiwald) - actionlist() now just appends a single action.
28 * 08/23/94 (seiwald) - Support for '+=' (append to variable)
29 * 06/21/02 (seiwald) - support for named parameters
30 * 11/04/02 (seiwald) - const-ing for string literals
31 * 12/03/02 (seiwald) - fix odd includes support by grafting them onto depends
32 * 12/17/02 (seiwald) - new copysettings() to protect target-specific vars
33 * 01/14/03 (seiwald) - fix includes fix with new internal includes TARGET
34 */
35
36# include "jam.h"
37# include "lists.h"
38# include "parse.h"
39# include "variable.h"
40# include "rules.h"
41# include "newstr.h"
42# include "hash.h"
43
44static struct hash *rulehash = 0;
45static struct hash *targethash = 0;
46
47# ifdef OPT_RULE_PROFILING_EXT
48static RULE *profiling_rule_list = 0;
49# endif
50
51
52/*
53 * bindrule() - return pointer to RULE, creating it if necessary
54 */
55
56RULE *
57bindrule( const char *rulename )
58{
59	RULE rule, *r = &rule;
60
61	if( !rulehash )
62	    rulehash = hashinit( sizeof( RULE ), "rules" );
63
64	r->name = rulename;
65
66	if( hashenter( rulehash, (HASHDATA **)&r ) )
67	{
68	    r->name = newstr( rulename );	/* never freed */
69	    r->procedure = (PARSE *)0;
70	    r->actions = (char *)0;
71	    r->bindlist = L0;
72	    r->params = L0;
73	    r->flags = 0;
74
75# ifdef OPT_RULE_PROFILING_EXT
76		if ( DEBUG_PROFILE_RULES )
77		{
78			r->invocations = 0;
79			r->invocation_time = 0;
80			r->next_profiling_rule = profiling_rule_list;
81			profiling_rule_list = r;
82		}
83# endif
84	}
85
86	return r;
87}
88
89/*
90 * bindtarget() - return pointer to TARGET, creating it if necessary
91 */
92
93TARGET *
94bindtarget( const char *targetname )
95{
96	TARGET target, *t = ⌖
97
98	if( !targethash )
99	    targethash = hashinit( sizeof( TARGET ), "targets" );
100
101	t->name = targetname;
102
103	if( hashenter( targethash, (HASHDATA **)&t ) )
104	{
105	    memset( (char *)t, '\0', sizeof( *t ) );
106	    t->name = newstr( targetname );	/* never freed */
107	    t->boundname = t->name;		/* default for T_FLAG_NOTFILE */
108	}
109
110	return t;
111}
112
113/*
114 * copytarget() - make a new target with the old target's name
115 *
116 * Not entered into hash table -- for internal nodes.
117 */
118
119TARGET *
120copytarget( const TARGET *ot )
121{
122	TARGET *t;
123
124	t = (TARGET *)malloc( sizeof( *t ) );
125	memset( (char *)t, '\0', sizeof( *t ) );
126	t->name = copystr( ot->name );
127	t->boundname = t->name;
128
129	t->flags |= T_FLAG_NOTFILE | T_FLAG_INTERNAL;
130
131	return t;
132}
133
134/*
135 * touchtarget() - mark a target to simulate being new
136 */
137
138void
139touchtarget( const char *t )
140{
141	bindtarget( t )->flags |= T_FLAG_TOUCHED;
142}
143
144/*
145 * targetlist() - turn list of target names into a TARGET chain
146 *
147 * Inputs:
148 *	chain	existing TARGETS to append to
149 *	targets	list of target names
150 */
151
152TARGETS *
153targetlist(
154	TARGETS	*chain,
155	LIST 	*targets )
156{
157	for( ; targets; targets = list_next( targets ) )
158	    chain = targetentry( chain, bindtarget( targets->string ) );
159
160	return chain;
161}
162
163/*
164 * targetentry() - add a TARGET to a chain of TARGETS
165 *
166 * Inputs:
167 *	chain	exisitng TARGETS to append to
168 *	target	new target to append
169 */
170
171TARGETS *
172targetentry(
173	TARGETS	*chain,
174	TARGET	*target )
175{
176	TARGETS *c;
177
178	c = (TARGETS *)malloc( sizeof( TARGETS ) );
179	c->target = target;
180
181	if( !chain ) chain = c;
182	else chain->tail->next = c;
183	chain->tail = c;
184	c->next = 0;
185
186	return chain;
187}
188
189/*
190 * targetchain() - append two TARGET chains
191 *
192 * Inputs:
193 *	chain	exisitng TARGETS to append to
194 *	target	new target to append
195 */
196
197TARGETS *
198targetchain(
199	TARGETS	*chain,
200	TARGETS	*targets )
201{
202	TARGETS *c;
203
204	if( !targets )
205	    return chain;
206	else if( !chain )
207	    return targets;
208
209	chain->tail->next = targets;
210	chain->tail = targets->tail;
211
212	return chain;
213}
214
215/*
216 * actionlist() - append to an ACTION chain
217 */
218
219ACTIONS *
220actionlist(
221	ACTIONS	*chain,
222	ACTION	*action )
223{
224	ACTIONS *actions = (ACTIONS *)malloc( sizeof( ACTIONS ) );
225
226	actions->action = action;
227
228	if( !chain ) chain = actions;
229	else chain->tail->next = actions;
230	chain->tail = actions;
231	actions->next = 0;
232
233	return chain;
234}
235
236/*
237 * addsettings() - add a deferred "set" command to a target
238 *
239 * Adds a variable setting (varname=list) onto a chain of settings
240 * for a particular target.  Replaces the previous previous value,
241 * if any, unless 'append' says to append the new list onto the old.
242 * Returns the head of the chain of settings.
243 */
244
245SETTINGS *
246addsettings(
247	SETTINGS *head,
248	int	setflag,
249	const char *symbol,
250	LIST	*value )
251{
252	SETTINGS *v;
253
254	/* Look for previous setting */
255
256	for( v = head; v; v = v->next )
257	    if( !strcmp( v->symbol, symbol ) )
258		break;
259
260	/* If not previously set, alloc a new. */
261	/* If appending, do so. */
262	/* Else free old and set new. */
263
264	if( !v )
265	{
266	    v = (SETTINGS *)malloc( sizeof( *v ) );
267	    v->symbol = newstr( symbol );
268	    v->value = value;
269	    v->next = head;
270	    head = v;
271	}
272	else switch( setflag )
273	{
274	case VAR_SET:
275	    /* Toss old, set new */
276	    list_free( v->value );
277	    v->value = value;
278	    break;
279
280	case VAR_APPEND:
281	    /* Append new to old */
282	    v->value = list_append( v->value, value );
283	    break;
284
285	case VAR_DEFAULT:
286	    /* Toss new, old already set */
287	    list_free( value );
288	    break;
289	}
290
291	/* Return (new) head of list. */
292
293	return head;
294}
295
296/*
297 * copysettings() - copy a settings list for temp use
298 *
299 * When target-specific variables are pushed into place with pushsettings(),
300 * any global variables with the same name are swapped onto the target's
301 * SETTINGS chain.  If that chain gets modified (by using the "on target"
302 * syntax), popsettings() would wrongly swap those modified values back
303 * as the new global values.
304 *
305 * copysettings() protects the target's SETTINGS chain by providing a
306 * copy of the chain to pass to pushsettings() and popsettings(), so that
307 * the target's original SETTINGS chain can be modified using the usual
308 * "on target" syntax.
309 */
310
311SETTINGS *
312copysettings( SETTINGS *from )
313{
314	SETTINGS *head = 0, *v;
315
316	for( ; from; from = from->next )
317	{
318	    SETTINGS *v = (SETTINGS *)malloc( sizeof( *v ) );
319	    v->symbol = copystr( from->symbol );
320	    v->value = list_copy( 0, from->value );
321	    v->next = head;
322	    head = v;
323	}
324
325	return head;
326}
327
328/*
329 * pushsettings() - set all target specific variables
330 */
331
332void
333pushsettings( SETTINGS *v )
334{
335	for( ; v; v = v->next )
336	    v->value = var_swap( v->symbol, v->value );
337}
338
339/*
340 * popsettings() - reset target specific variables to their pre-push values
341 */
342
343void
344popsettings( SETTINGS *v )
345{
346	pushsettings( v );	/* just swap again */
347}
348
349/*
350 *    freesettings() - delete a settings list
351 */
352
353void
354freesettings( SETTINGS *v )
355{
356	while( v )
357	{
358	    SETTINGS *n = v->next;
359
360	    freestr( v->symbol );
361	    list_free( v->value );
362	    free( (char *)v );
363
364	    v = n;
365	}
366}
367
368/*
369 * donerules() - free RULE and TARGET tables
370 */
371
372void
373donerules()
374{
375# ifdef OPT_RULE_PROFILING_EXT
376	if ( DEBUG_PROFILE_RULES )
377	{
378		RULE *rule;
379
380		printf("# invoked   total time (us)   rule\n");
381		printf("---------   ---------------   "
382			"------------------------------------\n");
383
384		for (rule = profiling_rule_list;
385			 rule;
386			 rule = rule->next_profiling_rule)
387		{
388			printf("%9d   %15lld   %s\n", rule->invocations,
389				(long long)rule->invocation_time, rule->name);
390		}
391	}
392# endif
393
394	hashdone( rulehash );
395	hashdone( targethash );
396}
397