xref: /netbsd/usr.bin/make/targ.c (revision bf9ec67e)
1 /*	$NetBSD: targ.c,v 1.27 2002/03/20 18:10:31 pk Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1989, 1990, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1989 by Berkeley Softworks
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Adam de Boor.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifdef MAKE_BOOTSTRAP
42 static char rcsid[] = "$NetBSD: targ.c,v 1.27 2002/03/20 18:10:31 pk Exp $";
43 #else
44 #include <sys/cdefs.h>
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)targ.c	8.2 (Berkeley) 3/19/94";
48 #else
49 __RCSID("$NetBSD: targ.c,v 1.27 2002/03/20 18:10:31 pk Exp $");
50 #endif
51 #endif /* not lint */
52 #endif
53 
54 /*-
55  * targ.c --
56  *	Functions for maintaining the Lst allTargets. Target nodes are
57  * kept in two structures: a Lst, maintained by the list library, and a
58  * hash table, maintained by the hash library.
59  *
60  * Interface:
61  *	Targ_Init 	    	Initialization procedure.
62  *
63  *	Targ_End 	    	Cleanup the module
64  *
65  *	Targ_List 	    	Return the list of all targets so far.
66  *
67  *	Targ_NewGN	    	Create a new GNode for the passed target
68  *	    	  	    	(string). The node is *not* placed in the
69  *	    	  	    	hash table, though all its fields are
70  *	    	  	    	initialized.
71  *
72  *	Targ_FindNode	    	Find the node for a given target, creating
73  *	    	  	    	and storing it if it doesn't exist and the
74  *	    	  	    	flags are right (TARG_CREATE)
75  *
76  *	Targ_FindList	    	Given a list of names, find nodes for all
77  *	    	  	    	of them. If a name doesn't exist and the
78  *	    	  	    	TARG_NOCREATE flag was given, an error message
79  *	    	  	    	is printed. Else, if a name doesn't exist,
80  *	    	  	    	its node is created.
81  *
82  *	Targ_Ignore	    	Return TRUE if errors should be ignored when
83  *	    	  	    	creating the given target.
84  *
85  *	Targ_Silent	    	Return TRUE if we should be silent when
86  *	    	  	    	creating the given target.
87  *
88  *	Targ_Precious	    	Return TRUE if the target is precious and
89  *	    	  	    	should not be removed if we are interrupted.
90  *
91  * Debugging:
92  *	Targ_PrintGraph	    	Print out the entire graphm all variables
93  *	    	  	    	and statistics for the directory cache. Should
94  *	    	  	    	print something for suffixes, too, but...
95  */
96 
97 #include	  <stdio.h>
98 #include	  <time.h>
99 #include	  "make.h"
100 #include	  "hash.h"
101 #include	  "dir.h"
102 
103 static Lst        allTargets;	/* the list of all targets found so far */
104 #ifdef CLEANUP
105 static Lst	  allGNs;	/* List of all the GNodes */
106 #endif
107 static Hash_Table targets;	/* a hash table of same */
108 
109 #define HTSIZE	191		/* initial size of hash table */
110 
111 static int TargPrintOnlySrc __P((ClientData, ClientData));
112 static int TargPrintName __P((ClientData, ClientData));
113 static int TargPrintNode __P((ClientData, ClientData));
114 #ifdef CLEANUP
115 static void TargFreeGN __P((ClientData));
116 #endif
117 static int TargPropagateCohort __P((ClientData, ClientData));
118 static int TargPropagateNode __P((ClientData, ClientData));
119 
120 /*-
121  *-----------------------------------------------------------------------
122  * Targ_Init --
123  *	Initialize this module
124  *
125  * Results:
126  *	None
127  *
128  * Side Effects:
129  *	The allTargets list and the targets hash table are initialized
130  *-----------------------------------------------------------------------
131  */
132 void
133 Targ_Init ()
134 {
135     allTargets = Lst_Init (FALSE);
136     Hash_InitTable (&targets, HTSIZE);
137 }
138 
139 /*-
140  *-----------------------------------------------------------------------
141  * Targ_End --
142  *	Finalize this module
143  *
144  * Results:
145  *	None
146  *
147  * Side Effects:
148  *	All lists and gnodes are cleared
149  *-----------------------------------------------------------------------
150  */
151 void
152 Targ_End ()
153 {
154 #ifdef CLEANUP
155     Lst_Destroy(allTargets, NOFREE);
156     if (allGNs)
157 	Lst_Destroy(allGNs, TargFreeGN);
158     Hash_DeleteTable(&targets);
159 #endif
160 }
161 
162 /*-
163  *-----------------------------------------------------------------------
164  * Targ_List --
165  *	Return the list of all targets
166  *
167  * Results:
168  *	The list of all targets.
169  *
170  * Side Effects:
171  *	None
172  *-----------------------------------------------------------------------
173  */
174 Lst
175 Targ_List ()
176 {
177     return allTargets;
178 }
179 
180 /*-
181  *-----------------------------------------------------------------------
182  * Targ_NewGN  --
183  *	Create and initialize a new graph node
184  *
185  * Results:
186  *	An initialized graph node with the name field filled with a copy
187  *	of the passed name
188  *
189  * Side Effects:
190  *	The gnode is added to the list of all gnodes.
191  *-----------------------------------------------------------------------
192  */
193 GNode *
194 Targ_NewGN (name)
195     char           *name;	/* the name to stick in the new node */
196 {
197     register GNode *gn;
198 
199     gn = (GNode *) emalloc (sizeof (GNode));
200     gn->name = estrdup (name);
201     gn->uname = NULL;
202     gn->path = (char *) 0;
203     if (name[0] == '-' && name[1] == 'l') {
204 	gn->type = OP_LIB;
205     } else {
206 	gn->type = 0;
207     }
208     gn->unmade =    	0;
209     gn->unmade_cohorts = 0;
210     gn->centurion =    	NULL;
211     gn->made = 	    	UNMADE;
212     gn->flags = 	0;
213     gn->order =		0;
214     gn->mtime = gn->cmtime = 0;
215     gn->iParents =  	Lst_Init (FALSE);
216     gn->cohorts =   	Lst_Init (FALSE);
217     gn->parents =   	Lst_Init (FALSE);
218     gn->children =  	Lst_Init (FALSE);
219     gn->successors = 	Lst_Init (FALSE);
220     gn->preds =     	Lst_Init (FALSE);
221     Hash_InitTable(&gn->context, 0);
222     gn->commands =  	Lst_Init (FALSE);
223     gn->suffix =	NULL;
224     gn->lineno =	0;
225     gn->fname = 	NULL;
226 
227 #ifdef CLEANUP
228     if (allGNs == NULL)
229 	allGNs = Lst_Init(FALSE);
230     Lst_AtEnd(allGNs, (ClientData) gn);
231 #endif
232 
233     return (gn);
234 }
235 
236 #ifdef CLEANUP
237 /*-
238  *-----------------------------------------------------------------------
239  * TargFreeGN  --
240  *	Destroy a GNode
241  *
242  * Results:
243  *	None.
244  *
245  * Side Effects:
246  *	None.
247  *-----------------------------------------------------------------------
248  */
249 static void
250 TargFreeGN (gnp)
251     ClientData gnp;
252 {
253     GNode *gn = (GNode *) gnp;
254 
255 
256     free(gn->name);
257     if (gn->uname)
258 	free(gn->uname);
259     if (gn->path)
260 	free(gn->path);
261     if (gn->fname)
262 	free(gn->fname);
263 
264     Lst_Destroy(gn->iParents, NOFREE);
265     Lst_Destroy(gn->cohorts, NOFREE);
266     Lst_Destroy(gn->parents, NOFREE);
267     Lst_Destroy(gn->children, NOFREE);
268     Lst_Destroy(gn->successors, NOFREE);
269     Lst_Destroy(gn->preds, NOFREE);
270     Hash_DeleteTable(&gn->context);
271     Lst_Destroy(gn->commands, NOFREE);
272     free((Address)gn);
273 }
274 #endif
275 
276 
277 /*-
278  *-----------------------------------------------------------------------
279  * Targ_FindNode  --
280  *	Find a node in the list using the given name for matching
281  *
282  * Results:
283  *	The node in the list if it was. If it wasn't, return NILGNODE of
284  *	flags was TARG_NOCREATE or the newly created and initialized node
285  *	if it was TARG_CREATE
286  *
287  * Side Effects:
288  *	Sometimes a node is created and added to the list
289  *-----------------------------------------------------------------------
290  */
291 GNode *
292 Targ_FindNode (name, flags)
293     char           *name;	/* the name to find */
294     int             flags;	/* flags governing events when target not
295 				 * found */
296 {
297     GNode         *gn;	      /* node in that element */
298     Hash_Entry	  *he;	      /* New or used hash entry for node */
299     Boolean	  isNew;      /* Set TRUE if Hash_CreateEntry had to create */
300 			      /* an entry for the node */
301 
302 
303     if (flags & TARG_CREATE) {
304 	he = Hash_CreateEntry (&targets, name, &isNew);
305 	if (isNew) {
306 	    gn = Targ_NewGN (name);
307 	    Hash_SetValue (he, gn);
308 	    Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
309 	    (void) Lst_AtEnd (allTargets, (ClientData)gn);
310 	}
311     } else {
312 	he = Hash_FindEntry (&targets, name);
313     }
314 
315     if (he == (Hash_Entry *) NULL) {
316 	return (NILGNODE);
317     } else {
318 	return ((GNode *) Hash_GetValue (he));
319     }
320 }
321 
322 /*-
323  *-----------------------------------------------------------------------
324  * Targ_FindList --
325  *	Make a complete list of GNodes from the given list of names
326  *
327  * Results:
328  *	A complete list of graph nodes corresponding to all instances of all
329  *	the names in names.
330  *
331  * Side Effects:
332  *	If flags is TARG_CREATE, nodes will be created for all names in
333  *	names which do not yet have graph nodes. If flags is TARG_NOCREATE,
334  *	an error message will be printed for each name which can't be found.
335  * -----------------------------------------------------------------------
336  */
337 Lst
338 Targ_FindList (names, flags)
339     Lst        	   names;	/* list of names to find */
340     int            flags;	/* flags used if no node is found for a given
341 				 * name */
342 {
343     Lst            nodes;	/* result list */
344     register LstNode  ln;		/* name list element */
345     register GNode *gn;		/* node in tLn */
346     char    	  *name;
347 
348     nodes = Lst_Init (FALSE);
349 
350     if (Lst_Open (names) == FAILURE) {
351 	return (nodes);
352     }
353     while ((ln = Lst_Next (names)) != NILLNODE) {
354 	name = (char *)Lst_Datum(ln);
355 	gn = Targ_FindNode (name, flags);
356 	if (gn != NILGNODE) {
357 	    /*
358 	     * Note: Lst_AtEnd must come before the Lst_Concat so the nodes
359 	     * are added to the list in the order in which they were
360 	     * encountered in the makefile.
361 	     */
362 	    (void) Lst_AtEnd (nodes, (ClientData)gn);
363 	} else if (flags == TARG_NOCREATE) {
364 	    Error ("\"%s\" -- target unknown.", name);
365 	}
366     }
367     Lst_Close (names);
368     return (nodes);
369 }
370 
371 /*-
372  *-----------------------------------------------------------------------
373  * Targ_Ignore  --
374  *	Return true if should ignore errors when creating gn
375  *
376  * Results:
377  *	TRUE if should ignore errors
378  *
379  * Side Effects:
380  *	None
381  *-----------------------------------------------------------------------
382  */
383 Boolean
384 Targ_Ignore (gn)
385     GNode          *gn;		/* node to check for */
386 {
387     if (ignoreErrors || gn->type & OP_IGNORE) {
388 	return (TRUE);
389     } else {
390 	return (FALSE);
391     }
392 }
393 
394 /*-
395  *-----------------------------------------------------------------------
396  * Targ_Silent  --
397  *	Return true if be silent when creating gn
398  *
399  * Results:
400  *	TRUE if should be silent
401  *
402  * Side Effects:
403  *	None
404  *-----------------------------------------------------------------------
405  */
406 Boolean
407 Targ_Silent (gn)
408     GNode          *gn;		/* node to check for */
409 {
410     if (beSilent || gn->type & OP_SILENT) {
411 	return (TRUE);
412     } else {
413 	return (FALSE);
414     }
415 }
416 
417 /*-
418  *-----------------------------------------------------------------------
419  * Targ_Precious --
420  *	See if the given target is precious
421  *
422  * Results:
423  *	TRUE if it is precious. FALSE otherwise
424  *
425  * Side Effects:
426  *	None
427  *-----------------------------------------------------------------------
428  */
429 Boolean
430 Targ_Precious (gn)
431     GNode          *gn;		/* the node to check */
432 {
433     if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) {
434 	return (TRUE);
435     } else {
436 	return (FALSE);
437     }
438 }
439 
440 /******************* DEBUG INFO PRINTING ****************/
441 
442 static GNode	  *mainTarg;	/* the main target, as set by Targ_SetMain */
443 /*-
444  *-----------------------------------------------------------------------
445  * Targ_SetMain --
446  *	Set our idea of the main target we'll be creating. Used for
447  *	debugging output.
448  *
449  * Results:
450  *	None.
451  *
452  * Side Effects:
453  *	"mainTarg" is set to the main target's node.
454  *-----------------------------------------------------------------------
455  */
456 void
457 Targ_SetMain (gn)
458     GNode   *gn;  	/* The main target we'll create */
459 {
460     mainTarg = gn;
461 }
462 
463 static int
464 TargPrintName (gnp, ppath)
465     ClientData     gnp;
466     ClientData	    ppath;
467 {
468     GNode *gn = (GNode *) gnp;
469     printf ("%s ", gn->name);
470 #ifdef notdef
471     if (ppath) {
472 	if (gn->path) {
473 	    printf ("[%s]  ", gn->path);
474 	}
475 	if (gn == mainTarg) {
476 	    printf ("(MAIN NAME)  ");
477 	}
478     }
479 #endif /* notdef */
480     return (ppath ? 0 : 0);
481 }
482 
483 
484 int
485 Targ_PrintCmd (cmd, dummy)
486     ClientData cmd;
487     ClientData dummy;
488 {
489     printf ("\t%s\n", (char *) cmd);
490     return (dummy ? 0 : 0);
491 }
492 
493 /*-
494  *-----------------------------------------------------------------------
495  * Targ_FmtTime --
496  *	Format a modification time in some reasonable way and return it.
497  *
498  * Results:
499  *	The time reformatted.
500  *
501  * Side Effects:
502  *	The time is placed in a static area, so it is overwritten
503  *	with each call.
504  *
505  *-----------------------------------------------------------------------
506  */
507 char *
508 Targ_FmtTime (tm)
509     time_t    tm;
510 {
511     struct tm	  	*parts;
512     static char	  	buf[128];
513 
514     parts = localtime(&tm);
515     (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
516     return(buf);
517 }
518 
519 /*-
520  *-----------------------------------------------------------------------
521  * Targ_PrintType --
522  *	Print out a type field giving only those attributes the user can
523  *	set.
524  *
525  * Results:
526  *
527  * Side Effects:
528  *
529  *-----------------------------------------------------------------------
530  */
531 void
532 Targ_PrintType (type)
533     register int    type;
534 {
535     register int    tbit;
536 
537 #ifdef __STDC__
538 #define PRINTBIT(attr)	case CONCAT(OP_,attr): printf("." #attr " "); break
539 #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break
540 #else
541 #define PRINTBIT(attr) 	case CONCAT(OP_,attr): printf(".attr "); break
542 #define PRINTDBIT(attr)	case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break
543 #endif /* __STDC__ */
544 
545     type &= ~OP_OPMASK;
546 
547     while (type) {
548 	tbit = 1 << (ffs(type) - 1);
549 	type &= ~tbit;
550 
551 	switch(tbit) {
552 	    PRINTBIT(OPTIONAL);
553 	    PRINTBIT(USE);
554 	    PRINTBIT(EXEC);
555 	    PRINTBIT(IGNORE);
556 	    PRINTBIT(PRECIOUS);
557 	    PRINTBIT(SILENT);
558 	    PRINTBIT(MAKE);
559 	    PRINTBIT(JOIN);
560 	    PRINTBIT(INVISIBLE);
561 	    PRINTBIT(NOTMAIN);
562 	    PRINTDBIT(LIB);
563 	    /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
564 	    case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break;
565 	    PRINTDBIT(ARCHV);
566 	    PRINTDBIT(MADE);
567 	    PRINTDBIT(PHONY);
568 	}
569     }
570 }
571 
572 /*-
573  *-----------------------------------------------------------------------
574  * TargPrintNode --
575  *	print the contents of a node
576  *-----------------------------------------------------------------------
577  */
578 static int
579 TargPrintNode (gnp, passp)
580     ClientData   gnp;
581     ClientData	 passp;
582 {
583     GNode         *gn = (GNode *) gnp;
584     int	    	  pass = *(int *) passp;
585     if (!OP_NOP(gn->type)) {
586 	printf("#\n");
587 	if (gn == mainTarg) {
588 	    printf("# *** MAIN TARGET ***\n");
589 	}
590 	if (pass == 2) {
591 	    if (gn->unmade) {
592 		printf("# %d unmade children\n", gn->unmade);
593 	    } else {
594 		printf("# No unmade children\n");
595 	    }
596 	    if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
597 		if (gn->mtime != 0) {
598 		    printf("# last modified %s: %s\n",
599 			      Targ_FmtTime(gn->mtime),
600 			      (gn->made == UNMADE ? "unmade" :
601 			       (gn->made == MADE ? "made" :
602 				(gn->made == UPTODATE ? "up-to-date" :
603 				 "error when made"))));
604 		} else if (gn->made != UNMADE) {
605 		    printf("# non-existent (maybe): %s\n",
606 			      (gn->made == MADE ? "made" :
607 			       (gn->made == UPTODATE ? "up-to-date" :
608 				(gn->made == ERROR ? "error when made" :
609 				 "aborted"))));
610 		} else {
611 		    printf("# unmade\n");
612 		}
613 	    }
614 	    if (!Lst_IsEmpty (gn->iParents)) {
615 		printf("# implicit parents: ");
616 		Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
617 		fputc ('\n', stdout);
618 	    }
619 	}
620 	if (!Lst_IsEmpty (gn->parents)) {
621 	    printf("# parents: ");
622 	    Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
623 	    fputc ('\n', stdout);
624 	}
625 
626 	printf("%-16s", gn->name);
627 	switch (gn->type & OP_OPMASK) {
628 	    case OP_DEPENDS:
629 		printf(": "); break;
630 	    case OP_FORCE:
631 		printf("! "); break;
632 	    case OP_DOUBLEDEP:
633 		printf(":: "); break;
634 	}
635 	Targ_PrintType (gn->type);
636 	Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
637 	fputc ('\n', stdout);
638 	Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
639 	printf("\n\n");
640 	if (gn->type & OP_DOUBLEDEP) {
641 	    Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)&pass);
642 	}
643     }
644     return (0);
645 }
646 
647 /*-
648  *-----------------------------------------------------------------------
649  * TargPrintOnlySrc --
650  *	Print only those targets that are just a source.
651  *
652  * Results:
653  *	0.
654  *
655  * Side Effects:
656  *	The name of each file is printed preceded by #\t
657  *
658  *-----------------------------------------------------------------------
659  */
660 static int
661 TargPrintOnlySrc(gnp, dummy)
662     ClientData 	  gnp;
663     ClientData 	  dummy;
664 {
665     GNode   	  *gn = (GNode *) gnp;
666     if (OP_NOP(gn->type))
667 	printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name);
668 
669     return (dummy ? 0 : 0);
670 }
671 
672 /*-
673  *-----------------------------------------------------------------------
674  * Targ_PrintGraph --
675  *	print the entire graph. heh heh
676  *
677  * Results:
678  *	none
679  *
680  * Side Effects:
681  *	lots o' output
682  *-----------------------------------------------------------------------
683  */
684 void
685 Targ_PrintGraph (pass)
686     int	    pass; 	/* Which pass this is. 1 => no processing
687 			 * 2 => processing done */
688 {
689     printf("#*** Input graph:\n");
690     Lst_ForEach (allTargets, TargPrintNode, (ClientData)&pass);
691     printf("\n\n");
692     printf("#\n#   Files that are only sources:\n");
693     Lst_ForEach (allTargets, TargPrintOnlySrc, (ClientData) 0);
694     printf("#*** Global Variables:\n");
695     Var_Dump (VAR_GLOBAL);
696     printf("#*** Command-line Variables:\n");
697     Var_Dump (VAR_CMD);
698     printf("\n");
699     Dir_PrintDirectories();
700     printf("\n");
701     Suff_PrintAll();
702 }
703 
704 static int
705 TargPropagateCohort (cgnp, pgnp)
706     ClientData   cgnp;
707     ClientData   pgnp;
708 {
709     GNode	  *cgn = (GNode *) cgnp;
710     GNode	  *pgn = (GNode *) pgnp;
711 
712     cgn->type |= pgn->type & ~OP_OPMASK;
713     return (0);
714 }
715 
716 static int
717 TargPropagateNode (gnp, junk)
718     ClientData   gnp;
719     ClientData   junk;
720 {
721     GNode	  *gn = (GNode *) gnp;
722     if (gn->type & OP_DOUBLEDEP)
723 	Lst_ForEach (gn->cohorts, TargPropagateCohort, gnp);
724     return (0);
725 }
726 
727 void
728 Targ_Propagate ()
729 {
730     Lst_ForEach (allTargets, TargPropagateNode, (ClientData)0);
731 }
732