1 | /* |
---|
2 | * Copyright 1993, 1995 Christopher Seiwald. |
---|
3 | * |
---|
4 | * This file is part of Jam - see jam.c for Copyright information. |
---|
5 | */ |
---|
6 | |
---|
7 | # include "jam.h" |
---|
8 | # include "lists.h" |
---|
9 | # include "parse.h" |
---|
10 | # include "variable.h" |
---|
11 | # include "rules.h" |
---|
12 | # include "newstr.h" |
---|
13 | # include "hash.h" |
---|
14 | # include "modules.h" |
---|
15 | # include "search.h" |
---|
16 | # include "lists.h" |
---|
17 | # include "pathsys.h" |
---|
18 | # include "timestamp.h" |
---|
19 | |
---|
20 | /* This file is ALSO: |
---|
21 | * Copyright 2001-2004 David Abrahams. |
---|
22 | * Distributed under the Boost Software License, Version 1.0. |
---|
23 | * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) |
---|
24 | */ |
---|
25 | |
---|
26 | /* |
---|
27 | * rules.c - access to RULEs, TARGETs, and ACTIONs |
---|
28 | * |
---|
29 | * External routines: |
---|
30 | * |
---|
31 | * bindrule() - return pointer to RULE, creating it if necessary |
---|
32 | * bindtarget() - return pointer to TARGET, creating it if necessary |
---|
33 | * touchtarget() - mark a target to simulate being new |
---|
34 | * targetlist() - turn list of target names into a TARGET chain |
---|
35 | * targetentry() - add a TARGET to a chain of TARGETS |
---|
36 | * actionlist() - append to an ACTION chain |
---|
37 | * addsettings() - add a deferred "set" command to a target |
---|
38 | #ifndef OPT_FIX_TARGET_VARIABLES_EXT |
---|
39 | * usesettings() - set all target specific variables |
---|
40 | #endif |
---|
41 | * pushsettings() - set all target specific variables |
---|
42 | * popsettings() - reset target specific variables to their pre-push values |
---|
43 | * freesettings() - delete a settings list |
---|
44 | * donerules() - free RULE and TARGET tables |
---|
45 | * |
---|
46 | * 04/12/94 (seiwald) - actionlist() now just appends a single action. |
---|
47 | * 08/23/94 (seiwald) - Support for '+=' (append to variable) |
---|
48 | */ |
---|
49 | |
---|
50 | static void set_rule_actions( RULE* rule, rule_actions* actions ); |
---|
51 | static void set_rule_body( RULE* rule, argument_list* args, PARSE* procedure ); |
---|
52 | static struct hash *targethash = 0; |
---|
53 | |
---|
54 | typedef struct _located_target LOCATED_TARGET ; |
---|
55 | |
---|
56 | struct _located_target { |
---|
57 | char* file_name; |
---|
58 | TARGET* target; |
---|
59 | }; |
---|
60 | static struct hash *located_targets = 0; |
---|
61 | |
---|
62 | |
---|
63 | |
---|
64 | |
---|
65 | /* |
---|
66 | * enter_rule() - return pointer to RULE, creating it if necessary in |
---|
67 | * target_module. |
---|
68 | */ |
---|
69 | static RULE * |
---|
70 | enter_rule( char *rulename, module_t *target_module ) |
---|
71 | { |
---|
72 | RULE rule, *r = &rule; |
---|
73 | |
---|
74 | r->name = rulename; |
---|
75 | |
---|
76 | if ( hashenter( demand_rules( target_module ), (HASHDATA **)&r ) ) |
---|
77 | { |
---|
78 | r->name = newstr( rulename ); /* never freed */ |
---|
79 | r->procedure = (PARSE *)0; |
---|
80 | r->module = 0; |
---|
81 | r->actions = 0; |
---|
82 | r->arguments = 0; |
---|
83 | r->exported = 0; |
---|
84 | r->module = target_module; |
---|
85 | #ifdef HAVE_PYTHON |
---|
86 | r->python_function = 0; |
---|
87 | #endif |
---|
88 | } |
---|
89 | return r; |
---|
90 | } |
---|
91 | |
---|
92 | /* |
---|
93 | * define_rule() - return pointer to RULE, creating it if necessary in |
---|
94 | * target_module. Prepare it to accept a body or action originating in |
---|
95 | * src_module. |
---|
96 | */ |
---|
97 | static RULE * |
---|
98 | define_rule( module_t *src_module, char *rulename, module_t *target_module ) |
---|
99 | { |
---|
100 | RULE *r = enter_rule( rulename, target_module ); |
---|
101 | |
---|
102 | if ( r->module != src_module ) /* if the rule was imported from elsewhere, clear it now */ |
---|
103 | { |
---|
104 | set_rule_body( r, 0, 0 ); |
---|
105 | set_rule_actions( r, 0 ); |
---|
106 | r->module = src_module; /* r will be executed in the source module */ |
---|
107 | } |
---|
108 | |
---|
109 | return r; |
---|
110 | } |
---|
111 | |
---|
112 | void |
---|
113 | rule_free( RULE* r ) |
---|
114 | { |
---|
115 | freestr( r->name ); |
---|
116 | r->name = ""; |
---|
117 | parse_free( r->procedure ); |
---|
118 | r->procedure = 0; |
---|
119 | if ( r->arguments ) |
---|
120 | args_free( r->arguments ); |
---|
121 | r->arguments = 0; |
---|
122 | if ( r->actions ) |
---|
123 | actions_free( r->actions ); |
---|
124 | r->actions = 0; |
---|
125 | } |
---|
126 | |
---|
127 | /* |
---|
128 | * bindtarget() - return pointer to TARGET, creating it if necessary |
---|
129 | */ |
---|
130 | |
---|
131 | TARGET * |
---|
132 | bindtarget( const char *targetname ) |
---|
133 | { |
---|
134 | TARGET target, *t = ⌖ |
---|
135 | |
---|
136 | if( !targethash ) |
---|
137 | targethash = hashinit( sizeof( TARGET ), "targets" ); |
---|
138 | |
---|
139 | /* Perforce added const everywhere. No time to merge that change. */ |
---|
140 | t->name = (char*)targetname; |
---|
141 | |
---|
142 | if( hashenter( targethash, (HASHDATA **)&t ) ) |
---|
143 | { |
---|
144 | memset( (char *)t, '\0', sizeof( *t ) ); |
---|
145 | t->name = newstr( (char*)targetname ); /* never freed */ |
---|
146 | t->boundname = t->name; /* default for T_FLAG_NOTFILE */ |
---|
147 | } |
---|
148 | |
---|
149 | return t; |
---|
150 | } |
---|
151 | |
---|
152 | |
---|
153 | static void bind_explicitly_located_target(void* xtarget, void* data) |
---|
154 | { |
---|
155 | TARGET* t = (TARGET*)xtarget; |
---|
156 | if (! (t->flags & T_FLAG_NOTFILE) ) |
---|
157 | { |
---|
158 | /* Check if there's a setting for LOCATE */ |
---|
159 | SETTINGS* s = t->settings; |
---|
160 | for(; s ; s = s->next) |
---|
161 | { |
---|
162 | if (strcmp(s->symbol, "LOCATE") == 0) |
---|
163 | { |
---|
164 | pushsettings(t->settings); |
---|
165 | /* We're binding a target with explicit LOCATE. So |
---|
166 | third argument is of now use: nothing will be returned |
---|
167 | through it. */ |
---|
168 | t->boundname = search( t->name, &t->time, 0 ); |
---|
169 | popsettings(t->settings); |
---|
170 | break; |
---|
171 | } |
---|
172 | } |
---|
173 | } |
---|
174 | } |
---|
175 | |
---|
176 | void bind_explicitly_located_targets() |
---|
177 | { |
---|
178 | if (targethash) |
---|
179 | hashenumerate(targethash, bind_explicitly_located_target, (void*)0); |
---|
180 | } |
---|
181 | |
---|
182 | /* TODO: this is probably not a good idea to use functions in other modules like |
---|
183 | that. */ |
---|
184 | void call_bind_rule(char* target, char* boundname); |
---|
185 | |
---|
186 | TARGET* search_for_target ( char * name, LIST* search_path ) |
---|
187 | { |
---|
188 | PATHNAME f[1]; |
---|
189 | string buf[1]; |
---|
190 | LOCATED_TARGET lt, *lta = < |
---|
191 | time_t time; |
---|
192 | int found = 0; |
---|
193 | TARGET* result; |
---|
194 | |
---|
195 | string_new( buf ); |
---|
196 | |
---|
197 | path_parse( name, f ); |
---|
198 | |
---|
199 | f->f_grist.ptr = 0; |
---|
200 | f->f_grist.len = 0; |
---|
201 | |
---|
202 | while( search_path ) |
---|
203 | { |
---|
204 | f->f_root.ptr = search_path->string; |
---|
205 | f->f_root.len = strlen( search_path->string ); |
---|
206 | |
---|
207 | string_truncate( buf, 0 ); |
---|
208 | path_build( f, buf, 1 ); |
---|
209 | |
---|
210 | lt.file_name = buf->value ; |
---|
211 | |
---|
212 | if (! located_targets ) |
---|
213 | located_targets = hashinit( sizeof(LOCATED_TARGET), |
---|
214 | "located targets" ); |
---|
215 | |
---|
216 | |
---|
217 | if ( hashcheck( located_targets, (HASHDATA **)<a ) ) |
---|
218 | { |
---|
219 | return lta->target; |
---|
220 | } |
---|
221 | |
---|
222 | timestamp( buf->value, &time ); |
---|
223 | if (time) |
---|
224 | { |
---|
225 | found = 1; |
---|
226 | break; |
---|
227 | } |
---|
228 | |
---|
229 | search_path = list_next( search_path ); |
---|
230 | } |
---|
231 | |
---|
232 | if ( ! found ) |
---|
233 | { |
---|
234 | f->f_root.ptr = 0; |
---|
235 | f->f_root.len = 0; |
---|
236 | |
---|
237 | string_truncate( buf, 0 ); |
---|
238 | path_build( f, buf, 1 ); |
---|
239 | |
---|
240 | timestamp( buf->value, &time ); |
---|
241 | } |
---|
242 | |
---|
243 | result = bindtarget( name ); |
---|
244 | result->boundname = newstr( buf->value ); |
---|
245 | result->time = time; |
---|
246 | result->binding = time ? T_BIND_EXISTS : T_BIND_MISSING; |
---|
247 | |
---|
248 | call_bind_rule( result->name, result->boundname ); |
---|
249 | |
---|
250 | string_free( buf ); |
---|
251 | |
---|
252 | return result; |
---|
253 | |
---|
254 | } |
---|
255 | |
---|
256 | /* |
---|
257 | * copytarget() - make a new target with the old target's name |
---|
258 | * |
---|
259 | * Not entered into hash table -- for internal nodes. |
---|
260 | */ |
---|
261 | |
---|
262 | TARGET * |
---|
263 | copytarget( const TARGET *ot ) |
---|
264 | { |
---|
265 | TARGET *t; |
---|
266 | |
---|
267 | t = (TARGET *)malloc( sizeof( *t ) ); |
---|
268 | memset( (char *)t, '\0', sizeof( *t ) ); |
---|
269 | t->name = copystr( ot->name ); |
---|
270 | t->boundname = t->name; |
---|
271 | |
---|
272 | t->flags |= T_FLAG_NOTFILE | T_FLAG_INTERNAL; |
---|
273 | |
---|
274 | return t; |
---|
275 | } |
---|
276 | |
---|
277 | /* |
---|
278 | * touchtarget() - mark a target to simulate being new |
---|
279 | */ |
---|
280 | |
---|
281 | void |
---|
282 | touchtarget( char *t ) |
---|
283 | { |
---|
284 | bindtarget( t )->flags |= T_FLAG_TOUCHED; |
---|
285 | } |
---|
286 | |
---|
287 | /* |
---|
288 | * targetlist() - turn list of target names into a TARGET chain |
---|
289 | * |
---|
290 | * Inputs: |
---|
291 | * chain existing TARGETS to append to |
---|
292 | * targets list of target names |
---|
293 | */ |
---|
294 | |
---|
295 | TARGETS * |
---|
296 | targetlist( |
---|
297 | TARGETS *chain, |
---|
298 | LIST *targets ) |
---|
299 | { |
---|
300 | for( ; targets; targets = list_next( targets ) ) |
---|
301 | chain = targetentry( chain, bindtarget( targets->string ) ); |
---|
302 | |
---|
303 | return chain; |
---|
304 | } |
---|
305 | |
---|
306 | /* |
---|
307 | * targetentry() - add a TARGET to a chain of TARGETS |
---|
308 | * |
---|
309 | * Inputs: |
---|
310 | * chain exisitng TARGETS to append to |
---|
311 | * target new target to append |
---|
312 | */ |
---|
313 | |
---|
314 | TARGETS * |
---|
315 | targetentry( |
---|
316 | TARGETS *chain, |
---|
317 | TARGET *target ) |
---|
318 | { |
---|
319 | TARGETS *c; |
---|
320 | |
---|
321 | c = (TARGETS *)malloc( sizeof( TARGETS ) ); |
---|
322 | c->target = target; |
---|
323 | |
---|
324 | if( !chain ) chain = c; |
---|
325 | else chain->tail->next = c; |
---|
326 | chain->tail = c; |
---|
327 | c->next = 0; |
---|
328 | |
---|
329 | return chain; |
---|
330 | } |
---|
331 | |
---|
332 | /* |
---|
333 | * targetchain() - append two TARGET chains |
---|
334 | * |
---|
335 | * Inputs: |
---|
336 | * chain exisitng TARGETS to append to |
---|
337 | * target new target to append |
---|
338 | */ |
---|
339 | |
---|
340 | TARGETS * |
---|
341 | targetchain( |
---|
342 | TARGETS *chain, |
---|
343 | TARGETS *targets ) |
---|
344 | { |
---|
345 | TARGETS *c; |
---|
346 | |
---|
347 | if( !targets ) |
---|
348 | return chain; |
---|
349 | else if( !chain ) |
---|
350 | return targets; |
---|
351 | |
---|
352 | chain->tail->next = targets; |
---|
353 | chain->tail = targets->tail; |
---|
354 | |
---|
355 | return chain; |
---|
356 | } |
---|
357 | |
---|
358 | /* |
---|
359 | * actionlist() - append to an ACTION chain |
---|
360 | */ |
---|
361 | |
---|
362 | ACTIONS * |
---|
363 | actionlist( |
---|
364 | ACTIONS *chain, |
---|
365 | ACTION *action ) |
---|
366 | { |
---|
367 | ACTIONS *actions = (ACTIONS *)malloc( sizeof( ACTIONS ) ); |
---|
368 | |
---|
369 | actions->action = action; |
---|
370 | |
---|
371 | if( !chain ) chain = actions; |
---|
372 | else chain->tail->next = actions; |
---|
373 | chain->tail = actions; |
---|
374 | actions->next = 0; |
---|
375 | |
---|
376 | return chain; |
---|
377 | } |
---|
378 | |
---|
379 | static SETTINGS* settings_freelist; |
---|
380 | |
---|
381 | /* |
---|
382 | * addsettings() - add a deferred "set" command to a target |
---|
383 | * |
---|
384 | * Adds a variable setting (varname=list) onto a chain of settings |
---|
385 | * for a particular target. Replaces the previous previous value, |
---|
386 | * if any, unless 'append' says to append the new list onto the old. |
---|
387 | * Returns the head of the chain of settings. |
---|
388 | */ |
---|
389 | |
---|
390 | SETTINGS * |
---|
391 | addsettings( |
---|
392 | SETTINGS *head, |
---|
393 | int append, |
---|
394 | char *symbol, |
---|
395 | LIST *value ) |
---|
396 | { |
---|
397 | SETTINGS *v; |
---|
398 | |
---|
399 | /* Look for previous setting */ |
---|
400 | |
---|
401 | for( v = head; v; v = v->next ) |
---|
402 | if( !strcmp( v->symbol, symbol ) ) |
---|
403 | break; |
---|
404 | |
---|
405 | /* If not previously set, alloc a new. */ |
---|
406 | /* If appending, do so. */ |
---|
407 | /* Else free old and set new. */ |
---|
408 | |
---|
409 | if( !v ) |
---|
410 | { |
---|
411 | v = settings_freelist; |
---|
412 | |
---|
413 | if ( v ) |
---|
414 | settings_freelist = v->next; |
---|
415 | else |
---|
416 | v = (SETTINGS *)malloc( sizeof( *v ) ); |
---|
417 | |
---|
418 | v->symbol = newstr( symbol ); |
---|
419 | v->value = value; |
---|
420 | v->next = head; |
---|
421 | head = v; |
---|
422 | } |
---|
423 | else if( append ) |
---|
424 | { |
---|
425 | v->value = list_append( v->value, value ); |
---|
426 | } |
---|
427 | else |
---|
428 | { |
---|
429 | list_free( v->value ); |
---|
430 | v->value = value; |
---|
431 | } |
---|
432 | |
---|
433 | /* Return (new) head of list. */ |
---|
434 | |
---|
435 | return head; |
---|
436 | } |
---|
437 | |
---|
438 | /* |
---|
439 | * pushsettings() - set all target specific variables |
---|
440 | */ |
---|
441 | |
---|
442 | void |
---|
443 | pushsettings( SETTINGS *v ) |
---|
444 | { |
---|
445 | for( ; v; v = v->next ) |
---|
446 | v->value = var_swap( v->symbol, v->value ); |
---|
447 | } |
---|
448 | |
---|
449 | /* |
---|
450 | * popsettings() - reset target specific variables to their pre-push values |
---|
451 | */ |
---|
452 | |
---|
453 | void |
---|
454 | popsettings( SETTINGS *v ) |
---|
455 | { |
---|
456 | pushsettings( v ); /* just swap again */ |
---|
457 | } |
---|
458 | |
---|
459 | /* |
---|
460 | * copysettings() - duplicate a settings list, returning the new copy |
---|
461 | */ |
---|
462 | SETTINGS* |
---|
463 | copysettings( SETTINGS *head ) |
---|
464 | { |
---|
465 | SETTINGS *copy = 0, *v; |
---|
466 | |
---|
467 | for (v = head; v; v = v->next) |
---|
468 | copy = addsettings(copy, 0, v->symbol, list_copy(0, v->value)); |
---|
469 | |
---|
470 | return copy; |
---|
471 | } |
---|
472 | |
---|
473 | /* |
---|
474 | * freetargets() - delete a targets list |
---|
475 | */ |
---|
476 | void freetargets( TARGETS *chain ) |
---|
477 | { |
---|
478 | while( chain ) |
---|
479 | { |
---|
480 | TARGETS* n = chain->next; |
---|
481 | free( chain ); |
---|
482 | chain = n; |
---|
483 | } |
---|
484 | } |
---|
485 | |
---|
486 | /* |
---|
487 | * freeactions() - delete an action list |
---|
488 | */ |
---|
489 | void freeactions( ACTIONS *chain ) |
---|
490 | { |
---|
491 | while( chain ) |
---|
492 | { |
---|
493 | ACTIONS* n = chain->next; |
---|
494 | free( chain ); |
---|
495 | chain = n; |
---|
496 | } |
---|
497 | } |
---|
498 | |
---|
499 | |
---|
500 | /* |
---|
501 | * freesettings() - delete a settings list |
---|
502 | */ |
---|
503 | |
---|
504 | void |
---|
505 | freesettings( SETTINGS *v ) |
---|
506 | { |
---|
507 | while( v ) |
---|
508 | { |
---|
509 | SETTINGS *n = v->next; |
---|
510 | |
---|
511 | freestr( v->symbol ); |
---|
512 | list_free( v->value ); |
---|
513 | v->next = settings_freelist; |
---|
514 | settings_freelist = v; |
---|
515 | |
---|
516 | v = n; |
---|
517 | } |
---|
518 | } |
---|
519 | |
---|
520 | static void freetarget( void *xt, void *data ) |
---|
521 | { |
---|
522 | TARGET* t = (TARGET *)xt; |
---|
523 | if ( t->settings ) |
---|
524 | freesettings( t->settings ); |
---|
525 | if ( t->depends ) |
---|
526 | freetargets( t->depends ); |
---|
527 | if ( t->includes ) |
---|
528 | freetarget( t->includes, (void*)0); |
---|
529 | if ( t->actions ) |
---|
530 | freeactions( t->actions ); |
---|
531 | } |
---|
532 | |
---|
533 | /* |
---|
534 | * donerules() - free TARGET tables |
---|
535 | */ |
---|
536 | |
---|
537 | void |
---|
538 | donerules() |
---|
539 | { |
---|
540 | hashenumerate( targethash, freetarget, 0 ); |
---|
541 | hashdone( targethash ); |
---|
542 | while ( settings_freelist ) |
---|
543 | { |
---|
544 | SETTINGS* n = settings_freelist->next; |
---|
545 | free( settings_freelist ); |
---|
546 | settings_freelist = n; |
---|
547 | } |
---|
548 | } |
---|
549 | |
---|
550 | /* |
---|
551 | * args_new() - make a new reference-counted argument list |
---|
552 | */ |
---|
553 | argument_list* args_new() |
---|
554 | { |
---|
555 | argument_list* r = (argument_list*)malloc( sizeof(argument_list) ); |
---|
556 | r->reference_count = 0; |
---|
557 | lol_init(r->data); |
---|
558 | return r; |
---|
559 | } |
---|
560 | |
---|
561 | /* |
---|
562 | * args_refer() - add a new reference to the given argument list |
---|
563 | */ |
---|
564 | void args_refer( argument_list* a ) |
---|
565 | { |
---|
566 | ++a->reference_count; |
---|
567 | } |
---|
568 | |
---|
569 | /* |
---|
570 | * args_free() - release a reference to the given argument list |
---|
571 | */ |
---|
572 | void args_free( argument_list* a ) |
---|
573 | { |
---|
574 | if (--a->reference_count <= 0) |
---|
575 | { |
---|
576 | lol_free(a->data); |
---|
577 | free(a); |
---|
578 | } |
---|
579 | } |
---|
580 | |
---|
581 | /* |
---|
582 | * actions_refer() - add a new reference to the given actions |
---|
583 | */ |
---|
584 | void actions_refer(rule_actions* a) |
---|
585 | { |
---|
586 | ++a->reference_count; |
---|
587 | } |
---|
588 | |
---|
589 | /* |
---|
590 | * actions_free() - release a reference to the given actions |
---|
591 | */ |
---|
592 | void actions_free(rule_actions* a) |
---|
593 | { |
---|
594 | if (--a->reference_count <= 0) |
---|
595 | { |
---|
596 | freestr(a->command); |
---|
597 | list_free(a->bindlist); |
---|
598 | free(a); |
---|
599 | } |
---|
600 | } |
---|
601 | |
---|
602 | /* |
---|
603 | * set_rule_body() - set the argument list and procedure of the given rule |
---|
604 | */ |
---|
605 | static void set_rule_body( RULE* rule, argument_list* args, PARSE* procedure ) |
---|
606 | { |
---|
607 | if ( args ) |
---|
608 | args_refer( args ); |
---|
609 | if ( rule->arguments ) |
---|
610 | args_free( rule->arguments ); |
---|
611 | rule->arguments = args; |
---|
612 | |
---|
613 | if ( procedure ) |
---|
614 | parse_refer( procedure ); |
---|
615 | if ( rule->procedure ) |
---|
616 | parse_free( rule->procedure ); |
---|
617 | rule->procedure = procedure; |
---|
618 | } |
---|
619 | |
---|
620 | /* |
---|
621 | * global_name() - given a rule, return the name for a corresponding rule in the global module |
---|
622 | */ |
---|
623 | static char* global_rule_name( RULE* r ) |
---|
624 | { |
---|
625 | if ( r->module == root_module() ) |
---|
626 | { |
---|
627 | return r->name; |
---|
628 | } |
---|
629 | else |
---|
630 | { |
---|
631 | char name[4096] = ""; |
---|
632 | strncat(name, r->module->name, sizeof(name) - 1); |
---|
633 | strncat(name, r->name, sizeof(name) - 1 ); |
---|
634 | return newstr(name); |
---|
635 | } |
---|
636 | } |
---|
637 | |
---|
638 | /* |
---|
639 | * global_rule() - given a rule, produce the corresponding entry in the global module |
---|
640 | */ |
---|
641 | static RULE* global_rule( RULE* r ) |
---|
642 | { |
---|
643 | if ( r->module == root_module() ) |
---|
644 | { |
---|
645 | return r; |
---|
646 | } |
---|
647 | else |
---|
648 | { |
---|
649 | char* name = global_rule_name( r ); |
---|
650 | RULE* result = define_rule( r->module, name, root_module() ); |
---|
651 | freestr(name); |
---|
652 | return result; |
---|
653 | } |
---|
654 | } |
---|
655 | |
---|
656 | /* |
---|
657 | * new_rule_body() - make a new rule named rulename in the given |
---|
658 | * module, with the given argument list and procedure. If exported is |
---|
659 | * true, the rule is exported to the global module as |
---|
660 | * modulename.rulename. |
---|
661 | */ |
---|
662 | RULE* new_rule_body( module_t* m, char* rulename, argument_list* args, PARSE* procedure, int exported ) |
---|
663 | { |
---|
664 | RULE* local = define_rule( m, rulename, m ); |
---|
665 | local->exported = exported; |
---|
666 | set_rule_body( local, args, procedure ); |
---|
667 | |
---|
668 | /* Mark the procedure with the global rule name, regardless of |
---|
669 | * whether the rule is exported. That gives us something |
---|
670 | * reasonably identifiable that we can use, e.g. in profiling |
---|
671 | * output. Only do this once, since this could be called multiple |
---|
672 | * times with the same procedure. |
---|
673 | */ |
---|
674 | if ( procedure->rulename == 0 ) |
---|
675 | procedure->rulename = global_rule_name( local ); |
---|
676 | |
---|
677 | return local; |
---|
678 | } |
---|
679 | |
---|
680 | static void set_rule_actions( RULE* rule, rule_actions* actions ) |
---|
681 | { |
---|
682 | if ( actions ) |
---|
683 | actions_refer( actions ); |
---|
684 | if ( rule->actions ) |
---|
685 | actions_free( rule->actions ); |
---|
686 | rule->actions = actions; |
---|
687 | |
---|
688 | } |
---|
689 | |
---|
690 | static rule_actions* actions_new( char* command, LIST* bindlist, int flags ) |
---|
691 | { |
---|
692 | rule_actions* result = (rule_actions*)malloc(sizeof(rule_actions)); |
---|
693 | result->command = copystr( command ); |
---|
694 | result->bindlist = bindlist; |
---|
695 | result->flags = flags; |
---|
696 | result->reference_count = 0; |
---|
697 | return result; |
---|
698 | } |
---|
699 | |
---|
700 | RULE* new_rule_actions( module_t* m, char* rulename, char* command, LIST* bindlist, int flags ) |
---|
701 | { |
---|
702 | RULE* local = define_rule( m, rulename, m ); |
---|
703 | RULE* global = global_rule( local ); |
---|
704 | set_rule_actions( local, actions_new( command, bindlist, flags ) ); |
---|
705 | set_rule_actions( global, local->actions ); |
---|
706 | return local; |
---|
707 | } |
---|
708 | |
---|
709 | /* Looks for a rule in the specified module, and returns it, if found. |
---|
710 | First checks if the rule is present in the module's rule table. |
---|
711 | Second, if name of the rule is in the form name1.name2 and name1 is in |
---|
712 | the list of imported modules, look in module 'name1' for rule 'name2'. |
---|
713 | */ |
---|
714 | RULE *lookup_rule( char *rulename, module_t *m, int local_only ) |
---|
715 | { |
---|
716 | RULE rule, *r = &rule, *result = 0; |
---|
717 | module_t* original_module = m; |
---|
718 | r->name = rulename; |
---|
719 | |
---|
720 | if (m->class_module) |
---|
721 | m = m->class_module; |
---|
722 | |
---|
723 | if (m->rules && hashcheck( m->rules, (HASHDATA **)&r ) ) |
---|
724 | result = r; |
---|
725 | else if (!local_only && m->imported_modules) { |
---|
726 | /* Try splitting the name into module and rule. */ |
---|
727 | char *p = strchr(r->name, '.') ; |
---|
728 | if (p) { |
---|
729 | *p = '\0'; |
---|
730 | /* Now, r->name keeps the module name, and p+1 keeps the rule name. */ |
---|
731 | if (hashcheck( m->imported_modules, (HASHDATA **)&r)) |
---|
732 | { |
---|
733 | result = lookup_rule(p+1, bindmodule(rulename), 1); |
---|
734 | } |
---|
735 | *p = '.'; |
---|
736 | } |
---|
737 | } |
---|
738 | |
---|
739 | if (result) |
---|
740 | { |
---|
741 | if (local_only && !result->exported) |
---|
742 | result = 0; |
---|
743 | else |
---|
744 | { |
---|
745 | /* Lookup started in class module. We've found a rule in class module, |
---|
746 | which is marked for execution in that module, or in some instances. |
---|
747 | Mark it for execution in the instance where we've started lookup. |
---|
748 | */ |
---|
749 | int execute_in_class = (result->module == m); |
---|
750 | int execute_in_some_instance = |
---|
751 | (result->module->class_module && result->module->class_module == m); |
---|
752 | if (original_module != m && (execute_in_class || execute_in_some_instance)) |
---|
753 | result->module = original_module; |
---|
754 | } |
---|
755 | } |
---|
756 | |
---|
757 | return result; |
---|
758 | |
---|
759 | } |
---|
760 | |
---|
761 | |
---|
762 | RULE *bindrule( char *rulename, module_t* m) |
---|
763 | { |
---|
764 | RULE *result; |
---|
765 | |
---|
766 | result = lookup_rule(rulename, m, 0); |
---|
767 | if (!result) |
---|
768 | result = lookup_rule(rulename, root_module(), 0); |
---|
769 | /* We've only one caller, 'evaluate_rule', which will complain about |
---|
770 | calling underfined rule. We could issue the error |
---|
771 | here, but we don't have necessary information, such as frame. |
---|
772 | */ |
---|
773 | if (!result) |
---|
774 | result = enter_rule( rulename, m ); |
---|
775 | |
---|
776 | return result; |
---|
777 | } |
---|
778 | |
---|
779 | RULE* import_rule( RULE* source, module_t* m, char* name ) |
---|
780 | { |
---|
781 | RULE* dest = define_rule( source->module, name, m ); |
---|
782 | set_rule_body( dest, source->arguments, source->procedure ); |
---|
783 | set_rule_actions( dest, source->actions ); |
---|
784 | return dest; |
---|
785 | } |
---|