xref: /openbsd-src/usr.bin/make/targequiv.c (revision 7e30afce04b513f04457e7b94d25cad8950948ee)
1*7e30afceSmillert /*	$OpenBSD: targequiv.c,v 1.11 2024/06/18 02:11:04 millert Exp $ */
26c2dbbdaSespie /*
36c2dbbdaSespie  * Copyright (c) 2007-2008 Marc Espie.
46c2dbbdaSespie  *
56c2dbbdaSespie  * Extensive code changes for the OpenBSD project.
66c2dbbdaSespie  *
76c2dbbdaSespie  * Redistribution and use in source and binary forms, with or without
86c2dbbdaSespie  * modification, are permitted provided that the following conditions
96c2dbbdaSespie  * are met:
106c2dbbdaSespie  * 1. Redistributions of source code must retain the above copyright
116c2dbbdaSespie  *    notice, this list of conditions and the following disclaimer.
126c2dbbdaSespie  * 2. Redistributions in binary form must reproduce the above copyright
136c2dbbdaSespie  *    notice, this list of conditions and the following disclaimer in the
146c2dbbdaSespie  *    documentation and/or other materials provided with the distribution.
156c2dbbdaSespie  *
166c2dbbdaSespie  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
176c2dbbdaSespie  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186c2dbbdaSespie  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196c2dbbdaSespie  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
206c2dbbdaSespie  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216c2dbbdaSespie  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226c2dbbdaSespie  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236c2dbbdaSespie  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246c2dbbdaSespie  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256c2dbbdaSespie  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266c2dbbdaSespie  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276c2dbbdaSespie  */
286c2dbbdaSespie 
296c2dbbdaSespie /* Compute equivalence tables of targets, helpful for VPATH and parallel
306c2dbbdaSespie  * make.
316c2dbbdaSespie  */
326c2dbbdaSespie 
331f89b472Sespie #include <stddef.h>
341f89b472Sespie #include <stdint.h>
351f89b472Sespie #include <stdio.h>
361f89b472Sespie #include <stdlib.h>
376c2dbbdaSespie #include <string.h>
381f89b472Sespie #include <ohash.h>
39452f2662Sderaadt #include <limits.h>
406c2dbbdaSespie #include "defines.h"
416c2dbbdaSespie #include "memory.h"
426c2dbbdaSespie #include "gnode.h"
436c2dbbdaSespie #include "lst.h"
446c2dbbdaSespie #include "suff.h"
456c2dbbdaSespie #include "dir.h"
466c2dbbdaSespie #include "targ.h"
476c2dbbdaSespie #include "targequiv.h"
486c2dbbdaSespie 
496c2dbbdaSespie struct equiv_list {
506c2dbbdaSespie 	GNode *first, *last;
516c2dbbdaSespie 	char name[1];
526c2dbbdaSespie };
536c2dbbdaSespie 
546c2dbbdaSespie static struct ohash_info equiv_info = {
55e2ff9f51Sespie 	offsetof(struct equiv_list, name), NULL, hash_calloc, hash_free,
566c2dbbdaSespie 	element_alloc
576c2dbbdaSespie };
586c2dbbdaSespie 
596c2dbbdaSespie static void attach_node(GNode *, GNode *);
606c2dbbdaSespie static void build_equivalence(void);
616c2dbbdaSespie static void add_to_equiv_list(struct ohash *, GNode *);
626c2dbbdaSespie static char *names_match(GNode *, GNode *);
636c2dbbdaSespie static char *names_match_with_dir(const char *, const char *, char *,
646c2dbbdaSespie     char *,  const char *);
656c2dbbdaSespie static char *names_match_with_dirs(const char *, const char *, char *,
666c2dbbdaSespie     char *,  const char *, const char *);
676c2dbbdaSespie static char *relative_reduce(const char *, const char *);
686c2dbbdaSespie static char *relative_reduce2(const char *, const char *, const char *);
696c2dbbdaSespie static char *absolute_reduce(const char *);
706c2dbbdaSespie static size_t parse_reduce(size_t, const char *);
716c2dbbdaSespie static void find_siblings(GNode *);
726c2dbbdaSespie 
736c2dbbdaSespie /* These functions build `equivalence lists' of targets with the same
746c2dbbdaSespie  * basename, as circular lists. They use an intermediate ohash as scaffold,
756c2dbbdaSespie  * to insert same-basename targets in a simply linked list. Then they make
766c2dbbdaSespie  * those lists circular, to the exception of lone elements, since they can't
776c2dbbdaSespie  * alias to anything.
786c2dbbdaSespie  *
796c2dbbdaSespie  * This structure is used to simplify alias-lookup to a great extent: two
806c2dbbdaSespie  * nodes can only alias each other if they're part of the same equivalence
816c2dbbdaSespie  * set. Most nodes either don't belong to an alias set, or to a very simple
826c2dbbdaSespie  * alias set, thus removing most possibilities.
836c2dbbdaSespie  */
846c2dbbdaSespie static void
add_to_equiv_list(struct ohash * equiv,GNode * gn)856c2dbbdaSespie add_to_equiv_list(struct ohash *equiv, GNode *gn)
866c2dbbdaSespie {
876c2dbbdaSespie 	const char *end = NULL;
886c2dbbdaSespie 	struct equiv_list *e;
896c2dbbdaSespie 	unsigned int slot;
906c2dbbdaSespie 
916c2dbbdaSespie 	if (!should_have_file(gn))
926c2dbbdaSespie 		return;
936c2dbbdaSespie 
946c2dbbdaSespie 	gn->basename = strrchr(gn->name, '/');
956c2dbbdaSespie 	if (gn->basename == NULL)
966c2dbbdaSespie 		gn->basename = gn->name;
976c2dbbdaSespie 	else
986c2dbbdaSespie 		gn->basename++;
996c2dbbdaSespie 	slot = ohash_qlookupi(equiv, gn->basename, &end);
1006c2dbbdaSespie 	e = ohash_find(equiv, slot);
1016c2dbbdaSespie 	if (e == NULL) {
1026c2dbbdaSespie 		e = ohash_create_entry(&equiv_info, gn->basename, &end);
1036c2dbbdaSespie 		e->first = NULL;
1046c2dbbdaSespie 		e->last = gn;
1056c2dbbdaSespie 		ohash_insert(equiv, slot, e);
1066c2dbbdaSespie 	}
1076c2dbbdaSespie 	gn->next = e->first;
1086c2dbbdaSespie 	e->first = gn;
1096c2dbbdaSespie }
1106c2dbbdaSespie 
1116c2dbbdaSespie static void
build_equivalence(void)112*7e30afceSmillert build_equivalence(void)
1136c2dbbdaSespie {
1146c2dbbdaSespie 	unsigned int i;
1156c2dbbdaSespie 	GNode *gn;
1166c2dbbdaSespie 	struct equiv_list *e;
1176c2dbbdaSespie 	struct ohash equiv;
1186c2dbbdaSespie 	struct ohash *t = targets_hash();
1196c2dbbdaSespie 
1206c2dbbdaSespie 
1216c2dbbdaSespie 	ohash_init(&equiv, 10, &equiv_info);
1226c2dbbdaSespie 
1236c2dbbdaSespie 	for (gn = ohash_first(t, &i); gn != NULL; gn = ohash_next(t, &i))
1246c2dbbdaSespie 		add_to_equiv_list(&equiv, gn);
1256c2dbbdaSespie 
1266c2dbbdaSespie 	/* finish making the lists circular */
1276c2dbbdaSespie 	for (e = ohash_first(&equiv, &i); e != NULL;
1286c2dbbdaSespie 	    e = ohash_next(&equiv, &i)) {
1296c2dbbdaSespie 	    	if (e->last != e->first)
1306c2dbbdaSespie 			e->last->next = e->first;
1316c2dbbdaSespie 		free(e);
1326c2dbbdaSespie 	}
1336c2dbbdaSespie 	ohash_delete(&equiv);
1346c2dbbdaSespie }
1356c2dbbdaSespie 
1366c2dbbdaSespie static const char *curdir, *objdir;
1376c2dbbdaSespie static char *kobjdir;
1386c2dbbdaSespie static size_t objdir_len;
1396c2dbbdaSespie 
1406c2dbbdaSespie void
Targ_setdirs(const char * c,const char * o)1416c2dbbdaSespie Targ_setdirs(const char *c, const char *o)
1426c2dbbdaSespie {
1436c2dbbdaSespie 	curdir = c;
1446c2dbbdaSespie 	objdir = o;
1456c2dbbdaSespie 
1466c2dbbdaSespie 	objdir_len = strlen(o);
1476c2dbbdaSespie 	kobjdir = emalloc(objdir_len+2);
1486c2dbbdaSespie 	memcpy(kobjdir, o, objdir_len);
1496c2dbbdaSespie 	kobjdir[objdir_len++] = '/';
1506c2dbbdaSespie 	kobjdir[objdir_len] = 0;
1516c2dbbdaSespie }
1526c2dbbdaSespie 
1536c2dbbdaSespie 
1546c2dbbdaSespie void
kludge_look_harder_for_target(GNode * gn)1556c2dbbdaSespie kludge_look_harder_for_target(GNode *gn)
1566c2dbbdaSespie {
1576c2dbbdaSespie 	GNode *extra, *cgn;
1586c2dbbdaSespie 	LstNode ln;
1596c2dbbdaSespie 
1606c2dbbdaSespie 	if (strncmp(gn->name, kobjdir, objdir_len) == 0) {
1616c2dbbdaSespie 		extra = Targ_FindNode(gn->name + objdir_len, TARG_NOCREATE);
1626c2dbbdaSespie 		if (extra != NULL) {
1636c2dbbdaSespie 			if (Lst_IsEmpty(&gn->commands))
1646c2dbbdaSespie 				Lst_Concat(&gn->commands, &extra->commands);
1656c2dbbdaSespie 			for (ln = Lst_First(&extra->children); ln != NULL;
1666c2dbbdaSespie 			    ln = Lst_Adv(ln)) {
167568f6c05Sespie 				cgn = Lst_Datum(ln);
1686c2dbbdaSespie 
1696c2dbbdaSespie 				if (Lst_AddNew(&gn->children, cgn)) {
1706c2dbbdaSespie 					Lst_AtEnd(&cgn->parents, gn);
171194f6eccSespie 					gn->children_left++;
1726c2dbbdaSespie 				}
1736c2dbbdaSespie 			}
1746c2dbbdaSespie 		}
1756c2dbbdaSespie 	}
1766c2dbbdaSespie }
1776c2dbbdaSespie 
1786c2dbbdaSespie static void
attach_node(GNode * gn,GNode * extra)1796c2dbbdaSespie attach_node(GNode *gn, GNode *extra)
1806c2dbbdaSespie {
1816c2dbbdaSespie 	/* XXX normally extra->sibling == extra, but this is not
1826c2dbbdaSespie 	 * always the case yet, so we merge the two lists
1836c2dbbdaSespie 	 */
1846c2dbbdaSespie 	GNode *tmp;
1856c2dbbdaSespie 
1866c2dbbdaSespie 	tmp = gn->sibling;
1876c2dbbdaSespie 	gn->sibling = extra->sibling;
1886c2dbbdaSespie 	extra->sibling = tmp;
1896c2dbbdaSespie }
1906c2dbbdaSespie 
1916c2dbbdaSespie static char *buffer = NULL;
192452f2662Sderaadt static size_t bufsize = PATH_MAX;
1936c2dbbdaSespie 
1946c2dbbdaSespie static size_t
parse_reduce(size_t i,const char * src)1956c2dbbdaSespie parse_reduce(size_t i, const char *src)
1966c2dbbdaSespie {
1976c2dbbdaSespie 	while (src[0] != 0) {
1986c2dbbdaSespie 		while (src[0] == '/')
1996c2dbbdaSespie 			src++;
2006c2dbbdaSespie 		/* special cases */
2016c2dbbdaSespie 		if (src[0] == '.') {
2026c2dbbdaSespie 			if (src[1] == '/') {
2036c2dbbdaSespie 				src += 2;
2046c2dbbdaSespie 				continue;
2056c2dbbdaSespie 			}
2066c2dbbdaSespie 			if (src[1] == '.' && src[2] == '/') {
2076c2dbbdaSespie 				src += 3;
2086c2dbbdaSespie 				i--;
2096c2dbbdaSespie 				while (i > 0 && buffer[i-1] != '/')
2106c2dbbdaSespie 					i--;
2116c2dbbdaSespie 				if (i == 0)
2126c2dbbdaSespie 					i = 1;
2136c2dbbdaSespie 				continue;
2146c2dbbdaSespie 			}
2156c2dbbdaSespie 		}
2166c2dbbdaSespie 		while (src[0] != '/' && src[0] != 0) {
2176c2dbbdaSespie 			if (i > bufsize - 2) {
2186c2dbbdaSespie 				bufsize *= 2;
2196c2dbbdaSespie 				buffer = erealloc(buffer, bufsize);
2206c2dbbdaSespie 			}
2216c2dbbdaSespie 			buffer[i++] = *src++;
2226c2dbbdaSespie 		}
2234ef28080Sespie 		if (src[0] == '/')
2244ef28080Sespie 			buffer[i++] = *src++;
2256c2dbbdaSespie 	}
2264ef28080Sespie 	buffer[i++] = 0;
2276c2dbbdaSespie 	return i;
2286c2dbbdaSespie }
2296c2dbbdaSespie 
2306c2dbbdaSespie static char *
absolute_reduce(const char * src)2316c2dbbdaSespie absolute_reduce(const char *src)
2326c2dbbdaSespie {
2336c2dbbdaSespie 	size_t i = 0;
2346c2dbbdaSespie 
2356c2dbbdaSespie 	if (buffer == NULL)
2366c2dbbdaSespie 		buffer = emalloc(bufsize);
2376c2dbbdaSespie 
2386c2dbbdaSespie 	buffer[i++] = '/';
2396c2dbbdaSespie 	i = parse_reduce(i, src);
2406c2dbbdaSespie 	return estrdup(buffer);
2416c2dbbdaSespie }
2426c2dbbdaSespie 
2436c2dbbdaSespie static char *
relative_reduce(const char * dir,const char * src)2446c2dbbdaSespie relative_reduce(const char *dir, const char *src)
2456c2dbbdaSespie {
2466c2dbbdaSespie 	size_t i = 0;
2476c2dbbdaSespie 
2486c2dbbdaSespie 	if (buffer == NULL)
2496c2dbbdaSespie 		buffer = emalloc(bufsize);
2506c2dbbdaSespie 
2516c2dbbdaSespie 	buffer[i++] = '/';
2526c2dbbdaSespie 	i = parse_reduce(i, dir);
2536c2dbbdaSespie 	i--;
2546c2dbbdaSespie 
2556c2dbbdaSespie 	if (buffer[i-1] != '/')
2566c2dbbdaSespie 		buffer[i++] = '/';
2576c2dbbdaSespie 	i = parse_reduce(i, src);
2586c2dbbdaSespie 	return estrdup(buffer);
2596c2dbbdaSespie }
2606c2dbbdaSespie 
2616c2dbbdaSespie static char *
relative_reduce2(const char * dir1,const char * dir2,const char * src)2626c2dbbdaSespie relative_reduce2(const char *dir1, const char *dir2, const char *src)
2636c2dbbdaSespie {
2646c2dbbdaSespie 	size_t i = 0;
2656c2dbbdaSespie 
2666c2dbbdaSespie 	if (buffer == NULL)
2676c2dbbdaSespie 		buffer = emalloc(bufsize);
2686c2dbbdaSespie 
2696c2dbbdaSespie 	buffer[i++] = '/';
2706c2dbbdaSespie 	i = parse_reduce(i, dir1);
2716c2dbbdaSespie 	i--;
2726c2dbbdaSespie 	if (buffer[i-1] != '/')
2736c2dbbdaSespie 		buffer[i++] = '/';
2746c2dbbdaSespie 
2756c2dbbdaSespie 	i = parse_reduce(i, dir2);
2766c2dbbdaSespie 	i--;
2776c2dbbdaSespie 	if (buffer[i-1] != '/')
2786c2dbbdaSespie 		buffer[i++] = '/';
2796c2dbbdaSespie 
2806c2dbbdaSespie 	i = parse_reduce(i, src);
2816c2dbbdaSespie 	return estrdup(buffer);
2826c2dbbdaSespie }
2836c2dbbdaSespie 
2846c2dbbdaSespie static char *
names_match_with_dir(const char * a,const char * b,char * ra,char * rb,const char * dir)2856c2dbbdaSespie names_match_with_dir(const char *a, const char *b, char *ra,
2866c2dbbdaSespie     char *rb,  const char *dir)
2876c2dbbdaSespie {
2886c2dbbdaSespie 	bool r;
2896c2dbbdaSespie 	bool free_a, free_b;
2906c2dbbdaSespie 
2916c2dbbdaSespie 	if (ra == NULL) {
2926c2dbbdaSespie 		ra = relative_reduce(dir, a);
2936c2dbbdaSespie 		free_a = true;
2946c2dbbdaSespie 	} else {
2956c2dbbdaSespie 		free_a = false;
2966c2dbbdaSespie 	}
2976c2dbbdaSespie 
2986c2dbbdaSespie 	if (rb == NULL) {
2996c2dbbdaSespie 		rb = relative_reduce(dir, b);
3006c2dbbdaSespie 		free_b = true;
3016c2dbbdaSespie 	} else {
3026c2dbbdaSespie 		free_b = false;
3036c2dbbdaSespie 	}
3046c2dbbdaSespie 	r = strcmp(ra, rb) == 0;
3056c2dbbdaSespie 	if (free_a)
3066c2dbbdaSespie 		free(ra);
3076c2dbbdaSespie 	if (r)
3086c2dbbdaSespie 		return rb;
3096c2dbbdaSespie 	else {
3106c2dbbdaSespie 		if (free_b)
3116c2dbbdaSespie 			free(rb);
3126c2dbbdaSespie 		return NULL;
3136c2dbbdaSespie 	}
3146c2dbbdaSespie }
3156c2dbbdaSespie 
3166c2dbbdaSespie static char *
names_match_with_dirs(const char * a,const char * b,char * ra,char * rb,const char * dir1,const char * dir2)3176c2dbbdaSespie names_match_with_dirs(const char *a, const char *b, char *ra,
3186c2dbbdaSespie     char *rb,  const char *dir1, const char *dir2)
3196c2dbbdaSespie {
3206c2dbbdaSespie 	bool r;
3216c2dbbdaSespie 	bool free_a, free_b;
3226c2dbbdaSespie 
3236c2dbbdaSespie 	if (ra == NULL) {
3246c2dbbdaSespie 		ra = relative_reduce2(dir1, dir2, a);
3256c2dbbdaSespie 		free_a = true;
3266c2dbbdaSespie 	} else {
3276c2dbbdaSespie 		free_a = false;
3286c2dbbdaSespie 	}
3296c2dbbdaSespie 
3306c2dbbdaSespie 	if (rb == NULL) {
3316c2dbbdaSespie 		rb = relative_reduce2(dir1, dir2, b);
3326c2dbbdaSespie 		free_b = true;
3336c2dbbdaSespie 	} else {
3346c2dbbdaSespie 		free_b = false;
3356c2dbbdaSespie 	}
3366c2dbbdaSespie 	r = strcmp(ra, rb) == 0;
3376c2dbbdaSespie 	if (free_a)
3386c2dbbdaSespie 		free(ra);
3396c2dbbdaSespie 	if (r)
3406c2dbbdaSespie 		return rb;
3416c2dbbdaSespie 	else {
3426c2dbbdaSespie 		if (free_b)
3436c2dbbdaSespie 			free(rb);
3446c2dbbdaSespie 		return NULL;
3456c2dbbdaSespie 	}
3466c2dbbdaSespie }
3476c2dbbdaSespie 
3486c2dbbdaSespie static char *
names_match(GNode * a,GNode * b)3496c2dbbdaSespie names_match(GNode *a, GNode *b)
3506c2dbbdaSespie {
3516c2dbbdaSespie 	char *ra = NULL , *rb = NULL;
3526c2dbbdaSespie 	char *r;
3536c2dbbdaSespie 
3546c2dbbdaSespie 	if (a->name[0] == '/')
3556c2dbbdaSespie 		ra = absolute_reduce(a->name);
3566c2dbbdaSespie 	if (b->name[0] == '/')
3576c2dbbdaSespie 		rb = absolute_reduce(b->name);
3586c2dbbdaSespie 	if (ra && rb) {
3596c2dbbdaSespie 		if (strcmp(ra, rb) == 0)
3606c2dbbdaSespie 			r = rb;
3616c2dbbdaSespie 		else
3626c2dbbdaSespie 			r = NULL;
3636c2dbbdaSespie 	} else {
3646c2dbbdaSespie 		r = names_match_with_dir(a->name, b->name, ra, rb, objdir);
3656c2dbbdaSespie 		if (!r)
3666c2dbbdaSespie 			r = names_match_with_dir(a->name, b->name, ra, rb,
3676c2dbbdaSespie 			    curdir);
3686c2dbbdaSespie 		if (!r) {
3696c2dbbdaSespie 			/* b has necessarily the same one */
3706c2dbbdaSespie 			Lst l = find_suffix_path(a);
3716c2dbbdaSespie 			LstNode ln;
3726c2dbbdaSespie 
3736c2dbbdaSespie 			for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln)) {
3746c2dbbdaSespie 				const char *p = PathEntry_name(Lst_Datum(ln));
3756c2dbbdaSespie 				if (p[0] == '/') {
3766c2dbbdaSespie 					r = names_match_with_dir(a->name,
3776c2dbbdaSespie 					    b->name, ra, rb, p);
3786c2dbbdaSespie 					if (r)
3796c2dbbdaSespie 						break;
3806c2dbbdaSespie 				} else {
3816c2dbbdaSespie 					r = names_match_with_dirs(a->name,
3826c2dbbdaSespie 					    b->name, ra, rb, p, objdir);
3836c2dbbdaSespie 					if (r)
3846c2dbbdaSespie 						break;
3856c2dbbdaSespie 					r = names_match_with_dirs(a->name,
3866c2dbbdaSespie 					    b->name, ra, rb, p, curdir);
3876c2dbbdaSespie 					if (r)
3886c2dbbdaSespie 						break;
3896c2dbbdaSespie 				}
3906c2dbbdaSespie 			}
3916c2dbbdaSespie 		}
3926c2dbbdaSespie 	}
3936c2dbbdaSespie 	free(ra);
3946c2dbbdaSespie 	if (r != rb)
3956c2dbbdaSespie 		free(rb);
3966c2dbbdaSespie 	return r;
3976c2dbbdaSespie }
3986c2dbbdaSespie 
3996c2dbbdaSespie static void
find_siblings(GNode * gn)4006c2dbbdaSespie find_siblings(GNode *gn)
4016c2dbbdaSespie {
4026c2dbbdaSespie 	GNode *gn2;
4036c2dbbdaSespie 	char *fullpath;
4046c2dbbdaSespie 
4056c2dbbdaSespie 	/* not part of an equivalence class: can't alias */
4066c2dbbdaSespie 	if (gn->next == NULL)
4076c2dbbdaSespie 		return;
4086c2dbbdaSespie 	/* already resolved, actually */
4096c2dbbdaSespie 	if (gn->sibling != gn)
4106c2dbbdaSespie 		return;
4116c2dbbdaSespie 	if (DEBUG(NAME_MATCHING))
4126c2dbbdaSespie 		fprintf(stderr, "Matching for %s:", gn->name);
4136c2dbbdaSespie 	/* look through the aliases */
4146c2dbbdaSespie 	for (gn2 = gn->next; gn2 != gn; gn2 = gn2->next) {
4156c2dbbdaSespie 		fullpath = names_match(gn, gn2);
4166c2dbbdaSespie 		if (fullpath) {
4176c2dbbdaSespie 			attach_node(gn, gn2);
4186c2dbbdaSespie 		} else {
4196c2dbbdaSespie 			if (DEBUG(NAME_MATCHING))
4206c2dbbdaSespie 				fputc('!', stderr);
4216c2dbbdaSespie 		}
4226c2dbbdaSespie 		if (DEBUG(NAME_MATCHING))
4236c2dbbdaSespie 			fprintf(stderr, "%s ", gn2->name);
4246c2dbbdaSespie 	}
4256c2dbbdaSespie 	if (DEBUG(NAME_MATCHING))
4266c2dbbdaSespie 		fputc('\n', stderr);
4276c2dbbdaSespie }
4286c2dbbdaSespie 
4296c2dbbdaSespie void
look_harder_for_target(GNode * gn)4306c2dbbdaSespie look_harder_for_target(GNode *gn)
4316c2dbbdaSespie {
4326c2dbbdaSespie 	static bool equiv_was_built = false;
4336c2dbbdaSespie 
4346c2dbbdaSespie 	if (!equiv_was_built) {
4356c2dbbdaSespie 		build_equivalence();
4366c2dbbdaSespie 		equiv_was_built = true;
4376c2dbbdaSespie 	}
4386c2dbbdaSespie 
4396c2dbbdaSespie 	if (gn->type & (OP_RESOLVED | OP_PHONY))
4406c2dbbdaSespie 		return;
4416c2dbbdaSespie 	gn->type |= OP_RESOLVED;
4426c2dbbdaSespie 	find_siblings(gn);
4436c2dbbdaSespie }
4446c2dbbdaSespie 
4456c2dbbdaSespie bool
is_sibling(GNode * gn,GNode * gn2)4466c2dbbdaSespie is_sibling(GNode *gn, GNode *gn2)
4476c2dbbdaSespie {
4486c2dbbdaSespie 	GNode *sibling;
4496c2dbbdaSespie 
4506c2dbbdaSespie 	sibling = gn;
4516c2dbbdaSespie 	do {
4526c2dbbdaSespie 		if (sibling == gn2)
4536c2dbbdaSespie 			return true;
4546c2dbbdaSespie 		sibling = sibling->sibling;
4556c2dbbdaSespie 	} while (sibling != gn);
4566c2dbbdaSespie 
4576c2dbbdaSespie 	return false;
4586c2dbbdaSespie }
459