xref: /freebsd/contrib/bmake/suff.c (revision 9f45a3c8c82ffead7044ae836d9257113c630d3b)
1*9f45a3c8SSimon J. Gerraty /*	$NetBSD: suff.c,v 1.364 2022/01/07 20:54:45 rillig Exp $	*/
23955d011SMarcel Moolenaar 
33955d011SMarcel Moolenaar /*
43955d011SMarcel Moolenaar  * Copyright (c) 1988, 1989, 1990, 1993
53955d011SMarcel Moolenaar  *	The Regents of the University of California.  All rights reserved.
63955d011SMarcel Moolenaar  *
73955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
83955d011SMarcel Moolenaar  * Adam de Boor.
93955d011SMarcel Moolenaar  *
103955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
113955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
123955d011SMarcel Moolenaar  * are met:
133955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
143955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
153955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
163955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
173955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
183955d011SMarcel Moolenaar  * 3. Neither the name of the University nor the names of its contributors
193955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
203955d011SMarcel Moolenaar  *    without specific prior written permission.
213955d011SMarcel Moolenaar  *
223955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
233955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
243955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
253955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
263955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
273955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
283955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
293955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
303955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
313955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323955d011SMarcel Moolenaar  * SUCH DAMAGE.
333955d011SMarcel Moolenaar  */
343955d011SMarcel Moolenaar 
353955d011SMarcel Moolenaar /*
363955d011SMarcel Moolenaar  * Copyright (c) 1989 by Berkeley Softworks
373955d011SMarcel Moolenaar  * All rights reserved.
383955d011SMarcel Moolenaar  *
393955d011SMarcel Moolenaar  * This code is derived from software contributed to Berkeley by
403955d011SMarcel Moolenaar  * Adam de Boor.
413955d011SMarcel Moolenaar  *
423955d011SMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
433955d011SMarcel Moolenaar  * modification, are permitted provided that the following conditions
443955d011SMarcel Moolenaar  * are met:
453955d011SMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
463955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
473955d011SMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
483955d011SMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
493955d011SMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
503955d011SMarcel Moolenaar  * 3. All advertising materials mentioning features or use of this software
513955d011SMarcel Moolenaar  *    must display the following acknowledgement:
523955d011SMarcel Moolenaar  *	This product includes software developed by the University of
533955d011SMarcel Moolenaar  *	California, Berkeley and its contributors.
543955d011SMarcel Moolenaar  * 4. Neither the name of the University nor the names of its contributors
553955d011SMarcel Moolenaar  *    may be used to endorse or promote products derived from this software
563955d011SMarcel Moolenaar  *    without specific prior written permission.
573955d011SMarcel Moolenaar  *
583955d011SMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
593955d011SMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
603955d011SMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
613955d011SMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
623955d011SMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
633955d011SMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
643955d011SMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
653955d011SMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
663955d011SMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
673955d011SMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
683955d011SMarcel Moolenaar  * SUCH DAMAGE.
693955d011SMarcel Moolenaar  */
703955d011SMarcel Moolenaar 
71e2eeea75SSimon J. Gerraty /*
72e2eeea75SSimon J. Gerraty  * Maintain suffix lists and find implicit dependents using suffix
73e2eeea75SSimon J. Gerraty  * transformation rules such as ".c.o".
743955d011SMarcel Moolenaar  *
753955d011SMarcel Moolenaar  * Interface:
76e2eeea75SSimon J. Gerraty  *	Suff_Init	Initialize the module.
773955d011SMarcel Moolenaar  *
78e2eeea75SSimon J. Gerraty  *	Suff_End	Clean up the module.
793955d011SMarcel Moolenaar  *
80b0c40a00SSimon J. Gerraty  *	Suff_ExtendPaths
81b0c40a00SSimon J. Gerraty  *			Extend the search path of each suffix to include the
82e2eeea75SSimon J. Gerraty  *			default search path.
833955d011SMarcel Moolenaar  *
84956e45f6SSimon J. Gerraty  *	Suff_ClearSuffixes
85e2eeea75SSimon J. Gerraty  *			Clear out all the suffixes and transformations.
863955d011SMarcel Moolenaar  *
87956e45f6SSimon J. Gerraty  *	Suff_IsTransform
88e2eeea75SSimon J. Gerraty  *			See if the passed string is a transformation rule.
893955d011SMarcel Moolenaar  *
903955d011SMarcel Moolenaar  *	Suff_AddSuffix	Add the passed string as another known suffix.
913955d011SMarcel Moolenaar  *
923955d011SMarcel Moolenaar  *	Suff_GetPath	Return the search path for the given suffix.
933955d011SMarcel Moolenaar  *
94956e45f6SSimon J. Gerraty  *	Suff_AddInclude
95956e45f6SSimon J. Gerraty  *			Mark the given suffix as denoting an include file.
963955d011SMarcel Moolenaar  *
973955d011SMarcel Moolenaar  *	Suff_AddLib	Mark the given suffix as denoting a library.
983955d011SMarcel Moolenaar  *
99956e45f6SSimon J. Gerraty  *	Suff_AddTransform
100e2eeea75SSimon J. Gerraty  *			Add another transformation to the suffix graph.
1013955d011SMarcel Moolenaar  *
1023955d011SMarcel Moolenaar  *	Suff_SetNull	Define the suffix to consider the suffix of
1033955d011SMarcel Moolenaar  *			any file that doesn't have a known one.
1043955d011SMarcel Moolenaar  *
1053955d011SMarcel Moolenaar  *	Suff_FindDeps	Find implicit sources for and the location of
1063955d011SMarcel Moolenaar  *			a target based on its suffix. Returns the
1073955d011SMarcel Moolenaar  *			bottom-most node added to the graph or NULL
1083955d011SMarcel Moolenaar  *			if the target had no implicit sources.
1093955d011SMarcel Moolenaar  *
110956e45f6SSimon J. Gerraty  *	Suff_FindPath	Return the appropriate path to search in order to
111956e45f6SSimon J. Gerraty  *			find the node.
1123955d011SMarcel Moolenaar  */
1133955d011SMarcel Moolenaar 
1143955d011SMarcel Moolenaar #include "make.h"
1153955d011SMarcel Moolenaar #include "dir.h"
1163955d011SMarcel Moolenaar 
117956e45f6SSimon J. Gerraty /*	"@(#)suff.c	8.4 (Berkeley) 3/21/94"	*/
118*9f45a3c8SSimon J. Gerraty MAKE_RCSID("$NetBSD: suff.c,v 1.364 2022/01/07 20:54:45 rillig Exp $");
1192c3632d1SSimon J. Gerraty 
12006b9b3e0SSimon J. Gerraty typedef List SuffixList;
12106b9b3e0SSimon J. Gerraty typedef ListNode SuffixListNode;
1222c3632d1SSimon J. Gerraty 
12306b9b3e0SSimon J. Gerraty typedef List CandidateList;
12406b9b3e0SSimon J. Gerraty typedef ListNode CandidateListNode;
1252c3632d1SSimon J. Gerraty 
12606b9b3e0SSimon J. Gerraty /* The defined suffixes, such as '.c', '.o', '.l'. */
12706b9b3e0SSimon J. Gerraty static SuffixList sufflist = LST_INIT;
1283955d011SMarcel Moolenaar #ifdef CLEANUP
12906b9b3e0SSimon J. Gerraty /* The suffixes to be cleaned up at the end. */
13006b9b3e0SSimon J. Gerraty static SuffixList suffClean = LST_INIT;
1313955d011SMarcel Moolenaar #endif
132e2eeea75SSimon J. Gerraty 
13306b9b3e0SSimon J. Gerraty /*
13406b9b3e0SSimon J. Gerraty  * The transformation rules, such as '.c.o' to transform '.c' into '.o',
13506b9b3e0SSimon J. Gerraty  * or simply '.c' to transform 'file.c' into 'file'.
13606b9b3e0SSimon J. Gerraty  */
13706b9b3e0SSimon J. Gerraty static GNodeList transforms = LST_INIT;
1383955d011SMarcel Moolenaar 
13906b9b3e0SSimon J. Gerraty /*
14006b9b3e0SSimon J. Gerraty  * Counter for assigning suffix numbers.
14106b9b3e0SSimon J. Gerraty  * TODO: What are these suffix numbers used for?
14206b9b3e0SSimon J. Gerraty  */
14306b9b3e0SSimon J. Gerraty static int sNum = 0;
1443955d011SMarcel Moolenaar 
14506b9b3e0SSimon J. Gerraty typedef List SuffixListList;
146956e45f6SSimon J. Gerraty 
14706b9b3e0SSimon J. Gerraty /*
14806b9b3e0SSimon J. Gerraty  * A suffix such as ".c" or ".o" that is used in suffix transformation rules
14906b9b3e0SSimon J. Gerraty  * such as ".c.o:".
15006b9b3e0SSimon J. Gerraty  */
15106b9b3e0SSimon J. Gerraty typedef struct Suffix {
152e2eeea75SSimon J. Gerraty 	/* The suffix itself, such as ".c" */
153e2eeea75SSimon J. Gerraty 	char *name;
154e2eeea75SSimon J. Gerraty 	/* Length of the name, to avoid strlen calls */
155e2eeea75SSimon J. Gerraty 	size_t nameLen;
15612904384SSimon J. Gerraty 	/*
15712904384SSimon J. Gerraty 	 * This suffix marks include files.  Their search path ends up in the
15812904384SSimon J. Gerraty 	 * undocumented special variable '.INCLUDES'.
15912904384SSimon J. Gerraty 	 */
16012904384SSimon J. Gerraty 	bool include:1;
16112904384SSimon J. Gerraty 	/*
16212904384SSimon J. Gerraty 	 * This suffix marks library files.  Their search path ends up in the
16312904384SSimon J. Gerraty 	 * undocumented special variable '.LIBS'.
16412904384SSimon J. Gerraty 	 */
16512904384SSimon J. Gerraty 	bool library:1;
16612904384SSimon J. Gerraty 	/*
16712904384SSimon J. Gerraty 	 * The empty suffix.
16812904384SSimon J. Gerraty 	 *
16912904384SSimon J. Gerraty 	 * XXX: What is the difference between the empty suffix and the null
17012904384SSimon J. Gerraty 	 * suffix?
17112904384SSimon J. Gerraty 	 *
17212904384SSimon J. Gerraty 	 * XXX: Why is SUFF_NULL needed at all? Wouldn't nameLen == 0 mean
17312904384SSimon J. Gerraty 	 * the same?
17412904384SSimon J. Gerraty 	 */
17512904384SSimon J. Gerraty 	bool isNull:1;
176e2eeea75SSimon J. Gerraty 	/* The path along which files of this suffix may be found */
177e2eeea75SSimon J. Gerraty 	SearchPath *searchPath;
17812904384SSimon J. Gerraty 
179e2eeea75SSimon J. Gerraty 	/* The suffix number; TODO: document the purpose of this number */
180e2eeea75SSimon J. Gerraty 	int sNum;
181e2eeea75SSimon J. Gerraty 	/* Reference count of list membership and several other places */
182e2eeea75SSimon J. Gerraty 	int refCount;
18312904384SSimon J. Gerraty 
184e2eeea75SSimon J. Gerraty 	/* Suffixes we have a transformation to */
18506b9b3e0SSimon J. Gerraty 	SuffixList parents;
186e2eeea75SSimon J. Gerraty 	/* Suffixes we have a transformation from */
18706b9b3e0SSimon J. Gerraty 	SuffixList children;
18812904384SSimon J. Gerraty 	/*
18912904384SSimon J. Gerraty 	 * Lists in which this suffix is referenced.
19006b9b3e0SSimon J. Gerraty 	 *
19106b9b3e0SSimon J. Gerraty 	 * XXX: These lists are used nowhere, they are just appended to, for
19206b9b3e0SSimon J. Gerraty 	 * no apparent reason.  They do have the side effect of increasing
19312904384SSimon J. Gerraty 	 * refCount though.
19412904384SSimon J. Gerraty 	 */
19506b9b3e0SSimon J. Gerraty 	SuffixListList ref;
19606b9b3e0SSimon J. Gerraty } Suffix;
1973955d011SMarcel Moolenaar 
1983955d011SMarcel Moolenaar /*
19906b9b3e0SSimon J. Gerraty  * A candidate when searching for implied sources.
20006b9b3e0SSimon J. Gerraty  *
20106b9b3e0SSimon J. Gerraty  * For example, when "src.o" is to be made, a typical candidate is "src.c"
20206b9b3e0SSimon J. Gerraty  * via the transformation rule ".c.o".  If that doesn't exist, maybe there is
20306b9b3e0SSimon J. Gerraty  * another transformation rule ".pas.c" that would make "src.pas" an indirect
20406b9b3e0SSimon J. Gerraty  * candidate as well.  The first such chain that leads to an existing file or
20506b9b3e0SSimon J. Gerraty  * node is finally chosen to be made.
2063955d011SMarcel Moolenaar  */
20706b9b3e0SSimon J. Gerraty typedef struct Candidate {
20806b9b3e0SSimon J. Gerraty 	/* The file or node to look for. */
20906b9b3e0SSimon J. Gerraty 	char *file;
210*9f45a3c8SSimon J. Gerraty 	/*
211*9f45a3c8SSimon J. Gerraty 	 * The prefix from which file was formed. Its memory is shared among
212*9f45a3c8SSimon J. Gerraty 	 * all candidates.
213*9f45a3c8SSimon J. Gerraty 	 */
21406b9b3e0SSimon J. Gerraty 	char *prefix;
21506b9b3e0SSimon J. Gerraty 	/* The suffix on the file. */
21606b9b3e0SSimon J. Gerraty 	Suffix *suff;
21706b9b3e0SSimon J. Gerraty 
218*9f45a3c8SSimon J. Gerraty 	/*
219*9f45a3c8SSimon J. Gerraty 	 * The candidate that can be made from this, or NULL for the
220*9f45a3c8SSimon J. Gerraty 	 * top-level candidate.
221*9f45a3c8SSimon J. Gerraty 	 */
22206b9b3e0SSimon J. Gerraty 	struct Candidate *parent;
22306b9b3e0SSimon J. Gerraty 	/* The node describing the file. */
22406b9b3e0SSimon J. Gerraty 	GNode *node;
22506b9b3e0SSimon J. Gerraty 
226*9f45a3c8SSimon J. Gerraty 	/*
227*9f45a3c8SSimon J. Gerraty 	 * Count of existing children, only used for memory management, so we
228*9f45a3c8SSimon J. Gerraty 	 * don't free this candidate too early or too late.
229*9f45a3c8SSimon J. Gerraty 	 */
23006b9b3e0SSimon J. Gerraty 	int numChildren;
2313955d011SMarcel Moolenaar #ifdef DEBUG_SRC
23206b9b3e0SSimon J. Gerraty 	CandidateList childrenList;
2333955d011SMarcel Moolenaar #endif
23406b9b3e0SSimon J. Gerraty } Candidate;
2353955d011SMarcel Moolenaar 
23606b9b3e0SSimon J. Gerraty typedef struct CandidateSearcher {
23706b9b3e0SSimon J. Gerraty 
23806b9b3e0SSimon J. Gerraty 	CandidateList list;
23906b9b3e0SSimon J. Gerraty 
24006b9b3e0SSimon J. Gerraty 	/*
24106b9b3e0SSimon J. Gerraty 	 * TODO: Add HashSet for seen entries, to avoid endless loops such as
24206b9b3e0SSimon J. Gerraty 	 * in suff-transform-endless.mk.
24306b9b3e0SSimon J. Gerraty 	 */
24406b9b3e0SSimon J. Gerraty 
24506b9b3e0SSimon J. Gerraty } CandidateSearcher;
24606b9b3e0SSimon J. Gerraty 
24706b9b3e0SSimon J. Gerraty 
24806b9b3e0SSimon J. Gerraty /* TODO: Document the difference between nullSuff and emptySuff. */
249*9f45a3c8SSimon J. Gerraty /* The NULL suffix is used when a file has no known suffix */
25006b9b3e0SSimon J. Gerraty static Suffix *nullSuff;
251e2eeea75SSimon J. Gerraty /* The empty suffix required for POSIX single-suffix transformation rules */
25206b9b3e0SSimon J. Gerraty static Suffix *emptySuff;
2533955d011SMarcel Moolenaar 
2543955d011SMarcel Moolenaar 
25506b9b3e0SSimon J. Gerraty static Suffix *
25606b9b3e0SSimon J. Gerraty Suffix_Ref(Suffix *suff)
25706b9b3e0SSimon J. Gerraty {
25806b9b3e0SSimon J. Gerraty 	suff->refCount++;
25906b9b3e0SSimon J. Gerraty 	return suff;
26006b9b3e0SSimon J. Gerraty }
26106b9b3e0SSimon J. Gerraty 
26206b9b3e0SSimon J. Gerraty /* Change the value of a Suffix variable, adjusting the reference counts. */
26306b9b3e0SSimon J. Gerraty static void
26406b9b3e0SSimon J. Gerraty Suffix_Reassign(Suffix **var, Suffix *suff)
26506b9b3e0SSimon J. Gerraty {
26606b9b3e0SSimon J. Gerraty 	if (*var != NULL)
26706b9b3e0SSimon J. Gerraty 		(*var)->refCount--;
26806b9b3e0SSimon J. Gerraty 	*var = suff;
26906b9b3e0SSimon J. Gerraty 	suff->refCount++;
27006b9b3e0SSimon J. Gerraty }
27106b9b3e0SSimon J. Gerraty 
27206b9b3e0SSimon J. Gerraty /* Set a Suffix variable to NULL, adjusting the reference count. */
27306b9b3e0SSimon J. Gerraty static void
27406b9b3e0SSimon J. Gerraty Suffix_Unassign(Suffix **var)
27506b9b3e0SSimon J. Gerraty {
27606b9b3e0SSimon J. Gerraty 	if (*var != NULL)
27706b9b3e0SSimon J. Gerraty 		(*var)->refCount--;
27806b9b3e0SSimon J. Gerraty 	*var = NULL;
27906b9b3e0SSimon J. Gerraty }
2803955d011SMarcel Moolenaar 
281e2eeea75SSimon J. Gerraty /*
2823955d011SMarcel Moolenaar  * See if pref is a prefix of str.
283e2eeea75SSimon J. Gerraty  * Return NULL if it ain't, pointer to character in str after prefix if so.
2843955d011SMarcel Moolenaar  */
2853955d011SMarcel Moolenaar static const char *
28606b9b3e0SSimon J. Gerraty StrTrimPrefix(const char *pref, const char *str)
2873955d011SMarcel Moolenaar {
28806b9b3e0SSimon J. Gerraty 	while (*str != '\0' && *pref == *str) {
2893955d011SMarcel Moolenaar 		pref++;
2903955d011SMarcel Moolenaar 		str++;
2913955d011SMarcel Moolenaar 	}
2923955d011SMarcel Moolenaar 
293e2eeea75SSimon J. Gerraty 	return *pref != '\0' ? NULL : str;
2943955d011SMarcel Moolenaar }
2953955d011SMarcel Moolenaar 
296e2eeea75SSimon J. Gerraty /*
29706b9b3e0SSimon J. Gerraty  * See if suff is a suffix of str, and if so, return the pointer to the suffix
29806b9b3e0SSimon J. Gerraty  * in str, which at the same time marks the end of the prefix.
2993955d011SMarcel Moolenaar  */
300956e45f6SSimon J. Gerraty static const char *
30106b9b3e0SSimon J. Gerraty StrTrimSuffix(const char *str, size_t strLen, const char *suff, size_t suffLen)
3023955d011SMarcel Moolenaar {
30306b9b3e0SSimon J. Gerraty 	const char *suffInStr;
30406b9b3e0SSimon J. Gerraty 	size_t i;
3053955d011SMarcel Moolenaar 
30606b9b3e0SSimon J. Gerraty 	if (strLen < suffLen)
30706b9b3e0SSimon J. Gerraty 		return NULL;
3083955d011SMarcel Moolenaar 
30906b9b3e0SSimon J. Gerraty 	suffInStr = str + strLen - suffLen;
31006b9b3e0SSimon J. Gerraty 	for (i = 0; i < suffLen; i++)
31106b9b3e0SSimon J. Gerraty 		if (suff[i] != suffInStr[i])
31206b9b3e0SSimon J. Gerraty 			return NULL;
3133955d011SMarcel Moolenaar 
31406b9b3e0SSimon J. Gerraty 	return suffInStr;
3153955d011SMarcel Moolenaar }
3163955d011SMarcel Moolenaar 
31706b9b3e0SSimon J. Gerraty /*
31806b9b3e0SSimon J. Gerraty  * See if suff is a suffix of name, and if so, return the end of the prefix
31906b9b3e0SSimon J. Gerraty  * in name.
32006b9b3e0SSimon J. Gerraty  */
32106b9b3e0SSimon J. Gerraty static const char *
32206b9b3e0SSimon J. Gerraty Suffix_TrimSuffix(const Suffix *suff, size_t nameLen, const char *nameEnd)
32306b9b3e0SSimon J. Gerraty {
32406b9b3e0SSimon J. Gerraty 	return StrTrimSuffix(nameEnd - nameLen, nameLen,
32506b9b3e0SSimon J. Gerraty 	    suff->name, suff->nameLen);
3263955d011SMarcel Moolenaar }
3273955d011SMarcel Moolenaar 
328b0c40a00SSimon J. Gerraty static bool
32906b9b3e0SSimon J. Gerraty Suffix_IsSuffix(const Suffix *suff, size_t nameLen, const char *nameEnd)
3303955d011SMarcel Moolenaar {
33106b9b3e0SSimon J. Gerraty 	return Suffix_TrimSuffix(suff, nameLen, nameEnd) != NULL;
3323955d011SMarcel Moolenaar }
3333955d011SMarcel Moolenaar 
33406b9b3e0SSimon J. Gerraty static Suffix *
33506b9b3e0SSimon J. Gerraty FindSuffixByNameLen(const char *name, size_t nameLen)
3363955d011SMarcel Moolenaar {
33706b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
338956e45f6SSimon J. Gerraty 
33906b9b3e0SSimon J. Gerraty 	for (ln = sufflist.first; ln != NULL; ln = ln->next) {
34006b9b3e0SSimon J. Gerraty 		Suffix *suff = ln->datum;
34106b9b3e0SSimon J. Gerraty 		if (suff->nameLen == nameLen &&
34206b9b3e0SSimon J. Gerraty 		    memcmp(suff->name, name, nameLen) == 0)
343956e45f6SSimon J. Gerraty 			return suff;
344956e45f6SSimon J. Gerraty 	}
345956e45f6SSimon J. Gerraty 	return NULL;
3463955d011SMarcel Moolenaar }
3473955d011SMarcel Moolenaar 
34806b9b3e0SSimon J. Gerraty static Suffix *
34906b9b3e0SSimon J. Gerraty FindSuffixByName(const char *name)
3503955d011SMarcel Moolenaar {
35106b9b3e0SSimon J. Gerraty 	return FindSuffixByNameLen(name, strlen(name));
3523955d011SMarcel Moolenaar }
3533955d011SMarcel Moolenaar 
354956e45f6SSimon J. Gerraty static GNode *
355956e45f6SSimon J. Gerraty FindTransformByName(const char *name)
3563955d011SMarcel Moolenaar {
357956e45f6SSimon J. Gerraty 	GNodeListNode *ln;
35806b9b3e0SSimon J. Gerraty 
35906b9b3e0SSimon J. Gerraty 	for (ln = transforms.first; ln != NULL; ln = ln->next) {
360956e45f6SSimon J. Gerraty 		GNode *gn = ln->datum;
361956e45f6SSimon J. Gerraty 		if (strcmp(gn->name, name) == 0)
362956e45f6SSimon J. Gerraty 			return gn;
363956e45f6SSimon J. Gerraty 	}
364956e45f6SSimon J. Gerraty 	return NULL;
3653955d011SMarcel Moolenaar }
3663955d011SMarcel Moolenaar 
3673955d011SMarcel Moolenaar static void
36806b9b3e0SSimon J. Gerraty SuffixList_Unref(SuffixList *list, Suffix *suff)
3693955d011SMarcel Moolenaar {
37006b9b3e0SSimon J. Gerraty 	SuffixListNode *ln = Lst_FindDatum(list, suff);
3713955d011SMarcel Moolenaar 	if (ln != NULL) {
372956e45f6SSimon J. Gerraty 		Lst_Remove(list, ln);
373956e45f6SSimon J. Gerraty 		suff->refCount--;
3743955d011SMarcel Moolenaar 	}
3753955d011SMarcel Moolenaar }
3763955d011SMarcel Moolenaar 
3772c3632d1SSimon J. Gerraty /* Free up all memory associated with the given suffix structure. */
3783955d011SMarcel Moolenaar static void
37906b9b3e0SSimon J. Gerraty Suffix_Free(Suffix *suff)
3803955d011SMarcel Moolenaar {
3813955d011SMarcel Moolenaar 
38206b9b3e0SSimon J. Gerraty 	if (suff == nullSuff)
38306b9b3e0SSimon J. Gerraty 		nullSuff = NULL;
3843955d011SMarcel Moolenaar 
385e2eeea75SSimon J. Gerraty 	if (suff == emptySuff)
3863955d011SMarcel Moolenaar 		emptySuff = NULL;
3873955d011SMarcel Moolenaar 
388956e45f6SSimon J. Gerraty #if 0
3893955d011SMarcel Moolenaar 	/* We don't delete suffixes in order, so we cannot use this */
390e2eeea75SSimon J. Gerraty 	if (suff->refCount != 0)
391e2eeea75SSimon J. Gerraty 		Punt("Internal error deleting suffix `%s' with refcount = %d",
392e2eeea75SSimon J. Gerraty 		    suff->name, suff->refCount);
3933955d011SMarcel Moolenaar #endif
3943955d011SMarcel Moolenaar 
39506b9b3e0SSimon J. Gerraty 	Lst_Done(&suff->ref);
39606b9b3e0SSimon J. Gerraty 	Lst_Done(&suff->children);
39706b9b3e0SSimon J. Gerraty 	Lst_Done(&suff->parents);
39806b9b3e0SSimon J. Gerraty 	SearchPath_Free(suff->searchPath);
3993955d011SMarcel Moolenaar 
400e2eeea75SSimon J. Gerraty 	free(suff->name);
401e2eeea75SSimon J. Gerraty 	free(suff);
4023955d011SMarcel Moolenaar }
4033955d011SMarcel Moolenaar 
40406b9b3e0SSimon J. Gerraty static void
40506b9b3e0SSimon J. Gerraty SuffFree(void *p)
40606b9b3e0SSimon J. Gerraty {
40706b9b3e0SSimon J. Gerraty 	Suffix_Free(p);
40806b9b3e0SSimon J. Gerraty }
40906b9b3e0SSimon J. Gerraty 
4102c3632d1SSimon J. Gerraty /* Remove the suffix from the list, and free if it is otherwise unused. */
4113955d011SMarcel Moolenaar static void
41206b9b3e0SSimon J. Gerraty SuffixList_Remove(SuffixList *list, Suffix *suff)
4133955d011SMarcel Moolenaar {
41406b9b3e0SSimon J. Gerraty 	SuffixList_Unref(list, suff);
415956e45f6SSimon J. Gerraty 	if (suff->refCount == 0) {
416e2eeea75SSimon J. Gerraty 		/* XXX: can lead to suff->refCount == -1 */
41706b9b3e0SSimon J. Gerraty 		SuffixList_Unref(&sufflist, suff);
41806b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "Removing suffix \"%s\"\n", suff->name);
419956e45f6SSimon J. Gerraty 		SuffFree(suff);
4203955d011SMarcel Moolenaar 	}
4213955d011SMarcel Moolenaar }
4222c3632d1SSimon J. Gerraty 
42306b9b3e0SSimon J. Gerraty /*
42406b9b3e0SSimon J. Gerraty  * Insert the suffix into the list, keeping the list ordered by suffix
42506b9b3e0SSimon J. Gerraty  * number.
42606b9b3e0SSimon J. Gerraty  */
4273955d011SMarcel Moolenaar static void
42806b9b3e0SSimon J. Gerraty SuffixList_Insert(SuffixList *list, Suffix *suff)
4293955d011SMarcel Moolenaar {
43006b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
43106b9b3e0SSimon J. Gerraty 	Suffix *listSuff = NULL;
4323955d011SMarcel Moolenaar 
433956e45f6SSimon J. Gerraty 	for (ln = list->first; ln != NULL; ln = ln->next) {
434956e45f6SSimon J. Gerraty 		listSuff = ln->datum;
435956e45f6SSimon J. Gerraty 		if (listSuff->sNum >= suff->sNum)
4363955d011SMarcel Moolenaar 			break;
4373955d011SMarcel Moolenaar 	}
4382c3632d1SSimon J. Gerraty 
4393955d011SMarcel Moolenaar 	if (ln == NULL) {
44006b9b3e0SSimon J. Gerraty 		DEBUG2(SUFF, "inserting \"%s\" (%d) at end of list\n",
441956e45f6SSimon J. Gerraty 		    suff->name, suff->sNum);
44206b9b3e0SSimon J. Gerraty 		Lst_Append(list, Suffix_Ref(suff));
44306b9b3e0SSimon J. Gerraty 		Lst_Append(&suff->ref, list);
444956e45f6SSimon J. Gerraty 	} else if (listSuff->sNum != suff->sNum) {
445e2eeea75SSimon J. Gerraty 		DEBUG4(SUFF, "inserting \"%s\" (%d) before \"%s\" (%d)\n",
446956e45f6SSimon J. Gerraty 		    suff->name, suff->sNum, listSuff->name, listSuff->sNum);
44706b9b3e0SSimon J. Gerraty 		Lst_InsertBefore(list, ln, Suffix_Ref(suff));
44806b9b3e0SSimon J. Gerraty 		Lst_Append(&suff->ref, list);
4492c3632d1SSimon J. Gerraty 	} else {
45006b9b3e0SSimon J. Gerraty 		DEBUG2(SUFF, "\"%s\" (%d) is already there\n",
45106b9b3e0SSimon J. Gerraty 		    suff->name, suff->sNum);
4523955d011SMarcel Moolenaar 	}
4533955d011SMarcel Moolenaar }
4543955d011SMarcel Moolenaar 
455e2eeea75SSimon J. Gerraty static void
45606b9b3e0SSimon J. Gerraty Relate(Suffix *srcSuff, Suffix *targSuff)
457e2eeea75SSimon J. Gerraty {
45806b9b3e0SSimon J. Gerraty 	SuffixList_Insert(&targSuff->children, srcSuff);
45906b9b3e0SSimon J. Gerraty 	SuffixList_Insert(&srcSuff->parents, targSuff);
460e2eeea75SSimon J. Gerraty }
461e2eeea75SSimon J. Gerraty 
46206b9b3e0SSimon J. Gerraty static Suffix *
46306b9b3e0SSimon J. Gerraty Suffix_New(const char *name)
4642c3632d1SSimon J. Gerraty {
46506b9b3e0SSimon J. Gerraty 	Suffix *suff = bmake_malloc(sizeof *suff);
4662c3632d1SSimon J. Gerraty 
467e2eeea75SSimon J. Gerraty 	suff->name = bmake_strdup(name);
468e2eeea75SSimon J. Gerraty 	suff->nameLen = strlen(suff->name);
46906b9b3e0SSimon J. Gerraty 	suff->searchPath = SearchPath_New();
47006b9b3e0SSimon J. Gerraty 	Lst_Init(&suff->children);
47106b9b3e0SSimon J. Gerraty 	Lst_Init(&suff->parents);
47206b9b3e0SSimon J. Gerraty 	Lst_Init(&suff->ref);
473e2eeea75SSimon J. Gerraty 	suff->sNum = sNum++;
47412904384SSimon J. Gerraty 	suff->include = false;
47512904384SSimon J. Gerraty 	suff->library = false;
47612904384SSimon J. Gerraty 	suff->isNull = false;
477e2eeea75SSimon J. Gerraty 	suff->refCount = 1; /* XXX: why 1? It's not assigned anywhere yet. */
4782c3632d1SSimon J. Gerraty 
479e2eeea75SSimon J. Gerraty 	return suff;
4802c3632d1SSimon J. Gerraty }
4812c3632d1SSimon J. Gerraty 
482e2eeea75SSimon J. Gerraty /*
483e2eeea75SSimon J. Gerraty  * Nuke the list of suffixes but keep all transformation rules around. The
484e2eeea75SSimon J. Gerraty  * transformation graph is destroyed in this process, but we leave the list
485e2eeea75SSimon J. Gerraty  * of rules so when a new graph is formed, the rules will remain. This
486e2eeea75SSimon J. Gerraty  * function is called when a line '.SUFFIXES:' with an empty suffixes list is
487e2eeea75SSimon J. Gerraty  * encountered in a makefile.
488e2eeea75SSimon J. Gerraty  */
4893955d011SMarcel Moolenaar void
4903955d011SMarcel Moolenaar Suff_ClearSuffixes(void)
4913955d011SMarcel Moolenaar {
4923955d011SMarcel Moolenaar #ifdef CLEANUP
49306b9b3e0SSimon J. Gerraty 	Lst_MoveAll(&suffClean, &sufflist);
4943955d011SMarcel Moolenaar #endif
49506b9b3e0SSimon J. Gerraty 	DEBUG0(SUFF, "Clearing all suffixes\n");
49606b9b3e0SSimon J. Gerraty 	Lst_Init(&sufflist);
4973955d011SMarcel Moolenaar 	sNum = 0;
49806b9b3e0SSimon J. Gerraty 	if (nullSuff != NULL)
49906b9b3e0SSimon J. Gerraty 		SuffFree(nullSuff);
50006b9b3e0SSimon J. Gerraty 	emptySuff = nullSuff = Suffix_New("");
5016e050540SSimon J. Gerraty 
50206b9b3e0SSimon J. Gerraty 	SearchPath_AddAll(nullSuff->searchPath, &dirSearchPath);
50312904384SSimon J. Gerraty 	nullSuff->include = false;
50412904384SSimon J. Gerraty 	nullSuff->library = false;
50512904384SSimon J. Gerraty 	nullSuff->isNull = true;
5063955d011SMarcel Moolenaar }
5073955d011SMarcel Moolenaar 
50806b9b3e0SSimon J. Gerraty /*
50906b9b3e0SSimon J. Gerraty  * Parse a transformation string such as ".c.o" to find its two component
510956e45f6SSimon J. Gerraty  * suffixes (the source ".c" and the target ".o").  If there are no such
511956e45f6SSimon J. Gerraty  * suffixes, try a single-suffix transformation as well.
5123955d011SMarcel Moolenaar  *
513b0c40a00SSimon J. Gerraty  * Return true if the string is a valid transformation.
5143955d011SMarcel Moolenaar  */
515b0c40a00SSimon J. Gerraty static bool
51606b9b3e0SSimon J. Gerraty ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
5173955d011SMarcel Moolenaar {
51806b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
51906b9b3e0SSimon J. Gerraty 	Suffix *single = NULL;
5203955d011SMarcel Moolenaar 
5213955d011SMarcel Moolenaar 	/*
5223955d011SMarcel Moolenaar 	 * Loop looking first for a suffix that matches the start of the
5233955d011SMarcel Moolenaar 	 * string and then for one that exactly matches the rest of it. If
5243955d011SMarcel Moolenaar 	 * we can find two that meet these criteria, we've successfully
5253955d011SMarcel Moolenaar 	 * parsed the string.
5263955d011SMarcel Moolenaar 	 */
52706b9b3e0SSimon J. Gerraty 	for (ln = sufflist.first; ln != NULL; ln = ln->next) {
52806b9b3e0SSimon J. Gerraty 		Suffix *src = ln->datum;
529956e45f6SSimon J. Gerraty 
53006b9b3e0SSimon J. Gerraty 		if (StrTrimPrefix(src->name, str) == NULL)
531956e45f6SSimon J. Gerraty 			continue;
532956e45f6SSimon J. Gerraty 
533956e45f6SSimon J. Gerraty 		if (str[src->nameLen] == '\0') {
53406b9b3e0SSimon J. Gerraty 			single = src;
5353955d011SMarcel Moolenaar 		} else {
53606b9b3e0SSimon J. Gerraty 			Suffix *targ = FindSuffixByName(str + src->nameLen);
537956e45f6SSimon J. Gerraty 			if (targ != NULL) {
538956e45f6SSimon J. Gerraty 				*out_src = src;
539956e45f6SSimon J. Gerraty 				*out_targ = targ;
540b0c40a00SSimon J. Gerraty 				return true;
5413955d011SMarcel Moolenaar 			}
542956e45f6SSimon J. Gerraty 		}
543956e45f6SSimon J. Gerraty 	}
544956e45f6SSimon J. Gerraty 
54506b9b3e0SSimon J. Gerraty 	if (single != NULL) {
5463955d011SMarcel Moolenaar 		/*
54706b9b3e0SSimon J. Gerraty 		 * There was a suffix that encompassed the entire string, so we
54806b9b3e0SSimon J. Gerraty 		 * assume it was a transformation to the null suffix (thank you
54906b9b3e0SSimon J. Gerraty 		 * POSIX; search for "single suffix" or "single-suffix").
5503955d011SMarcel Moolenaar 		 *
55106b9b3e0SSimon J. Gerraty 		 * We still prefer to find a double rule over a singleton,
55206b9b3e0SSimon J. Gerraty 		 * hence we leave this check until the end.
55306b9b3e0SSimon J. Gerraty 		 *
55406b9b3e0SSimon J. Gerraty 		 * XXX: Use emptySuff over nullSuff?
5553955d011SMarcel Moolenaar 		 */
55606b9b3e0SSimon J. Gerraty 		*out_src = single;
55706b9b3e0SSimon J. Gerraty 		*out_targ = nullSuff;
558b0c40a00SSimon J. Gerraty 		return true;
5593955d011SMarcel Moolenaar 	}
560b0c40a00SSimon J. Gerraty 	return false;
5613955d011SMarcel Moolenaar }
5623955d011SMarcel Moolenaar 
56306b9b3e0SSimon J. Gerraty /*
564b0c40a00SSimon J. Gerraty  * Return true if the given string is a transformation rule, that is, a
565e2eeea75SSimon J. Gerraty  * concatenation of two known suffixes such as ".c.o" or a single suffix
56606b9b3e0SSimon J. Gerraty  * such as ".o".
56706b9b3e0SSimon J. Gerraty  */
568b0c40a00SSimon J. Gerraty bool
569956e45f6SSimon J. Gerraty Suff_IsTransform(const char *str)
5703955d011SMarcel Moolenaar {
57106b9b3e0SSimon J. Gerraty 	Suffix *src, *targ;
5723955d011SMarcel Moolenaar 
57306b9b3e0SSimon J. Gerraty 	return ParseTransform(str, &src, &targ);
5743955d011SMarcel Moolenaar }
5753955d011SMarcel Moolenaar 
57606b9b3e0SSimon J. Gerraty /*
57706b9b3e0SSimon J. Gerraty  * Add the transformation rule to the list of rules and place the
578956e45f6SSimon J. Gerraty  * transformation itself in the graph.
5793955d011SMarcel Moolenaar  *
580956e45f6SSimon J. Gerraty  * The transformation is linked to the two suffixes mentioned in the name.
581956e45f6SSimon J. Gerraty  *
5823955d011SMarcel Moolenaar  * Input:
583956e45f6SSimon J. Gerraty  *	name		must have the form ".from.to" or just ".from"
5843955d011SMarcel Moolenaar  *
5853955d011SMarcel Moolenaar  * Results:
586956e45f6SSimon J. Gerraty  *	The created or existing transformation node in the transforms list
5873955d011SMarcel Moolenaar  */
5883955d011SMarcel Moolenaar GNode *
589956e45f6SSimon J. Gerraty Suff_AddTransform(const char *name)
5903955d011SMarcel Moolenaar {
59106b9b3e0SSimon J. Gerraty 	Suffix *srcSuff;
59206b9b3e0SSimon J. Gerraty 	Suffix *targSuff;
5933955d011SMarcel Moolenaar 
594e2eeea75SSimon J. Gerraty 	GNode *gn = FindTransformByName(name);
595956e45f6SSimon J. Gerraty 	if (gn == NULL) {
5963955d011SMarcel Moolenaar 		/*
59706b9b3e0SSimon J. Gerraty 		 * Make a new graph node for the transformation. It will be
59806b9b3e0SSimon J. Gerraty 		 * filled in by the Parse module.
5993955d011SMarcel Moolenaar 		 */
600e2eeea75SSimon J. Gerraty 		gn = GNode_New(name);
60106b9b3e0SSimon J. Gerraty 		Lst_Append(&transforms, gn);
6023955d011SMarcel Moolenaar 	} else {
6033955d011SMarcel Moolenaar 		/*
60406b9b3e0SSimon J. Gerraty 		 * New specification for transformation rule. Just nuke the
60506b9b3e0SSimon J. Gerraty 		 * old list of commands so they can be filled in again. We
60606b9b3e0SSimon J. Gerraty 		 * don't actually free the commands themselves, because a
60706b9b3e0SSimon J. Gerraty 		 * given command can be attached to several different
60806b9b3e0SSimon J. Gerraty 		 * transformations.
6093955d011SMarcel Moolenaar 		 */
61006b9b3e0SSimon J. Gerraty 		Lst_Done(&gn->commands);
61106b9b3e0SSimon J. Gerraty 		Lst_Init(&gn->commands);
61206b9b3e0SSimon J. Gerraty 		Lst_Done(&gn->children);
61306b9b3e0SSimon J. Gerraty 		Lst_Init(&gn->children);
6143955d011SMarcel Moolenaar 	}
6153955d011SMarcel Moolenaar 
6163955d011SMarcel Moolenaar 	gn->type = OP_TRANSFORM;
6173955d011SMarcel Moolenaar 
618e2eeea75SSimon J. Gerraty 	{
61906b9b3e0SSimon J. Gerraty 		/* TODO: Avoid the redundant parsing here. */
620b0c40a00SSimon J. Gerraty 		bool ok = ParseTransform(name, &srcSuff, &targSuff);
6212c3632d1SSimon J. Gerraty 		assert(ok);
62212904384SSimon J. Gerraty 		/* LINTED 129 *//* expression has null effect */
6232c3632d1SSimon J. Gerraty 		(void)ok;
624e2eeea75SSimon J. Gerraty 	}
6253955d011SMarcel Moolenaar 
62606b9b3e0SSimon J. Gerraty 	/* Link the two together in the proper relationship and order. */
62706b9b3e0SSimon J. Gerraty 	DEBUG2(SUFF, "defining transformation from `%s' to `%s'\n",
628e2eeea75SSimon J. Gerraty 	    srcSuff->name, targSuff->name);
62906b9b3e0SSimon J. Gerraty 	Relate(srcSuff, targSuff);
6303955d011SMarcel Moolenaar 
6313841c287SSimon J. Gerraty 	return gn;
6323955d011SMarcel Moolenaar }
6333955d011SMarcel Moolenaar 
63406b9b3e0SSimon J. Gerraty /*
63506b9b3e0SSimon J. Gerraty  * Handle the finish of a transformation definition, removing the
6362c3632d1SSimon J. Gerraty  * transformation from the graph if it has neither commands nor sources.
6372c3632d1SSimon J. Gerraty  *
6382c3632d1SSimon J. Gerraty  * If the node has no commands or children, the children and parents lists
6392c3632d1SSimon J. Gerraty  * of the affected suffixes are altered.
6403955d011SMarcel Moolenaar  *
6413955d011SMarcel Moolenaar  * Input:
642956e45f6SSimon J. Gerraty  *	gn		Node for transformation
6433955d011SMarcel Moolenaar  */
644956e45f6SSimon J. Gerraty void
645956e45f6SSimon J. Gerraty Suff_EndTransform(GNode *gn)
6463955d011SMarcel Moolenaar {
64706b9b3e0SSimon J. Gerraty 	Suffix *srcSuff, *targSuff;
64806b9b3e0SSimon J. Gerraty 	SuffixList *srcSuffParents;
649e2eeea75SSimon J. Gerraty 
65006b9b3e0SSimon J. Gerraty 	if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(&gn->cohorts))
65106b9b3e0SSimon J. Gerraty 		gn = gn->cohorts.last->datum;
65206b9b3e0SSimon J. Gerraty 
65306b9b3e0SSimon J. Gerraty 	if (!(gn->type & OP_TRANSFORM))
65406b9b3e0SSimon J. Gerraty 		return;
65506b9b3e0SSimon J. Gerraty 
65606b9b3e0SSimon J. Gerraty 	if (!Lst_IsEmpty(&gn->commands) || !Lst_IsEmpty(&gn->children)) {
65706b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "transformation %s complete\n", gn->name);
65806b9b3e0SSimon J. Gerraty 		return;
65906b9b3e0SSimon J. Gerraty 	}
6603955d011SMarcel Moolenaar 
6613955d011SMarcel Moolenaar 	/*
6623955d011SMarcel Moolenaar 	 * SuffParseTransform() may fail for special rules which are not
6633955d011SMarcel Moolenaar 	 * actual transformation rules. (e.g. .DEFAULT)
6643955d011SMarcel Moolenaar 	 */
66506b9b3e0SSimon J. Gerraty 	if (!ParseTransform(gn->name, &srcSuff, &targSuff))
66606b9b3e0SSimon J. Gerraty 		return;
667e2eeea75SSimon J. Gerraty 
66806b9b3e0SSimon J. Gerraty 	DEBUG2(SUFF, "deleting incomplete transformation from `%s' to `%s'\n",
669e2eeea75SSimon J. Gerraty 	    srcSuff->name, targSuff->name);
6703955d011SMarcel Moolenaar 
67106b9b3e0SSimon J. Gerraty 	/*
67206b9b3e0SSimon J. Gerraty 	 * Remember the parents since srcSuff could be deleted in
67306b9b3e0SSimon J. Gerraty 	 * SuffixList_Remove.
67406b9b3e0SSimon J. Gerraty 	 */
67506b9b3e0SSimon J. Gerraty 	srcSuffParents = &srcSuff->parents;
67606b9b3e0SSimon J. Gerraty 	SuffixList_Remove(&targSuff->children, srcSuff);
67706b9b3e0SSimon J. Gerraty 	SuffixList_Remove(srcSuffParents, targSuff);
6783955d011SMarcel Moolenaar }
6793955d011SMarcel Moolenaar 
68006b9b3e0SSimon J. Gerraty /*
68106b9b3e0SSimon J. Gerraty  * Called from Suff_AddSuffix to search through the list of
6822c3632d1SSimon J. Gerraty  * existing transformation rules and rebuild the transformation graph when
6832c3632d1SSimon J. Gerraty  * it has been destroyed by Suff_ClearSuffixes. If the given rule is a
6842c3632d1SSimon J. Gerraty  * transformation involving this suffix and another, existing suffix, the
6852c3632d1SSimon J. Gerraty  * proper relationship is established between the two.
6862c3632d1SSimon J. Gerraty  *
6872c3632d1SSimon J. Gerraty  * The appropriate links will be made between this suffix and others if
6882c3632d1SSimon J. Gerraty  * transformation rules exist for it.
6893955d011SMarcel Moolenaar  *
6903955d011SMarcel Moolenaar  * Input:
691956e45f6SSimon J. Gerraty  *	transform	Transformation to test
692956e45f6SSimon J. Gerraty  *	suff		Suffix to rebuild
6933955d011SMarcel Moolenaar  */
694956e45f6SSimon J. Gerraty static void
69506b9b3e0SSimon J. Gerraty RebuildGraph(GNode *transform, Suffix *suff)
6963955d011SMarcel Moolenaar {
697956e45f6SSimon J. Gerraty 	const char *name = transform->name;
698956e45f6SSimon J. Gerraty 	size_t nameLen = strlen(name);
699956e45f6SSimon J. Gerraty 	const char *toName;
7003955d011SMarcel Moolenaar 
701*9f45a3c8SSimon J. Gerraty 	/*
702*9f45a3c8SSimon J. Gerraty 	 * See if it is a transformation from this suffix to another suffix.
703*9f45a3c8SSimon J. Gerraty 	 */
70406b9b3e0SSimon J. Gerraty 	toName = StrTrimPrefix(suff->name, name);
705956e45f6SSimon J. Gerraty 	if (toName != NULL) {
70606b9b3e0SSimon J. Gerraty 		Suffix *to = FindSuffixByName(toName);
707956e45f6SSimon J. Gerraty 		if (to != NULL) {
70806b9b3e0SSimon J. Gerraty 			Relate(suff, to);
709956e45f6SSimon J. Gerraty 			return;
7103955d011SMarcel Moolenaar 		}
7113955d011SMarcel Moolenaar 	}
7123955d011SMarcel Moolenaar 
713*9f45a3c8SSimon J. Gerraty 	/*
714*9f45a3c8SSimon J. Gerraty 	 * See if it is a transformation from another suffix to this suffix.
715*9f45a3c8SSimon J. Gerraty 	 */
71606b9b3e0SSimon J. Gerraty 	toName = Suffix_TrimSuffix(suff, nameLen, name + nameLen);
717956e45f6SSimon J. Gerraty 	if (toName != NULL) {
71806b9b3e0SSimon J. Gerraty 		Suffix *from = FindSuffixByNameLen(name,
71906b9b3e0SSimon J. Gerraty 		    (size_t)(toName - name));
720e2eeea75SSimon J. Gerraty 		if (from != NULL)
72106b9b3e0SSimon J. Gerraty 			Relate(from, suff);
7223955d011SMarcel Moolenaar 	}
7233955d011SMarcel Moolenaar }
7243955d011SMarcel Moolenaar 
72506b9b3e0SSimon J. Gerraty /*
72606b9b3e0SSimon J. Gerraty  * During Suff_AddSuffix, search through the list of existing targets and find
727956e45f6SSimon J. Gerraty  * if any of the existing targets can be turned into a transformation rule.
7282c3632d1SSimon J. Gerraty  *
7292c3632d1SSimon J. Gerraty  * If such a target is found and the target is the current main target, the
7302c3632d1SSimon J. Gerraty  * main target is set to NULL and the next target examined (if that exists)
7312c3632d1SSimon J. Gerraty  * becomes the main target.
7323955d011SMarcel Moolenaar  *
7333955d011SMarcel Moolenaar  * Results:
734b0c40a00SSimon J. Gerraty  *	true iff a new main target has been selected.
7353955d011SMarcel Moolenaar  */
736b0c40a00SSimon J. Gerraty static bool
737*9f45a3c8SSimon J. Gerraty UpdateTarget(GNode *target, Suffix *suff, bool *inout_removedMain)
7383955d011SMarcel Moolenaar {
73906b9b3e0SSimon J. Gerraty 	Suffix *srcSuff, *targSuff;
7403955d011SMarcel Moolenaar 	char *ptr;
7413955d011SMarcel Moolenaar 
742*9f45a3c8SSimon J. Gerraty 	if (mainNode == NULL && *inout_removedMain &&
743*9f45a3c8SSimon J. Gerraty 	    GNode_IsMainCandidate(target)) {
74406b9b3e0SSimon J. Gerraty 		DEBUG1(MAKE, "Setting main node to \"%s\"\n", target->name);
745*9f45a3c8SSimon J. Gerraty 		mainNode = target;
74606b9b3e0SSimon J. Gerraty 		/*
747b0c40a00SSimon J. Gerraty 		 * XXX: Why could it be a good idea to return true here?
74806b9b3e0SSimon J. Gerraty 		 * The main task of this function is to turn ordinary nodes
74906b9b3e0SSimon J. Gerraty 		 * into transformations, no matter whether or not a new .MAIN
75006b9b3e0SSimon J. Gerraty 		 * node has been found.
75106b9b3e0SSimon J. Gerraty 		 */
75206b9b3e0SSimon J. Gerraty 		/*
753b0c40a00SSimon J. Gerraty 		 * XXX: Even when changing this to false, none of the existing
75406b9b3e0SSimon J. Gerraty 		 * unit tests fails.
75506b9b3e0SSimon J. Gerraty 		 */
756b0c40a00SSimon J. Gerraty 		return true;
7573955d011SMarcel Moolenaar 	}
7583955d011SMarcel Moolenaar 
7592c3632d1SSimon J. Gerraty 	if (target->type == OP_TRANSFORM)
760b0c40a00SSimon J. Gerraty 		return false;
7613955d011SMarcel Moolenaar 
76206b9b3e0SSimon J. Gerraty 	/*
76306b9b3e0SSimon J. Gerraty 	 * XXX: What about a transformation ".cpp.c"?  If ".c" is added as
76406b9b3e0SSimon J. Gerraty 	 * a new suffix, it seems wrong that this transformation would be
76506b9b3e0SSimon J. Gerraty 	 * skipped just because ".c" happens to be a prefix of ".cpp".
76606b9b3e0SSimon J. Gerraty 	 */
76706b9b3e0SSimon J. Gerraty 	ptr = strstr(target->name, suff->name);
76806b9b3e0SSimon J. Gerraty 	if (ptr == NULL)
769b0c40a00SSimon J. Gerraty 		return false;
7703955d011SMarcel Moolenaar 
77106b9b3e0SSimon J. Gerraty 	/*
77206b9b3e0SSimon J. Gerraty 	 * XXX: In suff-rebuild.mk, in the line '.SUFFIXES: .c .b .a', this
77306b9b3e0SSimon J. Gerraty 	 * condition prevents the rule '.b.c' from being added again during
77406b9b3e0SSimon J. Gerraty 	 * Suff_AddSuffix(".b").
77506b9b3e0SSimon J. Gerraty 	 *
77606b9b3e0SSimon J. Gerraty 	 * XXX: Removing this paragraph makes suff-add-later.mk use massive
77706b9b3e0SSimon J. Gerraty 	 * amounts of memory.
77806b9b3e0SSimon J. Gerraty 	 */
77906b9b3e0SSimon J. Gerraty 	if (ptr == target->name)
780b0c40a00SSimon J. Gerraty 		return false;
78106b9b3e0SSimon J. Gerraty 
78206b9b3e0SSimon J. Gerraty 	if (ParseTransform(target->name, &srcSuff, &targSuff)) {
783*9f45a3c8SSimon J. Gerraty 		if (mainNode == target) {
78406b9b3e0SSimon J. Gerraty 			DEBUG1(MAKE,
78506b9b3e0SSimon J. Gerraty 			    "Setting main node from \"%s\" back to null\n",
78606b9b3e0SSimon J. Gerraty 			    target->name);
787b0c40a00SSimon J. Gerraty 			*inout_removedMain = true;
788*9f45a3c8SSimon J. Gerraty 			mainNode = NULL;
7893955d011SMarcel Moolenaar 		}
79006b9b3e0SSimon J. Gerraty 		Lst_Done(&target->children);
79106b9b3e0SSimon J. Gerraty 		Lst_Init(&target->children);
7923955d011SMarcel Moolenaar 		target->type = OP_TRANSFORM;
79306b9b3e0SSimon J. Gerraty 
7943955d011SMarcel Moolenaar 		/*
79506b9b3e0SSimon J. Gerraty 		 * Link the two together in the proper relationship and order.
7963955d011SMarcel Moolenaar 		 */
79706b9b3e0SSimon J. Gerraty 		DEBUG2(SUFF, "defining transformation from `%s' to `%s'\n",
798e2eeea75SSimon J. Gerraty 		    srcSuff->name, targSuff->name);
79906b9b3e0SSimon J. Gerraty 		Relate(srcSuff, targSuff);
8003955d011SMarcel Moolenaar 	}
801b0c40a00SSimon J. Gerraty 	return false;
802956e45f6SSimon J. Gerraty }
803956e45f6SSimon J. Gerraty 
80406b9b3e0SSimon J. Gerraty /*
80506b9b3e0SSimon J. Gerraty  * Look at all existing targets to see if adding this suffix will make one
806956e45f6SSimon J. Gerraty  * of the current targets mutate into a suffix rule.
807956e45f6SSimon J. Gerraty  *
808956e45f6SSimon J. Gerraty  * This is ugly, but other makes treat all targets that start with a '.' as
80906b9b3e0SSimon J. Gerraty  * suffix rules.
81006b9b3e0SSimon J. Gerraty  */
811956e45f6SSimon J. Gerraty static void
812*9f45a3c8SSimon J. Gerraty UpdateTargets(Suffix *suff)
813956e45f6SSimon J. Gerraty {
814b0c40a00SSimon J. Gerraty 	bool removedMain = false;
815956e45f6SSimon J. Gerraty 	GNodeListNode *ln;
81606b9b3e0SSimon J. Gerraty 
817956e45f6SSimon J. Gerraty 	for (ln = Targ_List()->first; ln != NULL; ln = ln->next) {
818956e45f6SSimon J. Gerraty 		GNode *gn = ln->datum;
819*9f45a3c8SSimon J. Gerraty 		if (UpdateTarget(gn, suff, &removedMain))
820956e45f6SSimon J. Gerraty 			break;
821956e45f6SSimon J. Gerraty 	}
8223955d011SMarcel Moolenaar }
8233955d011SMarcel Moolenaar 
82406b9b3e0SSimon J. Gerraty /*
82506b9b3e0SSimon J. Gerraty  * Add the suffix to the end of the list of known suffixes.
82606b9b3e0SSimon J. Gerraty  * Should we restructure the suffix graph? Make doesn't.
8273955d011SMarcel Moolenaar  *
82806b9b3e0SSimon J. Gerraty  * A GNode is created for the suffix (XXX: this sounds completely wrong) and
82906b9b3e0SSimon J. Gerraty  * a Suffix structure is created and added to the suffixes list unless the
83006b9b3e0SSimon J. Gerraty  * suffix was already known.
8313955d011SMarcel Moolenaar  * The mainNode passed can be modified if a target mutated into a
8323955d011SMarcel Moolenaar  * transform and that target happened to be the main target.
8332c3632d1SSimon J. Gerraty  *
8342c3632d1SSimon J. Gerraty  * Input:
8352c3632d1SSimon J. Gerraty  *	name		the name of the suffix to add
8363955d011SMarcel Moolenaar  */
8373955d011SMarcel Moolenaar void
838*9f45a3c8SSimon J. Gerraty Suff_AddSuffix(const char *name)
8393955d011SMarcel Moolenaar {
840956e45f6SSimon J. Gerraty 	GNodeListNode *ln;
8413955d011SMarcel Moolenaar 
84206b9b3e0SSimon J. Gerraty 	Suffix *suff = FindSuffixByName(name);
84306b9b3e0SSimon J. Gerraty 	if (suff != NULL)
844956e45f6SSimon J. Gerraty 		return;
845956e45f6SSimon J. Gerraty 
84606b9b3e0SSimon J. Gerraty 	suff = Suffix_New(name);
84706b9b3e0SSimon J. Gerraty 	Lst_Append(&sufflist, suff);
84806b9b3e0SSimon J. Gerraty 	DEBUG1(SUFF, "Adding suffix \"%s\"\n", suff->name);
849956e45f6SSimon J. Gerraty 
850*9f45a3c8SSimon J. Gerraty 	UpdateTargets(suff);
851956e45f6SSimon J. Gerraty 
8523955d011SMarcel Moolenaar 	/*
8533955d011SMarcel Moolenaar 	 * Look for any existing transformations from or to this suffix.
8543955d011SMarcel Moolenaar 	 * XXX: Only do this after a Suff_ClearSuffixes?
8553955d011SMarcel Moolenaar 	 */
85606b9b3e0SSimon J. Gerraty 	for (ln = transforms.first; ln != NULL; ln = ln->next)
85706b9b3e0SSimon J. Gerraty 		RebuildGraph(ln->datum, suff);
8583955d011SMarcel Moolenaar }
8593955d011SMarcel Moolenaar 
8602c3632d1SSimon J. Gerraty /* Return the search path for the given suffix, or NULL. */
861956e45f6SSimon J. Gerraty SearchPath *
862956e45f6SSimon J. Gerraty Suff_GetPath(const char *sname)
8633955d011SMarcel Moolenaar {
86406b9b3e0SSimon J. Gerraty 	Suffix *suff = FindSuffixByName(sname);
86506b9b3e0SSimon J. Gerraty 	return suff != NULL ? suff->searchPath : NULL;
8663955d011SMarcel Moolenaar }
8673955d011SMarcel Moolenaar 
868e2eeea75SSimon J. Gerraty /*
869e2eeea75SSimon J. Gerraty  * Extend the search paths for all suffixes to include the default search
870e2eeea75SSimon J. Gerraty  * path (dirSearchPath).
8713955d011SMarcel Moolenaar  *
872e2eeea75SSimon J. Gerraty  * The default search path can be defined using the special target '.PATH'.
873e2eeea75SSimon J. Gerraty  * The search path of each suffix can be defined using the special target
874e2eeea75SSimon J. Gerraty  * '.PATH<suffix>'.
875e2eeea75SSimon J. Gerraty  *
876e2eeea75SSimon J. Gerraty  * If paths were specified for the ".h" suffix, the directories are stuffed
877e2eeea75SSimon J. Gerraty  * into a global variable called ".INCLUDES" with each directory preceded by
878e2eeea75SSimon J. Gerraty  * '-I'. The same is done for the ".a" suffix, except the variable is called
879e2eeea75SSimon J. Gerraty  * ".LIBS" and the flag is '-L'.
8803955d011SMarcel Moolenaar  */
8813955d011SMarcel Moolenaar void
882b0c40a00SSimon J. Gerraty Suff_ExtendPaths(void)
8833955d011SMarcel Moolenaar {
88406b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
88506b9b3e0SSimon J. Gerraty 	char *flags;
88606b9b3e0SSimon J. Gerraty 	SearchPath *includesPath = SearchPath_New();
88706b9b3e0SSimon J. Gerraty 	SearchPath *libsPath = SearchPath_New();
8883955d011SMarcel Moolenaar 
88906b9b3e0SSimon J. Gerraty 	for (ln = sufflist.first; ln != NULL; ln = ln->next) {
89006b9b3e0SSimon J. Gerraty 		Suffix *suff = ln->datum;
891dba7b0efSSimon J. Gerraty 		if (!Lst_IsEmpty(&suff->searchPath->dirs)) {
8923955d011SMarcel Moolenaar #ifdef INCLUDES
89312904384SSimon J. Gerraty 			if (suff->include)
89406b9b3e0SSimon J. Gerraty 				SearchPath_AddAll(includesPath,
89506b9b3e0SSimon J. Gerraty 				    suff->searchPath);
896956e45f6SSimon J. Gerraty #endif
8973955d011SMarcel Moolenaar #ifdef LIBRARIES
89812904384SSimon J. Gerraty 			if (suff->library)
89906b9b3e0SSimon J. Gerraty 				SearchPath_AddAll(libsPath, suff->searchPath);
900956e45f6SSimon J. Gerraty #endif
90106b9b3e0SSimon J. Gerraty 			SearchPath_AddAll(suff->searchPath, &dirSearchPath);
9023955d011SMarcel Moolenaar 		} else {
90306b9b3e0SSimon J. Gerraty 			SearchPath_Free(suff->searchPath);
90406b9b3e0SSimon J. Gerraty 			suff->searchPath = Dir_CopyDirSearchPath();
9053955d011SMarcel Moolenaar 		}
9063955d011SMarcel Moolenaar 	}
9073955d011SMarcel Moolenaar 
908dba7b0efSSimon J. Gerraty 	flags = SearchPath_ToFlags(includesPath, "-I");
909dba7b0efSSimon J. Gerraty 	Global_Set(".INCLUDES", flags);
91006b9b3e0SSimon J. Gerraty 	free(flags);
9113955d011SMarcel Moolenaar 
912dba7b0efSSimon J. Gerraty 	flags = SearchPath_ToFlags(libsPath, "-L");
913dba7b0efSSimon J. Gerraty 	Global_Set(".LIBS", flags);
91406b9b3e0SSimon J. Gerraty 	free(flags);
91506b9b3e0SSimon J. Gerraty 
91606b9b3e0SSimon J. Gerraty 	SearchPath_Free(includesPath);
91706b9b3e0SSimon J. Gerraty 	SearchPath_Free(libsPath);
9183955d011SMarcel Moolenaar }
9193955d011SMarcel Moolenaar 
92006b9b3e0SSimon J. Gerraty /*
92106b9b3e0SSimon J. Gerraty  * Add the given suffix as a type of file which gets included.
92206b9b3e0SSimon J. Gerraty  * Called when a '.INCLUDES: .h' line is parsed.
92306b9b3e0SSimon J. Gerraty  * To have an effect, the suffix must already exist.
92406b9b3e0SSimon J. Gerraty  * This affects the magic variable '.INCLUDES'.
9253955d011SMarcel Moolenaar  */
9263955d011SMarcel Moolenaar void
92706b9b3e0SSimon J. Gerraty Suff_AddInclude(const char *suffName)
9283955d011SMarcel Moolenaar {
92906b9b3e0SSimon J. Gerraty 	Suffix *suff = FindSuffixByName(suffName);
930956e45f6SSimon J. Gerraty 	if (suff != NULL)
93112904384SSimon J. Gerraty 		suff->include = true;
9323955d011SMarcel Moolenaar }
9333955d011SMarcel Moolenaar 
93406b9b3e0SSimon J. Gerraty /*
93506b9b3e0SSimon J. Gerraty  * Add the given suffix as a type of file which is a library.
93606b9b3e0SSimon J. Gerraty  * Called when a '.LIBS: .a' line is parsed.
93706b9b3e0SSimon J. Gerraty  * To have an effect, the suffix must already exist.
93806b9b3e0SSimon J. Gerraty  * This affects the magic variable '.LIBS'.
9393955d011SMarcel Moolenaar  */
9403955d011SMarcel Moolenaar void
94106b9b3e0SSimon J. Gerraty Suff_AddLib(const char *suffName)
9423955d011SMarcel Moolenaar {
94306b9b3e0SSimon J. Gerraty 	Suffix *suff = FindSuffixByName(suffName);
944956e45f6SSimon J. Gerraty 	if (suff != NULL)
94512904384SSimon J. Gerraty 		suff->library = true;
9463955d011SMarcel Moolenaar }
9473955d011SMarcel Moolenaar 
9483955d011SMarcel Moolenaar /********** Implicit Source Search Functions *********/
9493955d011SMarcel Moolenaar 
95006b9b3e0SSimon J. Gerraty static void
95106b9b3e0SSimon J. Gerraty CandidateSearcher_Init(CandidateSearcher *cs)
95206b9b3e0SSimon J. Gerraty {
95306b9b3e0SSimon J. Gerraty 	Lst_Init(&cs->list);
95406b9b3e0SSimon J. Gerraty }
95506b9b3e0SSimon J. Gerraty 
95606b9b3e0SSimon J. Gerraty static void
95706b9b3e0SSimon J. Gerraty CandidateSearcher_Done(CandidateSearcher *cs)
95806b9b3e0SSimon J. Gerraty {
95906b9b3e0SSimon J. Gerraty 	Lst_Done(&cs->list);
96006b9b3e0SSimon J. Gerraty }
96106b9b3e0SSimon J. Gerraty 
96206b9b3e0SSimon J. Gerraty static void
96306b9b3e0SSimon J. Gerraty CandidateSearcher_Add(CandidateSearcher *cs, Candidate *cand)
96406b9b3e0SSimon J. Gerraty {
96506b9b3e0SSimon J. Gerraty 	/* TODO: filter duplicates */
96606b9b3e0SSimon J. Gerraty 	Lst_Append(&cs->list, cand);
96706b9b3e0SSimon J. Gerraty }
96806b9b3e0SSimon J. Gerraty 
96906b9b3e0SSimon J. Gerraty static void
97006b9b3e0SSimon J. Gerraty CandidateSearcher_AddIfNew(CandidateSearcher *cs, Candidate *cand)
97106b9b3e0SSimon J. Gerraty {
97206b9b3e0SSimon J. Gerraty 	/* TODO: filter duplicates */
97306b9b3e0SSimon J. Gerraty 	if (Lst_FindDatum(&cs->list, cand) == NULL)
97406b9b3e0SSimon J. Gerraty 		Lst_Append(&cs->list, cand);
97506b9b3e0SSimon J. Gerraty }
97606b9b3e0SSimon J. Gerraty 
97706b9b3e0SSimon J. Gerraty static void
97806b9b3e0SSimon J. Gerraty CandidateSearcher_MoveAll(CandidateSearcher *cs, CandidateList *list)
97906b9b3e0SSimon J. Gerraty {
98006b9b3e0SSimon J. Gerraty 	/* TODO: filter duplicates */
98106b9b3e0SSimon J. Gerraty 	Lst_MoveAll(&cs->list, list);
98206b9b3e0SSimon J. Gerraty }
98306b9b3e0SSimon J. Gerraty 
98406b9b3e0SSimon J. Gerraty 
985956e45f6SSimon J. Gerraty #ifdef DEBUG_SRC
986956e45f6SSimon J. Gerraty static void
98706b9b3e0SSimon J. Gerraty CandidateList_PrintAddrs(CandidateList *list)
988956e45f6SSimon J. Gerraty {
98906b9b3e0SSimon J. Gerraty 	CandidateListNode *ln;
99006b9b3e0SSimon J. Gerraty 
99106b9b3e0SSimon J. Gerraty 	for (ln = list->first; ln != NULL; ln = ln->next) {
99206b9b3e0SSimon J. Gerraty 		Candidate *cand = ln->datum;
99306b9b3e0SSimon J. Gerraty 		debug_printf(" %p:%s", cand, cand->file);
99406b9b3e0SSimon J. Gerraty 	}
995956e45f6SSimon J. Gerraty 	debug_printf("\n");
996956e45f6SSimon J. Gerraty }
997956e45f6SSimon J. Gerraty #endif
998956e45f6SSimon J. Gerraty 
99906b9b3e0SSimon J. Gerraty static Candidate *
100006b9b3e0SSimon J. Gerraty Candidate_New(char *name, char *prefix, Suffix *suff, Candidate *parent,
100106b9b3e0SSimon J. Gerraty 	      GNode *gn)
1002956e45f6SSimon J. Gerraty {
100306b9b3e0SSimon J. Gerraty 	Candidate *cand = bmake_malloc(sizeof *cand);
1004956e45f6SSimon J. Gerraty 
100506b9b3e0SSimon J. Gerraty 	cand->file = name;
100606b9b3e0SSimon J. Gerraty 	cand->prefix = prefix;
100706b9b3e0SSimon J. Gerraty 	cand->suff = Suffix_Ref(suff);
100806b9b3e0SSimon J. Gerraty 	cand->parent = parent;
100906b9b3e0SSimon J. Gerraty 	cand->node = gn;
101006b9b3e0SSimon J. Gerraty 	cand->numChildren = 0;
1011956e45f6SSimon J. Gerraty #ifdef DEBUG_SRC
101206b9b3e0SSimon J. Gerraty 	Lst_Init(&cand->childrenList);
1013956e45f6SSimon J. Gerraty #endif
1014956e45f6SSimon J. Gerraty 
101506b9b3e0SSimon J. Gerraty 	return cand;
1016956e45f6SSimon J. Gerraty }
1017956e45f6SSimon J. Gerraty 
101806b9b3e0SSimon J. Gerraty /* Add a new candidate to the list. */
101906b9b3e0SSimon J. Gerraty /*ARGSUSED*/
1020956e45f6SSimon J. Gerraty static void
102106b9b3e0SSimon J. Gerraty CandidateList_Add(CandidateList *list, char *srcName, Candidate *targ,
102206b9b3e0SSimon J. Gerraty 		  Suffix *suff, const char *debug_tag)
1023956e45f6SSimon J. Gerraty {
102406b9b3e0SSimon J. Gerraty 	Candidate *cand = Candidate_New(srcName, targ->prefix, suff, targ,
102506b9b3e0SSimon J. Gerraty 	    NULL);
1026e2eeea75SSimon J. Gerraty 	targ->numChildren++;
102706b9b3e0SSimon J. Gerraty 	Lst_Append(list, cand);
102806b9b3e0SSimon J. Gerraty 
1029956e45f6SSimon J. Gerraty #ifdef DEBUG_SRC
103006b9b3e0SSimon J. Gerraty 	Lst_Append(&targ->childrenList, cand);
103106b9b3e0SSimon J. Gerraty 	debug_printf("%s add suff %p:%s candidate %p:%s to list %p:",
103206b9b3e0SSimon J. Gerraty 	    debug_tag, targ, targ->file, cand, cand->file, list);
103306b9b3e0SSimon J. Gerraty 	CandidateList_PrintAddrs(list);
1034956e45f6SSimon J. Gerraty #endif
1035956e45f6SSimon J. Gerraty }
1036956e45f6SSimon J. Gerraty 
103706b9b3e0SSimon J. Gerraty /*
103806b9b3e0SSimon J. Gerraty  * Add all candidates to the list that can be formed by applying a suffix to
103906b9b3e0SSimon J. Gerraty  * the candidate.
10403955d011SMarcel Moolenaar  */
1041956e45f6SSimon J. Gerraty static void
104206b9b3e0SSimon J. Gerraty CandidateList_AddCandidatesFor(CandidateList *list, Candidate *cand)
10433955d011SMarcel Moolenaar {
104406b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
104506b9b3e0SSimon J. Gerraty 	for (ln = cand->suff->children.first; ln != NULL; ln = ln->next) {
104606b9b3e0SSimon J. Gerraty 		Suffix *suff = ln->datum;
104706b9b3e0SSimon J. Gerraty 
104812904384SSimon J. Gerraty 		if (suff->isNull && suff->name[0] != '\0') {
10493955d011SMarcel Moolenaar 			/*
105006b9b3e0SSimon J. Gerraty 			 * If the suffix has been marked as the NULL suffix,
105106b9b3e0SSimon J. Gerraty 			 * also create a candidate for a file with no suffix
105206b9b3e0SSimon J. Gerraty 			 * attached.
10533955d011SMarcel Moolenaar 			 */
105406b9b3e0SSimon J. Gerraty 			CandidateList_Add(list, bmake_strdup(cand->prefix),
105506b9b3e0SSimon J. Gerraty 			    cand, suff, "1");
10563955d011SMarcel Moolenaar 		}
10573955d011SMarcel Moolenaar 
105806b9b3e0SSimon J. Gerraty 		CandidateList_Add(list, str_concat2(cand->prefix, suff->name),
105906b9b3e0SSimon J. Gerraty 		    cand, suff, "2");
1060956e45f6SSimon J. Gerraty 	}
10613955d011SMarcel Moolenaar }
10623955d011SMarcel Moolenaar 
106306b9b3e0SSimon J. Gerraty /*
106406b9b3e0SSimon J. Gerraty  * Free the first candidate in the list that is not referenced anymore.
106506b9b3e0SSimon J. Gerraty  * Return whether a candidate was removed.
106606b9b3e0SSimon J. Gerraty  */
1067b0c40a00SSimon J. Gerraty static bool
106806b9b3e0SSimon J. Gerraty RemoveCandidate(CandidateList *srcs)
10693955d011SMarcel Moolenaar {
107006b9b3e0SSimon J. Gerraty 	CandidateListNode *ln;
10712c3632d1SSimon J. Gerraty 
10723955d011SMarcel Moolenaar #ifdef DEBUG_SRC
107306b9b3e0SSimon J. Gerraty 	debug_printf("cleaning list %p:", srcs);
107406b9b3e0SSimon J. Gerraty 	CandidateList_PrintAddrs(srcs);
10753955d011SMarcel Moolenaar #endif
10763955d011SMarcel Moolenaar 
107706b9b3e0SSimon J. Gerraty 	for (ln = srcs->first; ln != NULL; ln = ln->next) {
107806b9b3e0SSimon J. Gerraty 		Candidate *src = ln->datum;
1079956e45f6SSimon J. Gerraty 
1080e2eeea75SSimon J. Gerraty 		if (src->numChildren == 0) {
1081e2eeea75SSimon J. Gerraty 			if (src->parent == NULL)
108206b9b3e0SSimon J. Gerraty 				free(src->prefix);
10833955d011SMarcel Moolenaar 			else {
10843955d011SMarcel Moolenaar #ifdef DEBUG_SRC
108506b9b3e0SSimon J. Gerraty 				/* XXX: Lst_RemoveDatum */
108606b9b3e0SSimon J. Gerraty 				CandidateListNode *ln2;
108706b9b3e0SSimon J. Gerraty 				ln2 = Lst_FindDatum(&src->parent->childrenList,
108806b9b3e0SSimon J. Gerraty 				    src);
108995e3ed2cSSimon J. Gerraty 				if (ln2 != NULL)
109006b9b3e0SSimon J. Gerraty 					Lst_Remove(&src->parent->childrenList,
109106b9b3e0SSimon J. Gerraty 					    ln2);
10923955d011SMarcel Moolenaar #endif
1093e2eeea75SSimon J. Gerraty 				src->parent->numChildren--;
10943955d011SMarcel Moolenaar 			}
10953955d011SMarcel Moolenaar #ifdef DEBUG_SRC
109606b9b3e0SSimon J. Gerraty 			debug_printf("free: list %p src %p:%s children %d\n",
109706b9b3e0SSimon J. Gerraty 			    srcs, src, src->file, src->numChildren);
109806b9b3e0SSimon J. Gerraty 			Lst_Done(&src->childrenList);
10993955d011SMarcel Moolenaar #endif
110006b9b3e0SSimon J. Gerraty 			Lst_Remove(srcs, ln);
110106b9b3e0SSimon J. Gerraty 			free(src->file);
1102e2eeea75SSimon J. Gerraty 			free(src);
1103b0c40a00SSimon J. Gerraty 			return true;
11043955d011SMarcel Moolenaar 		}
11053955d011SMarcel Moolenaar #ifdef DEBUG_SRC
11063955d011SMarcel Moolenaar 		else {
110706b9b3e0SSimon J. Gerraty 			debug_printf("keep: list %p src %p:%s children %d:",
110806b9b3e0SSimon J. Gerraty 			    srcs, src, src->file, src->numChildren);
110906b9b3e0SSimon J. Gerraty 			CandidateList_PrintAddrs(&src->childrenList);
11103955d011SMarcel Moolenaar 		}
11113955d011SMarcel Moolenaar #endif
11123955d011SMarcel Moolenaar 	}
11133955d011SMarcel Moolenaar 
1114b0c40a00SSimon J. Gerraty 	return false;
11153955d011SMarcel Moolenaar }
11163955d011SMarcel Moolenaar 
1117e2eeea75SSimon J. Gerraty /* Find the first existing file/target in srcs. */
111806b9b3e0SSimon J. Gerraty static Candidate *
111906b9b3e0SSimon J. Gerraty FindThem(CandidateList *srcs, CandidateSearcher *cs)
11203955d011SMarcel Moolenaar {
112106b9b3e0SSimon J. Gerraty 	HashSet seen;
112206b9b3e0SSimon J. Gerraty 
112306b9b3e0SSimon J. Gerraty 	HashSet_Init(&seen);
11243955d011SMarcel Moolenaar 
11253955d011SMarcel Moolenaar 	while (!Lst_IsEmpty(srcs)) {
112606b9b3e0SSimon J. Gerraty 		Candidate *src = Lst_Dequeue(srcs);
11273955d011SMarcel Moolenaar 
112806b9b3e0SSimon J. Gerraty #ifdef DEBUG_SRC
112906b9b3e0SSimon J. Gerraty 		debug_printf("remove from list %p src %p:%s\n",
113006b9b3e0SSimon J. Gerraty 		    srcs, src, src->file);
113106b9b3e0SSimon J. Gerraty #endif
113206b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "\ttrying %s...", src->file);
11333955d011SMarcel Moolenaar 
11343955d011SMarcel Moolenaar 		/*
11353955d011SMarcel Moolenaar 		 * A file is considered to exist if either a node exists in the
11363955d011SMarcel Moolenaar 		 * graph for it or the file actually exists.
11373955d011SMarcel Moolenaar 		 */
1138956e45f6SSimon J. Gerraty 		if (Targ_FindNode(src->file) != NULL) {
113906b9b3e0SSimon J. Gerraty 		found:
114006b9b3e0SSimon J. Gerraty 			HashSet_Done(&seen);
114106b9b3e0SSimon J. Gerraty 			DEBUG0(SUFF, "got it\n");
114206b9b3e0SSimon J. Gerraty 			return src;
11433955d011SMarcel Moolenaar 		}
11443955d011SMarcel Moolenaar 
1145956e45f6SSimon J. Gerraty 		{
114606b9b3e0SSimon J. Gerraty 			char *file = Dir_FindFile(src->file,
114706b9b3e0SSimon J. Gerraty 			    src->suff->searchPath);
1148956e45f6SSimon J. Gerraty 			if (file != NULL) {
1149956e45f6SSimon J. Gerraty 				free(file);
115006b9b3e0SSimon J. Gerraty 				goto found;
11513955d011SMarcel Moolenaar 			}
1152956e45f6SSimon J. Gerraty 		}
11533955d011SMarcel Moolenaar 
115406b9b3e0SSimon J. Gerraty 		DEBUG0(SUFF, "not there\n");
11553955d011SMarcel Moolenaar 
115606b9b3e0SSimon J. Gerraty 		if (HashSet_Add(&seen, src->file))
115706b9b3e0SSimon J. Gerraty 			CandidateList_AddCandidatesFor(srcs, src);
115806b9b3e0SSimon J. Gerraty 		else {
115906b9b3e0SSimon J. Gerraty 			DEBUG1(SUFF, "FindThem: skipping duplicate \"%s\"\n",
116006b9b3e0SSimon J. Gerraty 			    src->file);
11613955d011SMarcel Moolenaar 		}
11623955d011SMarcel Moolenaar 
116306b9b3e0SSimon J. Gerraty 		CandidateSearcher_Add(cs, src);
11643955d011SMarcel Moolenaar 	}
11653955d011SMarcel Moolenaar 
116606b9b3e0SSimon J. Gerraty 	HashSet_Done(&seen);
116706b9b3e0SSimon J. Gerraty 	return NULL;
116806b9b3e0SSimon J. Gerraty }
116906b9b3e0SSimon J. Gerraty 
117006b9b3e0SSimon J. Gerraty /*
117106b9b3e0SSimon J. Gerraty  * See if any of the children of the candidate's GNode is one from which the
117206b9b3e0SSimon J. Gerraty  * target can be transformed. If there is one, a candidate is put together
117306b9b3e0SSimon J. Gerraty  * for it and returned.
11743955d011SMarcel Moolenaar  */
117506b9b3e0SSimon J. Gerraty static Candidate *
117606b9b3e0SSimon J. Gerraty FindCmds(Candidate *targ, CandidateSearcher *cs)
11773955d011SMarcel Moolenaar {
1178956e45f6SSimon J. Gerraty 	GNodeListNode *gln;
1179956e45f6SSimon J. Gerraty 	GNode *tgn;		/* Target GNode */
1180956e45f6SSimon J. Gerraty 	GNode *sgn;		/* Source GNode */
1181956e45f6SSimon J. Gerraty 	size_t prefLen;		/* The length of the defined prefix */
1182dba7b0efSSimon J. Gerraty 	Suffix *suff;		/* Suffix of the matching candidate */
118306b9b3e0SSimon J. Gerraty 	Candidate *ret;		/* Return value */
11843955d011SMarcel Moolenaar 
1185956e45f6SSimon J. Gerraty 	tgn = targ->node;
118606b9b3e0SSimon J. Gerraty 	prefLen = strlen(targ->prefix);
11873955d011SMarcel Moolenaar 
118806b9b3e0SSimon J. Gerraty 	for (gln = tgn->children.first; gln != NULL; gln = gln->next) {
1189dba7b0efSSimon J. Gerraty 		const char *base;
119006b9b3e0SSimon J. Gerraty 
1191956e45f6SSimon J. Gerraty 		sgn = gln->datum;
11923955d011SMarcel Moolenaar 
119306b9b3e0SSimon J. Gerraty 		if (sgn->type & OP_OPTIONAL && Lst_IsEmpty(&tgn->commands)) {
11943955d011SMarcel Moolenaar 			/*
119506b9b3e0SSimon J. Gerraty 			 * We haven't looked to see if .OPTIONAL files exist
119606b9b3e0SSimon J. Gerraty 			 * yet, so don't use one as the implicit source.
119706b9b3e0SSimon J. Gerraty 			 * This allows us to use .OPTIONAL in .depend files so
119806b9b3e0SSimon J. Gerraty 			 * make won't complain "don't know how to make xxx.h"
119906b9b3e0SSimon J. Gerraty 			 * when a dependent file has been moved/deleted.
12003955d011SMarcel Moolenaar 			 */
12013955d011SMarcel Moolenaar 			continue;
12023955d011SMarcel Moolenaar 		}
12033955d011SMarcel Moolenaar 
1204dba7b0efSSimon J. Gerraty 		base = str_basename(sgn->name);
1205dba7b0efSSimon J. Gerraty 		if (strncmp(base, targ->prefix, prefLen) != 0)
12063955d011SMarcel Moolenaar 			continue;
1207*9f45a3c8SSimon J. Gerraty 		/*
1208*9f45a3c8SSimon J. Gerraty 		 * The node matches the prefix, see if it has a known suffix.
1209*9f45a3c8SSimon J. Gerraty 		 */
1210dba7b0efSSimon J. Gerraty 		suff = FindSuffixByName(base + prefLen);
1211956e45f6SSimon J. Gerraty 		if (suff == NULL)
12123955d011SMarcel Moolenaar 			continue;
1213956e45f6SSimon J. Gerraty 
12143955d011SMarcel Moolenaar 		/*
12153955d011SMarcel Moolenaar 		 * It even has a known suffix, see if there's a transformation
12163955d011SMarcel Moolenaar 		 * defined between the node's suffix and the target's suffix.
12173955d011SMarcel Moolenaar 		 *
12183955d011SMarcel Moolenaar 		 * XXX: Handle multi-stage transformations here, too.
12193955d011SMarcel Moolenaar 		 */
12203955d011SMarcel Moolenaar 
122106b9b3e0SSimon J. Gerraty 		if (Lst_FindDatum(&suff->parents, targ->suff) != NULL)
12223955d011SMarcel Moolenaar 			break;
12233955d011SMarcel Moolenaar 	}
12243955d011SMarcel Moolenaar 
1225956e45f6SSimon J. Gerraty 	if (gln == NULL)
1226956e45f6SSimon J. Gerraty 		return NULL;
1227956e45f6SSimon J. Gerraty 
122806b9b3e0SSimon J. Gerraty 	ret = Candidate_New(bmake_strdup(sgn->name), targ->prefix, suff, targ,
122906b9b3e0SSimon J. Gerraty 	    sgn);
1230e2eeea75SSimon J. Gerraty 	targ->numChildren++;
12313955d011SMarcel Moolenaar #ifdef DEBUG_SRC
123206b9b3e0SSimon J. Gerraty 	debug_printf("3 add targ %p:%s ret %p:%s\n",
123306b9b3e0SSimon J. Gerraty 	    targ, targ->file, ret, ret->file);
123406b9b3e0SSimon J. Gerraty 	Lst_Append(&targ->childrenList, ret);
12353955d011SMarcel Moolenaar #endif
123606b9b3e0SSimon J. Gerraty 	CandidateSearcher_Add(cs, ret);
123706b9b3e0SSimon J. Gerraty 	DEBUG1(SUFF, "\tusing existing source %s\n", sgn->name);
12383841c287SSimon J. Gerraty 	return ret;
12393955d011SMarcel Moolenaar }
12403955d011SMarcel Moolenaar 
12413955d011SMarcel Moolenaar static void
124206b9b3e0SSimon J. Gerraty ExpandWildcards(GNodeListNode *cln, GNode *pgn)
12433955d011SMarcel Moolenaar {
1244956e45f6SSimon J. Gerraty 	GNode *cgn = cln->datum;
124506b9b3e0SSimon J. Gerraty 	StringList expansions;
12463955d011SMarcel Moolenaar 
124706b9b3e0SSimon J. Gerraty 	if (!Dir_HasWildcards(cgn->name))
12483955d011SMarcel Moolenaar 		return;
12493955d011SMarcel Moolenaar 
12503955d011SMarcel Moolenaar 	/*
125106b9b3e0SSimon J. Gerraty 	 * Expand the word along the chosen path
12523955d011SMarcel Moolenaar 	 */
125306b9b3e0SSimon J. Gerraty 	Lst_Init(&expansions);
1254dba7b0efSSimon J. Gerraty 	SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions);
125506b9b3e0SSimon J. Gerraty 
125606b9b3e0SSimon J. Gerraty 	while (!Lst_IsEmpty(&expansions)) {
125706b9b3e0SSimon J. Gerraty 		GNode *gn;
125806b9b3e0SSimon J. Gerraty 		/*
125906b9b3e0SSimon J. Gerraty 		 * Fetch next expansion off the list and find its GNode
126006b9b3e0SSimon J. Gerraty 		 */
126106b9b3e0SSimon J. Gerraty 		char *cp = Lst_Dequeue(&expansions);
126206b9b3e0SSimon J. Gerraty 
126306b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "%s...", cp);
126406b9b3e0SSimon J. Gerraty 		gn = Targ_GetNode(cp);
126506b9b3e0SSimon J. Gerraty 
1266*9f45a3c8SSimon J. Gerraty 		/* Insert gn before the original child. */
126706b9b3e0SSimon J. Gerraty 		Lst_InsertBefore(&pgn->children, cln, gn);
126806b9b3e0SSimon J. Gerraty 		Lst_Append(&gn->parents, pgn);
126906b9b3e0SSimon J. Gerraty 		pgn->unmade++;
12703955d011SMarcel Moolenaar 	}
12713955d011SMarcel Moolenaar 
127206b9b3e0SSimon J. Gerraty 	Lst_Done(&expansions);
12733955d011SMarcel Moolenaar 
127406b9b3e0SSimon J. Gerraty 	DEBUG0(SUFF, "\n");
127506b9b3e0SSimon J. Gerraty 
127606b9b3e0SSimon J. Gerraty 	/*
127706b9b3e0SSimon J. Gerraty 	 * Now the source is expanded, remove it from the list of children to
127806b9b3e0SSimon J. Gerraty 	 * keep it from being processed.
127906b9b3e0SSimon J. Gerraty 	 */
128006b9b3e0SSimon J. Gerraty 	pgn->unmade--;
128106b9b3e0SSimon J. Gerraty 	Lst_Remove(&pgn->children, cln);
128206b9b3e0SSimon J. Gerraty 	Lst_Remove(&cgn->parents, Lst_FindDatum(&cgn->parents, pgn));
128306b9b3e0SSimon J. Gerraty }
128406b9b3e0SSimon J. Gerraty 
128506b9b3e0SSimon J. Gerraty /*
128606b9b3e0SSimon J. Gerraty  * Break the result into a vector of strings whose nodes we can find, then
128706b9b3e0SSimon J. Gerraty  * add those nodes to the members list.
128806b9b3e0SSimon J. Gerraty  *
128906b9b3e0SSimon J. Gerraty  * Unfortunately, we can't use Str_Words because it doesn't understand about
1290b0c40a00SSimon J. Gerraty  * variable expressions with spaces in them.
129106b9b3e0SSimon J. Gerraty  */
129206b9b3e0SSimon J. Gerraty static void
129306b9b3e0SSimon J. Gerraty ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
12942c3632d1SSimon J. Gerraty {
12953955d011SMarcel Moolenaar 	char *start;
12963955d011SMarcel Moolenaar 
129706b9b3e0SSimon J. Gerraty 	pp_skip_hspace(&cp);
1298e2eeea75SSimon J. Gerraty 	start = cp;
1299956e45f6SSimon J. Gerraty 	while (*cp != '\0') {
13003955d011SMarcel Moolenaar 		if (*cp == ' ' || *cp == '\t') {
130106b9b3e0SSimon J. Gerraty 			GNode *gn;
13023955d011SMarcel Moolenaar 			/*
13033955d011SMarcel Moolenaar 			 * White-space -- terminate element, find the node,
13043955d011SMarcel Moolenaar 			 * add it, skip any further spaces.
13053955d011SMarcel Moolenaar 			 */
13063955d011SMarcel Moolenaar 			*cp++ = '\0';
1307956e45f6SSimon J. Gerraty 			gn = Targ_GetNode(start);
13082c3632d1SSimon J. Gerraty 			Lst_Append(members, gn);
1309e2eeea75SSimon J. Gerraty 			pp_skip_hspace(&cp);
131006b9b3e0SSimon J. Gerraty 			/* Continue at the next non-space. */
131106b9b3e0SSimon J. Gerraty 			start = cp;
13123955d011SMarcel Moolenaar 		} else if (*cp == '$') {
131306b9b3e0SSimon J. Gerraty 			/* Skip over the variable expression. */
1314956e45f6SSimon J. Gerraty 			const char *nested_p = cp;
131506b9b3e0SSimon J. Gerraty 			FStr junk;
13163955d011SMarcel Moolenaar 
1317b0c40a00SSimon J. Gerraty 			(void)Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY, &junk);
1318956e45f6SSimon J. Gerraty 			/* TODO: handle errors */
131906b9b3e0SSimon J. Gerraty 			if (junk.str == var_Error) {
1320956e45f6SSimon J. Gerraty 				Parse_Error(PARSE_FATAL,
1321956e45f6SSimon J. Gerraty 				    "Malformed variable expression at \"%s\"",
1322956e45f6SSimon J. Gerraty 				    cp);
1323956e45f6SSimon J. Gerraty 				cp++;
1324956e45f6SSimon J. Gerraty 			} else {
1325956e45f6SSimon J. Gerraty 				cp += nested_p - cp;
13263955d011SMarcel Moolenaar 			}
13273955d011SMarcel Moolenaar 
132806b9b3e0SSimon J. Gerraty 			FStr_Done(&junk);
1329e2eeea75SSimon J. Gerraty 		} else if (cp[0] == '\\' && cp[1] != '\0') {
133006b9b3e0SSimon J. Gerraty 			/* Escaped something -- skip over it. */
13313955d011SMarcel Moolenaar 			/*
133206b9b3e0SSimon J. Gerraty 			 * XXX: In other places, escaping at this syntactical
133306b9b3e0SSimon J. Gerraty 			 * position is done by a '$', not a '\'.  The '\' is
133406b9b3e0SSimon J. Gerraty 			 * only used in variable modifiers.
13353955d011SMarcel Moolenaar 			 */
1336956e45f6SSimon J. Gerraty 			cp += 2;
1337956e45f6SSimon J. Gerraty 		} else {
13383955d011SMarcel Moolenaar 			cp++;
13393955d011SMarcel Moolenaar 		}
13403955d011SMarcel Moolenaar 	}
13413955d011SMarcel Moolenaar 
13423955d011SMarcel Moolenaar 	if (cp != start) {
13433955d011SMarcel Moolenaar 		/*
13443955d011SMarcel Moolenaar 		 * Stuff left over -- add it to the list too
13453955d011SMarcel Moolenaar 		 */
134606b9b3e0SSimon J. Gerraty 		GNode *gn = Targ_GetNode(start);
13472c3632d1SSimon J. Gerraty 		Lst_Append(members, gn);
13483955d011SMarcel Moolenaar 	}
134906b9b3e0SSimon J. Gerraty }
135006b9b3e0SSimon J. Gerraty 
13513955d011SMarcel Moolenaar /*
135206b9b3e0SSimon J. Gerraty  * Expand the names of any children of a given node that contain variable
135306b9b3e0SSimon J. Gerraty  * expressions or file wildcards into actual targets.
135406b9b3e0SSimon J. Gerraty  *
135506b9b3e0SSimon J. Gerraty  * The expanded node is removed from the parent's list of children, and the
135606b9b3e0SSimon J. Gerraty  * parent's unmade counter is decremented, but other nodes may be added.
135706b9b3e0SSimon J. Gerraty  *
135806b9b3e0SSimon J. Gerraty  * Input:
135906b9b3e0SSimon J. Gerraty  *	cln		Child to examine
136006b9b3e0SSimon J. Gerraty  *	pgn		Parent node being processed
13613955d011SMarcel Moolenaar  */
136206b9b3e0SSimon J. Gerraty static void
136306b9b3e0SSimon J. Gerraty ExpandChildren(GNodeListNode *cln, GNode *pgn)
136406b9b3e0SSimon J. Gerraty {
136506b9b3e0SSimon J. Gerraty 	GNode *cgn = cln->datum;
136606b9b3e0SSimon J. Gerraty 	char *cp;		/* Expanded value */
136706b9b3e0SSimon J. Gerraty 
136806b9b3e0SSimon J. Gerraty 	if (!Lst_IsEmpty(&cgn->order_pred) || !Lst_IsEmpty(&cgn->order_succ))
136906b9b3e0SSimon J. Gerraty 		/* It is all too hard to process the result of .ORDER */
137006b9b3e0SSimon J. Gerraty 		return;
137106b9b3e0SSimon J. Gerraty 
137206b9b3e0SSimon J. Gerraty 	if (cgn->type & OP_WAIT)
137306b9b3e0SSimon J. Gerraty 		/* Ignore these (& OP_PHONY ?) */
137406b9b3e0SSimon J. Gerraty 		return;
137506b9b3e0SSimon J. Gerraty 
137606b9b3e0SSimon J. Gerraty 	/*
137706b9b3e0SSimon J. Gerraty 	 * First do variable expansion -- this takes precedence over wildcard
137806b9b3e0SSimon J. Gerraty 	 * expansion. If the result contains wildcards, they'll be gotten to
137906b9b3e0SSimon J. Gerraty 	 * later since the resulting words are tacked on to the end of the
138006b9b3e0SSimon J. Gerraty 	 * children list.
138106b9b3e0SSimon J. Gerraty 	 */
138206b9b3e0SSimon J. Gerraty 	if (strchr(cgn->name, '$') == NULL) {
138306b9b3e0SSimon J. Gerraty 		ExpandWildcards(cln, pgn);
138406b9b3e0SSimon J. Gerraty 		return;
138506b9b3e0SSimon J. Gerraty 	}
138606b9b3e0SSimon J. Gerraty 
138706b9b3e0SSimon J. Gerraty 	DEBUG1(SUFF, "Expanding \"%s\"...", cgn->name);
1388b0c40a00SSimon J. Gerraty 	(void)Var_Subst(cgn->name, pgn, VARE_UNDEFERR, &cp);
138906b9b3e0SSimon J. Gerraty 	/* TODO: handle errors */
139006b9b3e0SSimon J. Gerraty 
139106b9b3e0SSimon J. Gerraty 	{
139206b9b3e0SSimon J. Gerraty 		GNodeList members = LST_INIT;
139306b9b3e0SSimon J. Gerraty 
139406b9b3e0SSimon J. Gerraty 		if (cgn->type & OP_ARCHV) {
139506b9b3e0SSimon J. Gerraty 			/*
1396dba7b0efSSimon J. Gerraty 			 * Node was an 'archive(member)' target, so
139706b9b3e0SSimon J. Gerraty 			 * call on the Arch module to find the nodes for us,
1398dba7b0efSSimon J. Gerraty 			 * expanding variables in the parent's scope.
139906b9b3e0SSimon J. Gerraty 			 */
140006b9b3e0SSimon J. Gerraty 			char *p = cp;
140106b9b3e0SSimon J. Gerraty 			(void)Arch_ParseArchive(&p, &members, pgn);
140206b9b3e0SSimon J. Gerraty 		} else {
140306b9b3e0SSimon J. Gerraty 			ExpandChildrenRegular(cp, pgn, &members);
14043955d011SMarcel Moolenaar 		}
14053955d011SMarcel Moolenaar 
14063955d011SMarcel Moolenaar 		/*
14073955d011SMarcel Moolenaar 		 * Add all elements of the members list to the parent node.
14083955d011SMarcel Moolenaar 		 */
140906b9b3e0SSimon J. Gerraty 		while (!Lst_IsEmpty(&members)) {
141006b9b3e0SSimon J. Gerraty 			GNode *gn = Lst_Dequeue(&members);
14113955d011SMarcel Moolenaar 
141206b9b3e0SSimon J. Gerraty 			DEBUG1(SUFF, "%s...", gn->name);
141306b9b3e0SSimon J. Gerraty 			/*
141406b9b3e0SSimon J. Gerraty 			 * Add gn to the parents child list before the
141506b9b3e0SSimon J. Gerraty 			 * original child.
141606b9b3e0SSimon J. Gerraty 			 */
141706b9b3e0SSimon J. Gerraty 			Lst_InsertBefore(&pgn->children, cln, gn);
141806b9b3e0SSimon J. Gerraty 			Lst_Append(&gn->parents, pgn);
14193955d011SMarcel Moolenaar 			pgn->unmade++;
14203955d011SMarcel Moolenaar 			/* Expand wildcards on new node */
142106b9b3e0SSimon J. Gerraty 			ExpandWildcards(cln->prev, pgn);
14223955d011SMarcel Moolenaar 		}
142306b9b3e0SSimon J. Gerraty 		Lst_Done(&members);
14243955d011SMarcel Moolenaar 
14253955d011SMarcel Moolenaar 		free(cp);
14263955d011SMarcel Moolenaar 	}
14272c3632d1SSimon J. Gerraty 
142806b9b3e0SSimon J. Gerraty 	DEBUG0(SUFF, "\n");
14293955d011SMarcel Moolenaar 
14303955d011SMarcel Moolenaar 	/*
14313955d011SMarcel Moolenaar 	 * Now the source is expanded, remove it from the list of children to
14323955d011SMarcel Moolenaar 	 * keep it from being processed.
14333955d011SMarcel Moolenaar 	 */
14343955d011SMarcel Moolenaar 	pgn->unmade--;
143506b9b3e0SSimon J. Gerraty 	Lst_Remove(&pgn->children, cln);
143606b9b3e0SSimon J. Gerraty 	Lst_Remove(&cgn->parents, Lst_FindDatum(&cgn->parents, pgn));
14373955d011SMarcel Moolenaar }
14383955d011SMarcel Moolenaar 
14393955d011SMarcel Moolenaar static void
144006b9b3e0SSimon J. Gerraty ExpandAllChildren(GNode *gn)
14413955d011SMarcel Moolenaar {
144206b9b3e0SSimon J. Gerraty 	GNodeListNode *ln, *nln;
14433955d011SMarcel Moolenaar 
144406b9b3e0SSimon J. Gerraty 	for (ln = gn->children.first; ln != NULL; ln = nln) {
144506b9b3e0SSimon J. Gerraty 		nln = ln->next;
144606b9b3e0SSimon J. Gerraty 		ExpandChildren(ln, gn);
144706b9b3e0SSimon J. Gerraty 	}
14483955d011SMarcel Moolenaar }
14493955d011SMarcel Moolenaar 
14503955d011SMarcel Moolenaar /*
145106b9b3e0SSimon J. Gerraty  * Find a path along which to expand the node.
14523955d011SMarcel Moolenaar  *
1453e2eeea75SSimon J. Gerraty  * If the node has a known suffix, use that path.
14543955d011SMarcel Moolenaar  * If it has no known suffix, use the default system search path.
14553955d011SMarcel Moolenaar  *
14563955d011SMarcel Moolenaar  * Input:
14573955d011SMarcel Moolenaar  *	gn		Node being examined
14583955d011SMarcel Moolenaar  *
14593955d011SMarcel Moolenaar  * Results:
14603955d011SMarcel Moolenaar  *	The appropriate path to search for the GNode.
14613955d011SMarcel Moolenaar  */
1462956e45f6SSimon J. Gerraty SearchPath *
14633955d011SMarcel Moolenaar Suff_FindPath(GNode *gn)
14643955d011SMarcel Moolenaar {
146506b9b3e0SSimon J. Gerraty 	Suffix *suff = gn->suffix;
14663955d011SMarcel Moolenaar 
14673955d011SMarcel Moolenaar 	if (suff == NULL) {
1468956e45f6SSimon J. Gerraty 		char *name = gn->name;
1469956e45f6SSimon J. Gerraty 		size_t nameLen = strlen(gn->name);
147006b9b3e0SSimon J. Gerraty 		SuffixListNode *ln;
147106b9b3e0SSimon J. Gerraty 		for (ln = sufflist.first; ln != NULL; ln = ln->next)
147206b9b3e0SSimon J. Gerraty 			if (Suffix_IsSuffix(ln->datum, nameLen, name + nameLen))
1473956e45f6SSimon J. Gerraty 				break;
14743955d011SMarcel Moolenaar 
147506b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "Wildcard expanding \"%s\"...", gn->name);
14763955d011SMarcel Moolenaar 		if (ln != NULL)
1477956e45f6SSimon J. Gerraty 			suff = ln->datum;
147806b9b3e0SSimon J. Gerraty 		/*
147906b9b3e0SSimon J. Gerraty 		 * XXX: Here we can save the suffix so we don't have to do
148006b9b3e0SSimon J. Gerraty 		 * this again.
148106b9b3e0SSimon J. Gerraty 		 */
14823955d011SMarcel Moolenaar 	}
14833955d011SMarcel Moolenaar 
14843955d011SMarcel Moolenaar 	if (suff != NULL) {
148506b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "suffix is \"%s\"...\n", suff->name);
14863955d011SMarcel Moolenaar 		return suff->searchPath;
14873955d011SMarcel Moolenaar 	} else {
148806b9b3e0SSimon J. Gerraty 		DEBUG0(SUFF, "\n");
148906b9b3e0SSimon J. Gerraty 		return &dirSearchPath;	/* Use default search path */
14903955d011SMarcel Moolenaar 	}
14913955d011SMarcel Moolenaar }
14923955d011SMarcel Moolenaar 
149306b9b3e0SSimon J. Gerraty /*
149406b9b3e0SSimon J. Gerraty  * Apply a transformation rule, given the source and target nodes and
14952c3632d1SSimon J. Gerraty  * suffixes.
14963955d011SMarcel Moolenaar  *
1497e2eeea75SSimon J. Gerraty  * The source and target are linked and the commands from the transformation
1498e2eeea75SSimon J. Gerraty  * are added to the target node's commands list. The target also inherits all
1499e2eeea75SSimon J. Gerraty  * the sources for the transformation rule.
15003955d011SMarcel Moolenaar  *
15013955d011SMarcel Moolenaar  * Results:
1502b0c40a00SSimon J. Gerraty  *	true if successful, false if not.
15033955d011SMarcel Moolenaar  */
1504b0c40a00SSimon J. Gerraty static bool
150506b9b3e0SSimon J. Gerraty ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
15063955d011SMarcel Moolenaar {
1507e2eeea75SSimon J. Gerraty 	GNodeListNode *ln;
15083955d011SMarcel Moolenaar 	char *tname;		/* Name of transformation rule */
150906b9b3e0SSimon J. Gerraty 	GNode *gn;		/* Node for the transformation rule */
15103955d011SMarcel Moolenaar 
151106b9b3e0SSimon J. Gerraty 	/* Form the proper links between the target and source. */
151206b9b3e0SSimon J. Gerraty 	Lst_Append(&tgn->children, sgn);
151306b9b3e0SSimon J. Gerraty 	Lst_Append(&sgn->parents, tgn);
1514e2eeea75SSimon J. Gerraty 	tgn->unmade++;
15153955d011SMarcel Moolenaar 
151606b9b3e0SSimon J. Gerraty 	/* Locate the transformation rule itself. */
1517e2eeea75SSimon J. Gerraty 	tname = str_concat2(ssuff->name, tsuff->name);
1518956e45f6SSimon J. Gerraty 	gn = FindTransformByName(tname);
15193955d011SMarcel Moolenaar 	free(tname);
15203955d011SMarcel Moolenaar 
1521e2eeea75SSimon J. Gerraty 	/* This can happen when linking an OP_MEMBER and OP_ARCHV node. */
152206b9b3e0SSimon J. Gerraty 	if (gn == NULL)
1523b0c40a00SSimon J. Gerraty 		return false;
15243955d011SMarcel Moolenaar 
1525e2eeea75SSimon J. Gerraty 	DEBUG3(SUFF, "\tapplying %s -> %s to \"%s\"\n",
1526e2eeea75SSimon J. Gerraty 	    ssuff->name, tsuff->name, tgn->name);
15273955d011SMarcel Moolenaar 
1528e2eeea75SSimon J. Gerraty 	/* Record last child; Make_HandleUse may add child nodes. */
152906b9b3e0SSimon J. Gerraty 	ln = tgn->children.last;
15303955d011SMarcel Moolenaar 
1531e2eeea75SSimon J. Gerraty 	/* Apply the rule. */
1532e2eeea75SSimon J. Gerraty 	Make_HandleUse(gn, tgn);
15333955d011SMarcel Moolenaar 
1534e2eeea75SSimon J. Gerraty 	/* Deal with wildcards and variables in any acquired sources. */
1535e2eeea75SSimon J. Gerraty 	ln = ln != NULL ? ln->next : NULL;
1536e2eeea75SSimon J. Gerraty 	while (ln != NULL) {
1537e2eeea75SSimon J. Gerraty 		GNodeListNode *nln = ln->next;
153806b9b3e0SSimon J. Gerraty 		ExpandChildren(ln, tgn);
1539e2eeea75SSimon J. Gerraty 		ln = nln;
15403955d011SMarcel Moolenaar 	}
15413955d011SMarcel Moolenaar 
15423955d011SMarcel Moolenaar 	/*
1543e2eeea75SSimon J. Gerraty 	 * Keep track of another parent to which this node is transformed so
15443955d011SMarcel Moolenaar 	 * the .IMPSRC variable can be set correctly for the parent.
15453955d011SMarcel Moolenaar 	 */
154606b9b3e0SSimon J. Gerraty 	Lst_Append(&sgn->implicitParents, tgn);
15473955d011SMarcel Moolenaar 
1548b0c40a00SSimon J. Gerraty 	return true;
15493955d011SMarcel Moolenaar }
15503955d011SMarcel Moolenaar 
155106b9b3e0SSimon J. Gerraty /*
155206b9b3e0SSimon J. Gerraty  * Member has a known suffix, so look for a transformation rule from
155306b9b3e0SSimon J. Gerraty  * it to a possible suffix of the archive.
155406b9b3e0SSimon J. Gerraty  *
155506b9b3e0SSimon J. Gerraty  * Rather than searching through the entire list, we just look at
155606b9b3e0SSimon J. Gerraty  * suffixes to which the member's suffix may be transformed.
155706b9b3e0SSimon J. Gerraty  */
155806b9b3e0SSimon J. Gerraty static void
155906b9b3e0SSimon J. Gerraty ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
156006b9b3e0SSimon J. Gerraty {
156106b9b3e0SSimon J. Gerraty 	GNodeListNode *ln;
156206b9b3e0SSimon J. Gerraty 	size_t nameLen = (size_t)(eoarch - gn->name);
15633955d011SMarcel Moolenaar 
156406b9b3e0SSimon J. Gerraty 	/* Use first matching suffix... */
156506b9b3e0SSimon J. Gerraty 	for (ln = memSuff->parents.first; ln != NULL; ln = ln->next)
156606b9b3e0SSimon J. Gerraty 		if (Suffix_IsSuffix(ln->datum, nameLen, eoarch))
156706b9b3e0SSimon J. Gerraty 			break;
156806b9b3e0SSimon J. Gerraty 
156906b9b3e0SSimon J. Gerraty 	if (ln != NULL) {
157006b9b3e0SSimon J. Gerraty 		/* Got one -- apply it */
157106b9b3e0SSimon J. Gerraty 		Suffix *suff = ln->datum;
157206b9b3e0SSimon J. Gerraty 		if (!ApplyTransform(gn, mem, suff, memSuff)) {
157306b9b3e0SSimon J. Gerraty 			DEBUG2(SUFF, "\tNo transformation from %s -> %s\n",
157406b9b3e0SSimon J. Gerraty 			    memSuff->name, suff->name);
157506b9b3e0SSimon J. Gerraty 		}
157606b9b3e0SSimon J. Gerraty 	}
157706b9b3e0SSimon J. Gerraty }
157806b9b3e0SSimon J. Gerraty 
157906b9b3e0SSimon J. Gerraty static void FindDeps(GNode *, CandidateSearcher *);
158006b9b3e0SSimon J. Gerraty 
158106b9b3e0SSimon J. Gerraty /*
158206b9b3e0SSimon J. Gerraty  * Locate dependencies for an OP_ARCHV node.
15833955d011SMarcel Moolenaar  *
15843955d011SMarcel Moolenaar  * Input:
15853955d011SMarcel Moolenaar  *	gn		Node for which to locate dependencies
15863955d011SMarcel Moolenaar  *
15873955d011SMarcel Moolenaar  * Side Effects:
15883955d011SMarcel Moolenaar  *	Same as Suff_FindDeps
15893955d011SMarcel Moolenaar  */
15903955d011SMarcel Moolenaar static void
159106b9b3e0SSimon J. Gerraty FindDepsArchive(GNode *gn, CandidateSearcher *cs)
15923955d011SMarcel Moolenaar {
15933955d011SMarcel Moolenaar 	char *eoarch;		/* End of archive portion */
15943955d011SMarcel Moolenaar 	char *eoname;		/* End of member portion */
15953955d011SMarcel Moolenaar 	GNode *mem;		/* Node for member */
159606b9b3e0SSimon J. Gerraty 	Suffix *memSuff;
159706b9b3e0SSimon J. Gerraty 	const char *name;	/* Start of member's name */
15983955d011SMarcel Moolenaar 
15993955d011SMarcel Moolenaar 	/*
16003955d011SMarcel Moolenaar 	 * The node is an archive(member) pair. so we must find a
16013955d011SMarcel Moolenaar 	 * suffix for both of them.
16023955d011SMarcel Moolenaar 	 */
16033955d011SMarcel Moolenaar 	eoarch = strchr(gn->name, '(');
16043955d011SMarcel Moolenaar 	eoname = strchr(eoarch, ')');
16053955d011SMarcel Moolenaar 
1606e1cee40dSSimon J. Gerraty 	/*
1607e1cee40dSSimon J. Gerraty 	 * Caller guarantees the format `libname(member)', via
1608e1cee40dSSimon J. Gerraty 	 * Arch_ParseArchive.
1609e1cee40dSSimon J. Gerraty 	 */
1610e1cee40dSSimon J. Gerraty 	assert(eoarch != NULL);
1611e1cee40dSSimon J. Gerraty 	assert(eoname != NULL);
1612e1cee40dSSimon J. Gerraty 
16133955d011SMarcel Moolenaar 	*eoname = '\0';		/* Nuke parentheses during suffix search */
16143955d011SMarcel Moolenaar 	*eoarch = '\0';		/* So a suffix can be found */
16153955d011SMarcel Moolenaar 
16163955d011SMarcel Moolenaar 	name = eoarch + 1;
16173955d011SMarcel Moolenaar 
16183955d011SMarcel Moolenaar 	/*
161906b9b3e0SSimon J. Gerraty 	 * To simplify things, call Suff_FindDeps recursively on the member
162006b9b3e0SSimon J. Gerraty 	 * now, so we can simply compare the member's .PREFIX and .TARGET
162106b9b3e0SSimon J. Gerraty 	 * variables to locate its suffix. This allows us to figure out the
162206b9b3e0SSimon J. Gerraty 	 * suffix to use for the archive without having to do a quadratic
162306b9b3e0SSimon J. Gerraty 	 * search over the suffix list, backtracking for each one.
16243955d011SMarcel Moolenaar 	 */
1625956e45f6SSimon J. Gerraty 	mem = Targ_GetNode(name);
162606b9b3e0SSimon J. Gerraty 	FindDeps(mem, cs);
16273955d011SMarcel Moolenaar 
162806b9b3e0SSimon J. Gerraty 	/* Create the link between the two nodes right off. */
162906b9b3e0SSimon J. Gerraty 	Lst_Append(&gn->children, mem);
163006b9b3e0SSimon J. Gerraty 	Lst_Append(&mem->parents, gn);
1631956e45f6SSimon J. Gerraty 	gn->unmade++;
16323955d011SMarcel Moolenaar 
163306b9b3e0SSimon J. Gerraty 	/* Copy in the variables from the member node to this one. */
1634dba7b0efSSimon J. Gerraty 	Var_Set(gn, PREFIX, GNode_VarPrefix(mem));
1635dba7b0efSSimon J. Gerraty 	Var_Set(gn, TARGET, GNode_VarTarget(mem));
16363955d011SMarcel Moolenaar 
163706b9b3e0SSimon J. Gerraty 	memSuff = mem->suffix;
163806b9b3e0SSimon J. Gerraty 	if (memSuff == NULL) {	/* Didn't know what it was. */
163906b9b3e0SSimon J. Gerraty 		DEBUG0(SUFF, "using null suffix\n");
164006b9b3e0SSimon J. Gerraty 		memSuff = nullSuff;
16413955d011SMarcel Moolenaar 	}
16423955d011SMarcel Moolenaar 
16433955d011SMarcel Moolenaar 
164406b9b3e0SSimon J. Gerraty 	/* Set the other two local variables required for this target. */
1645dba7b0efSSimon J. Gerraty 	Var_Set(gn, MEMBER, name);
1646dba7b0efSSimon J. Gerraty 	Var_Set(gn, ARCHIVE, gn->name);
164706b9b3e0SSimon J. Gerraty 	/* Set $@ for compatibility with other makes. */
1648dba7b0efSSimon J. Gerraty 	Var_Set(gn, TARGET, gn->name);
16493bebe729SSimon J. Gerraty 
16503bebe729SSimon J. Gerraty 	/*
16513bebe729SSimon J. Gerraty 	 * Now we've got the important local variables set, expand any sources
16523bebe729SSimon J. Gerraty 	 * that still contain variables or wildcards in their names.
16533bebe729SSimon J. Gerraty 	 */
165406b9b3e0SSimon J. Gerraty 	ExpandAllChildren(gn);
16553bebe729SSimon J. Gerraty 
165606b9b3e0SSimon J. Gerraty 	if (memSuff != NULL)
165706b9b3e0SSimon J. Gerraty 		ExpandMember(gn, eoarch, mem, memSuff);
16583955d011SMarcel Moolenaar 
16593955d011SMarcel Moolenaar 	/*
166006b9b3e0SSimon J. Gerraty 	 * Replace the opening and closing parens now we've no need of the
166106b9b3e0SSimon J. Gerraty 	 * separate pieces.
16623955d011SMarcel Moolenaar 	 */
1663e2eeea75SSimon J. Gerraty 	*eoarch = '(';
1664e2eeea75SSimon J. Gerraty 	*eoname = ')';
16653955d011SMarcel Moolenaar 
16663955d011SMarcel Moolenaar 	/*
166706b9b3e0SSimon J. Gerraty 	 * Pretend gn appeared to the left of a dependency operator so the
166806b9b3e0SSimon J. Gerraty 	 * user needn't provide a transformation from the member to the
16693955d011SMarcel Moolenaar 	 * archive.
16703955d011SMarcel Moolenaar 	 */
1671e2eeea75SSimon J. Gerraty 	if (!GNode_IsTarget(gn))
16723955d011SMarcel Moolenaar 		gn->type |= OP_DEPENDS;
16733955d011SMarcel Moolenaar 
16743955d011SMarcel Moolenaar 	/*
16753955d011SMarcel Moolenaar 	 * Flag the member as such so we remember to look in the archive for
167606b9b3e0SSimon J. Gerraty 	 * its modification time. The OP_JOIN | OP_MADE is needed because
167706b9b3e0SSimon J. Gerraty 	 * this target should never get made.
16783955d011SMarcel Moolenaar 	 */
16793bebe729SSimon J. Gerraty 	mem->type |= OP_MEMBER | OP_JOIN | OP_MADE;
16803955d011SMarcel Moolenaar }
16813955d011SMarcel Moolenaar 
168206b9b3e0SSimon J. Gerraty /*
168306b9b3e0SSimon J. Gerraty  * If the node is a library, it is the arch module's job to find it
168406b9b3e0SSimon J. Gerraty  * and set the TARGET variable accordingly. We merely provide the
168506b9b3e0SSimon J. Gerraty  * search path, assuming all libraries end in ".a" (if the suffix
168606b9b3e0SSimon J. Gerraty  * hasn't been defined, there's nothing we can do for it, so we just
168706b9b3e0SSimon J. Gerraty  * set the TARGET variable to the node's name in order to give it a
168806b9b3e0SSimon J. Gerraty  * value).
168906b9b3e0SSimon J. Gerraty  */
1690956e45f6SSimon J. Gerraty static void
169106b9b3e0SSimon J. Gerraty FindDepsLib(GNode *gn)
1692956e45f6SSimon J. Gerraty {
169306b9b3e0SSimon J. Gerraty 	Suffix *suff = FindSuffixByName(LIBSUFF);
169406b9b3e0SSimon J. Gerraty 	if (suff != NULL) {
169506b9b3e0SSimon J. Gerraty 		Suffix_Reassign(&gn->suffix, suff);
169606b9b3e0SSimon J. Gerraty 		Arch_FindLib(gn, suff->searchPath);
169706b9b3e0SSimon J. Gerraty 	} else {
169806b9b3e0SSimon J. Gerraty 		Suffix_Unassign(&gn->suffix);
1699dba7b0efSSimon J. Gerraty 		Var_Set(gn, TARGET, gn->name);
170006b9b3e0SSimon J. Gerraty 	}
170106b9b3e0SSimon J. Gerraty 
170206b9b3e0SSimon J. Gerraty 	/*
170306b9b3e0SSimon J. Gerraty 	 * Because a library (-lfoo) target doesn't follow the standard
170406b9b3e0SSimon J. Gerraty 	 * filesystem conventions, we don't set the regular variables for
170506b9b3e0SSimon J. Gerraty 	 * the thing. .PREFIX is simply made empty.
170606b9b3e0SSimon J. Gerraty 	 */
1707dba7b0efSSimon J. Gerraty 	Var_Set(gn, PREFIX, "");
170806b9b3e0SSimon J. Gerraty }
170906b9b3e0SSimon J. Gerraty 
171006b9b3e0SSimon J. Gerraty static void
171106b9b3e0SSimon J. Gerraty FindDepsRegularKnown(const char *name, size_t nameLen, GNode *gn,
171206b9b3e0SSimon J. Gerraty 		     CandidateList *srcs, CandidateList *targs)
171306b9b3e0SSimon J. Gerraty {
171406b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
171506b9b3e0SSimon J. Gerraty 	Candidate *targ;
1716956e45f6SSimon J. Gerraty 	char *pref;
1717956e45f6SSimon J. Gerraty 
171806b9b3e0SSimon J. Gerraty 	for (ln = sufflist.first; ln != NULL; ln = ln->next) {
171906b9b3e0SSimon J. Gerraty 		Suffix *suff = ln->datum;
172006b9b3e0SSimon J. Gerraty 		if (!Suffix_IsSuffix(suff, nameLen, name + nameLen))
1721956e45f6SSimon J. Gerraty 			continue;
1722956e45f6SSimon J. Gerraty 
1723956e45f6SSimon J. Gerraty 		pref = bmake_strldup(name, (size_t)(nameLen - suff->nameLen));
172406b9b3e0SSimon J. Gerraty 		targ = Candidate_New(bmake_strdup(gn->name), pref, suff, NULL,
172506b9b3e0SSimon J. Gerraty 		    gn);
1726956e45f6SSimon J. Gerraty 
172706b9b3e0SSimon J. Gerraty 		CandidateList_AddCandidatesFor(srcs, targ);
1728956e45f6SSimon J. Gerraty 
172906b9b3e0SSimon J. Gerraty 		/* Record the target so we can nuke it. */
1730956e45f6SSimon J. Gerraty 		Lst_Append(targs, targ);
1731956e45f6SSimon J. Gerraty 	}
1732956e45f6SSimon J. Gerraty }
1733956e45f6SSimon J. Gerraty 
1734956e45f6SSimon J. Gerraty static void
173506b9b3e0SSimon J. Gerraty FindDepsRegularUnknown(GNode *gn, const char *sopref,
173606b9b3e0SSimon J. Gerraty 		       CandidateList *srcs, CandidateList *targs)
1737956e45f6SSimon J. Gerraty {
173806b9b3e0SSimon J. Gerraty 	Candidate *targ;
1739956e45f6SSimon J. Gerraty 
174006b9b3e0SSimon J. Gerraty 	if (!Lst_IsEmpty(targs) || nullSuff == NULL)
1741956e45f6SSimon J. Gerraty 		return;
1742956e45f6SSimon J. Gerraty 
174306b9b3e0SSimon J. Gerraty 	DEBUG1(SUFF, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name);
1744956e45f6SSimon J. Gerraty 
174506b9b3e0SSimon J. Gerraty 	targ = Candidate_New(bmake_strdup(gn->name), bmake_strdup(sopref),
174606b9b3e0SSimon J. Gerraty 	    nullSuff, NULL, gn);
1747956e45f6SSimon J. Gerraty 
1748956e45f6SSimon J. Gerraty 	/*
1749956e45f6SSimon J. Gerraty 	 * Only use the default suffix rules if we don't have commands
175006b9b3e0SSimon J. Gerraty 	 * defined for this gnode; traditional make programs used to not
175106b9b3e0SSimon J. Gerraty 	 * define suffix rules if the gnode had children but we don't do
175206b9b3e0SSimon J. Gerraty 	 * this anymore.
1753956e45f6SSimon J. Gerraty 	 */
175406b9b3e0SSimon J. Gerraty 	if (Lst_IsEmpty(&gn->commands))
175506b9b3e0SSimon J. Gerraty 		CandidateList_AddCandidatesFor(srcs, targ);
1756956e45f6SSimon J. Gerraty 	else {
175706b9b3e0SSimon J. Gerraty 		DEBUG0(SUFF, "not ");
1758956e45f6SSimon J. Gerraty 	}
1759956e45f6SSimon J. Gerraty 
176006b9b3e0SSimon J. Gerraty 	DEBUG0(SUFF, "adding suffix rules\n");
1761956e45f6SSimon J. Gerraty 
1762956e45f6SSimon J. Gerraty 	Lst_Append(targs, targ);
1763956e45f6SSimon J. Gerraty }
1764956e45f6SSimon J. Gerraty 
1765956e45f6SSimon J. Gerraty /*
176606b9b3e0SSimon J. Gerraty  * Deal with finding the thing on the default search path. We always do
176706b9b3e0SSimon J. Gerraty  * that, not only if the node is only a source (not on the lhs of a
176806b9b3e0SSimon J. Gerraty  * dependency operator or [XXX] it has neither children or commands) as
176906b9b3e0SSimon J. Gerraty  * the old pmake did.
1770956e45f6SSimon J. Gerraty  */
1771956e45f6SSimon J. Gerraty static void
177206b9b3e0SSimon J. Gerraty FindDepsRegularPath(GNode *gn, Candidate *targ)
1773956e45f6SSimon J. Gerraty {
1774956e45f6SSimon J. Gerraty 	if (gn->type & (OP_PHONY | OP_NOPATH))
1775956e45f6SSimon J. Gerraty 		return;
1776956e45f6SSimon J. Gerraty 
1777956e45f6SSimon J. Gerraty 	free(gn->path);
1778956e45f6SSimon J. Gerraty 	gn->path = Dir_FindFile(gn->name,
1779*9f45a3c8SSimon J. Gerraty 	    targ == NULL ? &dirSearchPath : targ->suff->searchPath);
1780956e45f6SSimon J. Gerraty 	if (gn->path == NULL)
1781956e45f6SSimon J. Gerraty 		return;
1782956e45f6SSimon J. Gerraty 
1783dba7b0efSSimon J. Gerraty 	Var_Set(gn, TARGET, gn->path);
1784956e45f6SSimon J. Gerraty 
1785956e45f6SSimon J. Gerraty 	if (targ != NULL) {
1786956e45f6SSimon J. Gerraty 		/*
1787956e45f6SSimon J. Gerraty 		 * Suffix known for the thing -- trim the suffix off
1788956e45f6SSimon J. Gerraty 		 * the path to form the proper .PREFIX variable.
1789956e45f6SSimon J. Gerraty 		 */
1790956e45f6SSimon J. Gerraty 		size_t savep = strlen(gn->path) - targ->suff->nameLen;
1791956e45f6SSimon J. Gerraty 		char savec;
1792956e45f6SSimon J. Gerraty 
179306b9b3e0SSimon J. Gerraty 		Suffix_Reassign(&gn->suffix, targ->suff);
1794956e45f6SSimon J. Gerraty 
1795956e45f6SSimon J. Gerraty 		savec = gn->path[savep];
1796956e45f6SSimon J. Gerraty 		gn->path[savep] = '\0';
1797956e45f6SSimon J. Gerraty 
1798dba7b0efSSimon J. Gerraty 		Var_Set(gn, PREFIX, str_basename(gn->path));
1799956e45f6SSimon J. Gerraty 
1800956e45f6SSimon J. Gerraty 		gn->path[savep] = savec;
1801956e45f6SSimon J. Gerraty 	} else {
180206b9b3e0SSimon J. Gerraty 		/*
180306b9b3e0SSimon J. Gerraty 		 * The .PREFIX gets the full path if the target has no
180406b9b3e0SSimon J. Gerraty 		 * known suffix.
180506b9b3e0SSimon J. Gerraty 		 */
180606b9b3e0SSimon J. Gerraty 		Suffix_Unassign(&gn->suffix);
1807dba7b0efSSimon J. Gerraty 		Var_Set(gn, PREFIX, str_basename(gn->path));
1808956e45f6SSimon J. Gerraty 	}
1809956e45f6SSimon J. Gerraty }
1810956e45f6SSimon J. Gerraty 
181106b9b3e0SSimon J. Gerraty /*
181206b9b3e0SSimon J. Gerraty  * Locate implicit dependencies for regular targets.
18133955d011SMarcel Moolenaar  *
18143955d011SMarcel Moolenaar  * Input:
18153955d011SMarcel Moolenaar  *	gn		Node for which to find sources
18163955d011SMarcel Moolenaar  *
18173955d011SMarcel Moolenaar  * Side Effects:
18182c3632d1SSimon J. Gerraty  *	Same as Suff_FindDeps
18193955d011SMarcel Moolenaar  */
18203955d011SMarcel Moolenaar static void
182106b9b3e0SSimon J. Gerraty FindDepsRegular(GNode *gn, CandidateSearcher *cs)
18223955d011SMarcel Moolenaar {
182306b9b3e0SSimon J. Gerraty 	/* List of sources at which to look */
182406b9b3e0SSimon J. Gerraty 	CandidateList srcs = LST_INIT;
182506b9b3e0SSimon J. Gerraty 	/*
182606b9b3e0SSimon J. Gerraty 	 * List of targets to which things can be transformed.
182706b9b3e0SSimon J. Gerraty 	 * They all have the same file, but different suff and prefix fields.
182806b9b3e0SSimon J. Gerraty 	 */
182906b9b3e0SSimon J. Gerraty 	CandidateList targs = LST_INIT;
183006b9b3e0SSimon J. Gerraty 	Candidate *bottom;	/* Start of found transformation path */
183106b9b3e0SSimon J. Gerraty 	Candidate *src;
183206b9b3e0SSimon J. Gerraty 	Candidate *targ;
18333955d011SMarcel Moolenaar 
1834956e45f6SSimon J. Gerraty 	const char *name = gn->name;
1835956e45f6SSimon J. Gerraty 	size_t nameLen = strlen(name);
18363955d011SMarcel Moolenaar 
183706b9b3e0SSimon J. Gerraty #ifdef DEBUG_SRC
183806b9b3e0SSimon J. Gerraty 	DEBUG1(SUFF, "FindDepsRegular \"%s\"\n", gn->name);
183906b9b3e0SSimon J. Gerraty #endif
18403955d011SMarcel Moolenaar 
18413955d011SMarcel Moolenaar 	/*
184206b9b3e0SSimon J. Gerraty 	 * We're caught in a catch-22 here. On the one hand, we want to use
184306b9b3e0SSimon J. Gerraty 	 * any transformation implied by the target's sources, but we can't
184406b9b3e0SSimon J. Gerraty 	 * examine the sources until we've expanded any variables/wildcards
184506b9b3e0SSimon J. Gerraty 	 * they may hold, and we can't do that until we've set up the
184606b9b3e0SSimon J. Gerraty 	 * target's local variables and we can't do that until we know what
184706b9b3e0SSimon J. Gerraty 	 * the proper suffix for the target is (in case there are two
184806b9b3e0SSimon J. Gerraty 	 * suffixes one of which is a suffix of the other) and we can't know
184906b9b3e0SSimon J. Gerraty 	 * that until we've found its implied source, which we may not want
185006b9b3e0SSimon J. Gerraty 	 * to use if there's an existing source that implies a different
185106b9b3e0SSimon J. Gerraty 	 * transformation.
18523955d011SMarcel Moolenaar 	 *
18533955d011SMarcel Moolenaar 	 * In an attempt to get around this, which may not work all the time,
185406b9b3e0SSimon J. Gerraty 	 * but should work most of the time, we look for implied sources
185506b9b3e0SSimon J. Gerraty 	 * first, checking transformations to all possible suffixes of the
185606b9b3e0SSimon J. Gerraty 	 * target, use what we find to set the target's local variables,
185706b9b3e0SSimon J. Gerraty 	 * expand the children, then look for any overriding transformations
185806b9b3e0SSimon J. Gerraty 	 * they imply. Should we find one, we discard the one we found before.
18593955d011SMarcel Moolenaar 	 */
18604fc82fe4SSimon J. Gerraty 	bottom = NULL;
18614fc82fe4SSimon J. Gerraty 	targ = NULL;
18624fc82fe4SSimon J. Gerraty 
18634fc82fe4SSimon J. Gerraty 	if (!(gn->type & OP_PHONY)) {
18643955d011SMarcel Moolenaar 
186506b9b3e0SSimon J. Gerraty 		FindDepsRegularKnown(name, nameLen, gn, &srcs, &targs);
18663955d011SMarcel Moolenaar 
1867956e45f6SSimon J. Gerraty 		/* Handle target of unknown suffix... */
186806b9b3e0SSimon J. Gerraty 		FindDepsRegularUnknown(gn, name, &srcs, &targs);
18693955d011SMarcel Moolenaar 
18703955d011SMarcel Moolenaar 		/*
187152d86256SSimon J. Gerraty 		 * Using the list of possible sources built up from the target
187206b9b3e0SSimon J. Gerraty 		 * suffix(es), try and find an existing file/target that
187306b9b3e0SSimon J. Gerraty 		 * matches.
18743955d011SMarcel Moolenaar 		 */
187506b9b3e0SSimon J. Gerraty 		bottom = FindThem(&srcs, cs);
18763955d011SMarcel Moolenaar 
18773955d011SMarcel Moolenaar 		if (bottom == NULL) {
18783955d011SMarcel Moolenaar 			/*
187906b9b3e0SSimon J. Gerraty 			 * No known transformations -- use the first suffix
188006b9b3e0SSimon J. Gerraty 			 * found for setting the local variables.
18813955d011SMarcel Moolenaar 			 */
188206b9b3e0SSimon J. Gerraty 			if (targs.first != NULL)
188306b9b3e0SSimon J. Gerraty 				targ = targs.first->datum;
1884e2eeea75SSimon J. Gerraty 			else
18853955d011SMarcel Moolenaar 				targ = NULL;
18863955d011SMarcel Moolenaar 		} else {
18873955d011SMarcel Moolenaar 			/*
188806b9b3e0SSimon J. Gerraty 			 * Work up the transformation path to find the suffix
188906b9b3e0SSimon J. Gerraty 			 * of the target to which the transformation was made.
18903955d011SMarcel Moolenaar 			 */
189106b9b3e0SSimon J. Gerraty 			for (targ = bottom;
189206b9b3e0SSimon J. Gerraty 			     targ->parent != NULL; targ = targ->parent)
18933955d011SMarcel Moolenaar 				continue;
18943955d011SMarcel Moolenaar 		}
18954fc82fe4SSimon J. Gerraty 	}
18963955d011SMarcel Moolenaar 
1897dba7b0efSSimon J. Gerraty 	Var_Set(gn, TARGET, GNode_Path(gn));
1898dba7b0efSSimon J. Gerraty 	Var_Set(gn, PREFIX, targ != NULL ? targ->prefix : gn->name);
18993955d011SMarcel Moolenaar 
19003955d011SMarcel Moolenaar 	/*
19013955d011SMarcel Moolenaar 	 * Now we've got the important local variables set, expand any sources
19023955d011SMarcel Moolenaar 	 * that still contain variables or wildcards in their names.
19033955d011SMarcel Moolenaar 	 */
1904956e45f6SSimon J. Gerraty 	{
190506b9b3e0SSimon J. Gerraty 		GNodeListNode *ln, *nln;
190606b9b3e0SSimon J. Gerraty 		for (ln = gn->children.first; ln != NULL; ln = nln) {
1907956e45f6SSimon J. Gerraty 			nln = ln->next;
190806b9b3e0SSimon J. Gerraty 			ExpandChildren(ln, gn);
19093955d011SMarcel Moolenaar 		}
1910956e45f6SSimon J. Gerraty 	}
19113955d011SMarcel Moolenaar 
19123955d011SMarcel Moolenaar 	if (targ == NULL) {
191306b9b3e0SSimon J. Gerraty 		DEBUG1(SUFF, "\tNo valid suffix on %s\n", gn->name);
19143955d011SMarcel Moolenaar 
19153955d011SMarcel Moolenaar 	sfnd_abort:
191606b9b3e0SSimon J. Gerraty 		FindDepsRegularPath(gn, targ);
19173955d011SMarcel Moolenaar 		goto sfnd_return;
19183955d011SMarcel Moolenaar 	}
19193955d011SMarcel Moolenaar 
19203955d011SMarcel Moolenaar 	/*
19213955d011SMarcel Moolenaar 	 * If the suffix indicates that the target is a library, mark that in
19223955d011SMarcel Moolenaar 	 * the node's type field.
19233955d011SMarcel Moolenaar 	 */
192412904384SSimon J. Gerraty 	if (targ->suff->library)
19253955d011SMarcel Moolenaar 		gn->type |= OP_LIB;
19263955d011SMarcel Moolenaar 
19273955d011SMarcel Moolenaar 	/*
19283955d011SMarcel Moolenaar 	 * Check for overriding transformation rule implied by sources
19293955d011SMarcel Moolenaar 	 */
193006b9b3e0SSimon J. Gerraty 	if (!Lst_IsEmpty(&gn->children)) {
193106b9b3e0SSimon J. Gerraty 		src = FindCmds(targ, cs);
19323955d011SMarcel Moolenaar 
19333955d011SMarcel Moolenaar 		if (src != NULL) {
19343955d011SMarcel Moolenaar 			/*
193506b9b3e0SSimon J. Gerraty 			 * Free up all the candidates in the transformation
193606b9b3e0SSimon J. Gerraty 			 * path, up to but not including the parent node.
19373955d011SMarcel Moolenaar 			 */
1938e2eeea75SSimon J. Gerraty 			while (bottom != NULL && bottom->parent != NULL) {
193906b9b3e0SSimon J. Gerraty 				CandidateSearcher_AddIfNew(cs, bottom);
19403955d011SMarcel Moolenaar 				bottom = bottom->parent;
19413955d011SMarcel Moolenaar 			}
19423955d011SMarcel Moolenaar 			bottom = src;
19433955d011SMarcel Moolenaar 		}
19443955d011SMarcel Moolenaar 	}
19453955d011SMarcel Moolenaar 
19463955d011SMarcel Moolenaar 	if (bottom == NULL) {
194706b9b3e0SSimon J. Gerraty 		/* No idea from where it can come -- return now. */
19483955d011SMarcel Moolenaar 		goto sfnd_abort;
19493955d011SMarcel Moolenaar 	}
19503955d011SMarcel Moolenaar 
19513955d011SMarcel Moolenaar 	/*
195206b9b3e0SSimon J. Gerraty 	 * We now have a list of candidates headed by 'bottom' and linked via
19533955d011SMarcel Moolenaar 	 * their 'parent' pointers. What we do next is create links between
19543955d011SMarcel Moolenaar 	 * source and target nodes (which may or may not have been created)
195506b9b3e0SSimon J. Gerraty 	 * and set the necessary local variables in each target.
195606b9b3e0SSimon J. Gerraty 	 *
195706b9b3e0SSimon J. Gerraty 	 * The commands for each target are set from the commands of the
19583955d011SMarcel Moolenaar 	 * transformation rule used to get from the src suffix to the targ
19593955d011SMarcel Moolenaar 	 * suffix. Note that this causes the commands list of the original
196006b9b3e0SSimon J. Gerraty 	 * node, gn, to be replaced with the commands of the final
196106b9b3e0SSimon J. Gerraty 	 * transformation rule.
19623955d011SMarcel Moolenaar 	 */
1963e2eeea75SSimon J. Gerraty 	if (bottom->node == NULL)
1964956e45f6SSimon J. Gerraty 		bottom->node = Targ_GetNode(bottom->file);
19653955d011SMarcel Moolenaar 
19663955d011SMarcel Moolenaar 	for (src = bottom; src->parent != NULL; src = src->parent) {
19673955d011SMarcel Moolenaar 		targ = src->parent;
19683955d011SMarcel Moolenaar 
196906b9b3e0SSimon J. Gerraty 		Suffix_Reassign(&src->node->suffix, src->suff);
19703955d011SMarcel Moolenaar 
1971e2eeea75SSimon J. Gerraty 		if (targ->node == NULL)
1972956e45f6SSimon J. Gerraty 			targ->node = Targ_GetNode(targ->file);
19733955d011SMarcel Moolenaar 
197406b9b3e0SSimon J. Gerraty 		ApplyTransform(targ->node, src->node,
19753955d011SMarcel Moolenaar 		    targ->suff, src->suff);
19763955d011SMarcel Moolenaar 
19773955d011SMarcel Moolenaar 		if (targ->node != gn) {
19783955d011SMarcel Moolenaar 			/*
197906b9b3e0SSimon J. Gerraty 			 * Finish off the dependency-search process for any
198006b9b3e0SSimon J. Gerraty 			 * nodes between bottom and gn (no point in questing
198106b9b3e0SSimon J. Gerraty 			 * around the filesystem for their implicit source
198206b9b3e0SSimon J. Gerraty 			 * when it's already known). Note that the node
198306b9b3e0SSimon J. Gerraty 			 * can't have any sources that need expanding, since
198406b9b3e0SSimon J. Gerraty 			 * SuffFindThem will stop on an existing node, so all
198506b9b3e0SSimon J. Gerraty 			 * we need to do is set the standard variables.
19863955d011SMarcel Moolenaar 			 */
19873955d011SMarcel Moolenaar 			targ->node->type |= OP_DEPS_FOUND;
1988dba7b0efSSimon J. Gerraty 			Var_Set(targ->node, PREFIX, targ->prefix);
1989dba7b0efSSimon J. Gerraty 			Var_Set(targ->node, TARGET, targ->node->name);
19903955d011SMarcel Moolenaar 		}
19913955d011SMarcel Moolenaar 	}
19923955d011SMarcel Moolenaar 
199306b9b3e0SSimon J. Gerraty 	Suffix_Reassign(&gn->suffix, src->suff);
19943955d011SMarcel Moolenaar 
19953955d011SMarcel Moolenaar 	/*
199606b9b3e0SSimon J. Gerraty 	 * Nuke the transformation path and the candidates left over in the
19973955d011SMarcel Moolenaar 	 * two lists.
19983955d011SMarcel Moolenaar 	 */
19993955d011SMarcel Moolenaar sfnd_return:
200006b9b3e0SSimon J. Gerraty 	if (bottom != NULL)
200106b9b3e0SSimon J. Gerraty 		CandidateSearcher_AddIfNew(cs, bottom);
20023955d011SMarcel Moolenaar 
200306b9b3e0SSimon J. Gerraty 	while (RemoveCandidate(&srcs) || RemoveCandidate(&targs))
20043955d011SMarcel Moolenaar 		continue;
20053955d011SMarcel Moolenaar 
200606b9b3e0SSimon J. Gerraty 	CandidateSearcher_MoveAll(cs, &srcs);
200706b9b3e0SSimon J. Gerraty 	CandidateSearcher_MoveAll(cs, &targs);
200806b9b3e0SSimon J. Gerraty }
200906b9b3e0SSimon J. Gerraty 
201006b9b3e0SSimon J. Gerraty static void
201106b9b3e0SSimon J. Gerraty CandidateSearcher_CleanUp(CandidateSearcher *cs)
201206b9b3e0SSimon J. Gerraty {
201306b9b3e0SSimon J. Gerraty 	while (RemoveCandidate(&cs->list))
201406b9b3e0SSimon J. Gerraty 		continue;
201506b9b3e0SSimon J. Gerraty 	assert(Lst_IsEmpty(&cs->list));
20163955d011SMarcel Moolenaar }
20173955d011SMarcel Moolenaar 
20183955d011SMarcel Moolenaar 
201906b9b3e0SSimon J. Gerraty /*
202006b9b3e0SSimon J. Gerraty  * Find implicit sources for the target.
20213955d011SMarcel Moolenaar  *
202206b9b3e0SSimon J. Gerraty  * Nodes are added to the graph as children of the passed-in node. The nodes
202306b9b3e0SSimon J. Gerraty  * are marked to have their IMPSRC variable filled in. The PREFIX variable
202406b9b3e0SSimon J. Gerraty  * is set for the given node and all its implied children.
20253955d011SMarcel Moolenaar  *
20262c3632d1SSimon J. Gerraty  * The path found by this target is the shortest path in the transformation
202706b9b3e0SSimon J. Gerraty  * graph, which may pass through nonexistent targets, to an existing target.
20282c3632d1SSimon J. Gerraty  * The search continues on all paths from the root suffix until a file is
20292c3632d1SSimon J. Gerraty  * found. I.e. if there's a path .o -> .c -> .l -> .l,v from the root and the
20302c3632d1SSimon J. Gerraty  * .l,v file exists but the .c and .l files don't, the search will branch out
20312c3632d1SSimon J. Gerraty  * in all directions from .o and again from all the nodes on the next level
20322c3632d1SSimon J. Gerraty  * until the .l,v node is encountered.
20333955d011SMarcel Moolenaar  */
20343955d011SMarcel Moolenaar void
20353955d011SMarcel Moolenaar Suff_FindDeps(GNode *gn)
20363955d011SMarcel Moolenaar {
203706b9b3e0SSimon J. Gerraty 	CandidateSearcher cs;
20383955d011SMarcel Moolenaar 
203906b9b3e0SSimon J. Gerraty 	CandidateSearcher_Init(&cs);
204006b9b3e0SSimon J. Gerraty 
204106b9b3e0SSimon J. Gerraty 	FindDeps(gn, &cs);
204206b9b3e0SSimon J. Gerraty 
204306b9b3e0SSimon J. Gerraty 	CandidateSearcher_CleanUp(&cs);
204406b9b3e0SSimon J. Gerraty 	CandidateSearcher_Done(&cs);
20453955d011SMarcel Moolenaar }
20463955d011SMarcel Moolenaar 
20473955d011SMarcel Moolenaar static void
204806b9b3e0SSimon J. Gerraty FindDeps(GNode *gn, CandidateSearcher *cs)
20493955d011SMarcel Moolenaar {
20502c3632d1SSimon J. Gerraty 	if (gn->type & OP_DEPS_FOUND)
20513955d011SMarcel Moolenaar 		return;
20523955d011SMarcel Moolenaar 	gn->type |= OP_DEPS_FOUND;
20532c3632d1SSimon J. Gerraty 
205406b9b3e0SSimon J. Gerraty 	/* Make sure we have these set, may get revised below. */
2055dba7b0efSSimon J. Gerraty 	Var_Set(gn, TARGET, GNode_Path(gn));
2056dba7b0efSSimon J. Gerraty 	Var_Set(gn, PREFIX, gn->name);
20574fc82fe4SSimon J. Gerraty 
205806b9b3e0SSimon J. Gerraty 	DEBUG1(SUFF, "SuffFindDeps \"%s\"\n", gn->name);
20593955d011SMarcel Moolenaar 
206006b9b3e0SSimon J. Gerraty 	if (gn->type & OP_ARCHV)
206106b9b3e0SSimon J. Gerraty 		FindDepsArchive(gn, cs);
206206b9b3e0SSimon J. Gerraty 	else if (gn->type & OP_LIB)
206306b9b3e0SSimon J. Gerraty 		FindDepsLib(gn);
206406b9b3e0SSimon J. Gerraty 	else
206506b9b3e0SSimon J. Gerraty 		FindDepsRegular(gn, cs);
20663955d011SMarcel Moolenaar }
20673955d011SMarcel Moolenaar 
206806b9b3e0SSimon J. Gerraty /*
206906b9b3e0SSimon J. Gerraty  * Define which suffix is the null suffix.
20702c3632d1SSimon J. Gerraty  *
20712c3632d1SSimon J. Gerraty  * Need to handle the changing of the null suffix gracefully so the old
20722c3632d1SSimon J. Gerraty  * transformation rules don't just go away.
20733955d011SMarcel Moolenaar  *
20743955d011SMarcel Moolenaar  * Input:
20753955d011SMarcel Moolenaar  *	name		Name of null suffix
20763955d011SMarcel Moolenaar  */
20773955d011SMarcel Moolenaar void
2078956e45f6SSimon J. Gerraty Suff_SetNull(const char *name)
20793955d011SMarcel Moolenaar {
208006b9b3e0SSimon J. Gerraty 	Suffix *suff = FindSuffixByName(name);
2081e2eeea75SSimon J. Gerraty 	if (suff == NULL) {
208206b9b3e0SSimon J. Gerraty 		Parse_Error(PARSE_WARNING,
208312904384SSimon J. Gerraty 		    "Desired null suffix %s not defined",
2084956e45f6SSimon J. Gerraty 		    name);
2085956e45f6SSimon J. Gerraty 		return;
2086956e45f6SSimon J. Gerraty 	}
20873955d011SMarcel Moolenaar 
208806b9b3e0SSimon J. Gerraty 	if (nullSuff != NULL)
208912904384SSimon J. Gerraty 		nullSuff->isNull = false;
209012904384SSimon J. Gerraty 	suff->isNull = true;
209106b9b3e0SSimon J. Gerraty 	/* XXX: Here's where the transformation mangling would take place. */
209206b9b3e0SSimon J. Gerraty 	nullSuff = suff;
20933955d011SMarcel Moolenaar }
20943955d011SMarcel Moolenaar 
20952c3632d1SSimon J. Gerraty /* Initialize the suffixes module. */
20963955d011SMarcel Moolenaar void
20973955d011SMarcel Moolenaar Suff_Init(void)
20983955d011SMarcel Moolenaar {
20993955d011SMarcel Moolenaar 	/*
210006b9b3e0SSimon J. Gerraty 	 * Create null suffix for single-suffix rules (POSIX). The thing
210106b9b3e0SSimon J. Gerraty 	 * doesn't actually go on the suffix list or everyone will think
210206b9b3e0SSimon J. Gerraty 	 * that's its suffix.
21033955d011SMarcel Moolenaar 	 */
21046e050540SSimon J. Gerraty 	Suff_ClearSuffixes();
21053955d011SMarcel Moolenaar }
21063955d011SMarcel Moolenaar 
21073955d011SMarcel Moolenaar 
21082c3632d1SSimon J. Gerraty /* Clean up the suffixes module. */
21093955d011SMarcel Moolenaar void
21103955d011SMarcel Moolenaar Suff_End(void)
21113955d011SMarcel Moolenaar {
21123955d011SMarcel Moolenaar #ifdef CLEANUP
211306b9b3e0SSimon J. Gerraty 	Lst_DoneCall(&sufflist, SuffFree);
211406b9b3e0SSimon J. Gerraty 	Lst_DoneCall(&suffClean, SuffFree);
211506b9b3e0SSimon J. Gerraty 	if (nullSuff != NULL)
211606b9b3e0SSimon J. Gerraty 		SuffFree(nullSuff);
211706b9b3e0SSimon J. Gerraty 	Lst_Done(&transforms);
21183955d011SMarcel Moolenaar #endif
21193955d011SMarcel Moolenaar }
21203955d011SMarcel Moolenaar 
21213955d011SMarcel Moolenaar 
2122956e45f6SSimon J. Gerraty static void
212312904384SSimon J. Gerraty PrintSuffNames(const char *prefix, const SuffixList *suffs)
21243955d011SMarcel Moolenaar {
212506b9b3e0SSimon J. Gerraty 	SuffixListNode *ln;
212695e3ed2cSSimon J. Gerraty 
2127956e45f6SSimon J. Gerraty 	debug_printf("#\t%s: ", prefix);
2128956e45f6SSimon J. Gerraty 	for (ln = suffs->first; ln != NULL; ln = ln->next) {
212912904384SSimon J. Gerraty 		const Suffix *suff = ln->datum;
2130956e45f6SSimon J. Gerraty 		debug_printf("%s ", suff->name);
2131956e45f6SSimon J. Gerraty 	}
2132956e45f6SSimon J. Gerraty 	debug_printf("\n");
21333955d011SMarcel Moolenaar }
21343955d011SMarcel Moolenaar 
2135956e45f6SSimon J. Gerraty static void
213612904384SSimon J. Gerraty Suffix_Print(const Suffix *suff)
21373955d011SMarcel Moolenaar {
213812904384SSimon J. Gerraty 	Buffer buf;
213912904384SSimon J. Gerraty 
214012904384SSimon J. Gerraty 	Buf_InitSize(&buf, 16);
214112904384SSimon J. Gerraty 	Buf_AddFlag(&buf, suff->include, "SUFF_INCLUDE");
214212904384SSimon J. Gerraty 	Buf_AddFlag(&buf, suff->library, "SUFF_LIBRARY");
214312904384SSimon J. Gerraty 	Buf_AddFlag(&buf, suff->isNull, "SUFF_NULL");
214412904384SSimon J. Gerraty 
2145e2eeea75SSimon J. Gerraty 	debug_printf("# \"%s\" (num %d, ref %d)",
2146e2eeea75SSimon J. Gerraty 	    suff->name, suff->sNum, suff->refCount);
214712904384SSimon J. Gerraty 	if (buf.len > 0)
214812904384SSimon J. Gerraty 		debug_printf(" (%s)", buf.data);
2149956e45f6SSimon J. Gerraty 	debug_printf("\n");
2150956e45f6SSimon J. Gerraty 
215112904384SSimon J. Gerraty 	Buf_Done(&buf);
215212904384SSimon J. Gerraty 
215306b9b3e0SSimon J. Gerraty 	PrintSuffNames("To", &suff->parents);
215406b9b3e0SSimon J. Gerraty 	PrintSuffNames("From", &suff->children);
2155956e45f6SSimon J. Gerraty 
2156956e45f6SSimon J. Gerraty 	debug_printf("#\tSearch Path: ");
215706b9b3e0SSimon J. Gerraty 	SearchPath_Print(suff->searchPath);
2158956e45f6SSimon J. Gerraty 	debug_printf("\n");
21593955d011SMarcel Moolenaar }
21603955d011SMarcel Moolenaar 
2161956e45f6SSimon J. Gerraty static void
2162956e45f6SSimon J. Gerraty PrintTransformation(GNode *t)
21633955d011SMarcel Moolenaar {
2164956e45f6SSimon J. Gerraty 	debug_printf("%-16s:", t->name);
21653955d011SMarcel Moolenaar 	Targ_PrintType(t->type);
2166956e45f6SSimon J. Gerraty 	debug_printf("\n");
2167956e45f6SSimon J. Gerraty 	Targ_PrintCmds(t);
2168956e45f6SSimon J. Gerraty 	debug_printf("\n");
21693955d011SMarcel Moolenaar }
21703955d011SMarcel Moolenaar 
21713955d011SMarcel Moolenaar void
21723955d011SMarcel Moolenaar Suff_PrintAll(void)
21733955d011SMarcel Moolenaar {
2174956e45f6SSimon J. Gerraty 	debug_printf("#*** Suffixes:\n");
2175956e45f6SSimon J. Gerraty 	{
217606b9b3e0SSimon J. Gerraty 		SuffixListNode *ln;
217706b9b3e0SSimon J. Gerraty 		for (ln = sufflist.first; ln != NULL; ln = ln->next)
217806b9b3e0SSimon J. Gerraty 			Suffix_Print(ln->datum);
2179956e45f6SSimon J. Gerraty 	}
21803955d011SMarcel Moolenaar 
2181956e45f6SSimon J. Gerraty 	debug_printf("#*** Transformations:\n");
2182956e45f6SSimon J. Gerraty 	{
2183956e45f6SSimon J. Gerraty 		GNodeListNode *ln;
218406b9b3e0SSimon J. Gerraty 		for (ln = transforms.first; ln != NULL; ln = ln->next)
2185956e45f6SSimon J. Gerraty 			PrintTransformation(ln->datum);
2186956e45f6SSimon J. Gerraty 	}
21873955d011SMarcel Moolenaar }
218812904384SSimon J. Gerraty 
2189*9f45a3c8SSimon J. Gerraty char *
219012904384SSimon J. Gerraty Suff_NamesStr(void)
219112904384SSimon J. Gerraty {
219212904384SSimon J. Gerraty 	Buffer buf;
219312904384SSimon J. Gerraty 	SuffixListNode *ln;
219412904384SSimon J. Gerraty 	Suffix *suff;
219512904384SSimon J. Gerraty 
219612904384SSimon J. Gerraty 	Buf_InitSize(&buf, 16);
219712904384SSimon J. Gerraty 	for (ln = sufflist.first; ln != NULL; ln = ln->next) {
219812904384SSimon J. Gerraty 		suff = ln->datum;
219912904384SSimon J. Gerraty 		if (ln != sufflist.first)
220012904384SSimon J. Gerraty 			Buf_AddByte(&buf, ' ');
220112904384SSimon J. Gerraty 		Buf_AddStr(&buf, suff->name);
220212904384SSimon J. Gerraty 	}
220312904384SSimon J. Gerraty 	return Buf_DoneData(&buf);
220412904384SSimon J. Gerraty }
2205