Edit

IABSD.fr/src/usr.bin/make/suff.c

Branch :

  • Show log

    Commit

  • Author : espie
    Date : 2020-01-13 15:41:53
    Hash : ec86491d
    Message : make the choice of engine explicit simplify the running of .BEGIN/.END so that they pass through the engine first (so they can now have dependencies). Error out properly if .BEGIN/.END fails.

  • usr.bin/make/suff.c
  • /*	$OpenBSD: suff.c,v 1.102 2020/01/13 15:41:53 espie Exp $ */
    /*	$NetBSD: suff.c,v 1.13 1996/11/06 17:59:25 christos Exp $	*/
    
    /*
     * Copyright (c) 1988, 1989, 1990, 1993
     *	The Regents of the University of California.  All rights reserved.
     * Copyright (c) 1989 by Berkeley Softworks
     * All rights reserved.
     *
     * This code is derived from software contributed to Berkeley by
     * Adam de Boor.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. Neither the name of the University nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     */
    
    /*-
     * suff.c --
     *	Functions to maintain suffix lists and find implicit dependents
     *	using suffix transformation rules
     */
    
    #include <ctype.h>
    #include <stddef.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ohash.h>
    #include "config.h"
    #include "defines.h"
    #include "dir.h"
    #include "engine.h"
    #include "suff.h"
    #include "var.h"
    #include "targ.h"
    #include "error.h"
    #include "str.h"
    #include "lst.h"
    #include "memory.h"
    #include "gnode.h"
    #include "stats.h"
    #include "dump.h"
    #include "expandchildren.h"
    
    /* XXX the suffixes hash is stored using a specific hash function, suitable
     * for looking up suffixes in reverse.
     */
    static struct ohash suffixes;
    
    /* We remember the longest suffix, so we don't need to look beyond that.  */
    size_t maxLen = 0U;
    static LIST srclist;
    
    /* Transforms (.c.o) are stored in another hash, independently from suffixes.
     * When make sees a target, it checks whether it's currently parsable as a
     * transform (according to the active suffixes). If yes, it's stored as a
     * new transform.
     *
     * XXX
     * But transforms DO NOT have a canonical decomposition as a set of suffixes,
     * and will be used as necessary later, when looking up implicit rules for
     * actual targets.
     *
     * For instance, a transform .a.b.c  can be parsed as .a -> .b.c if suffixes
     * .a and .b.c are active, and then LATER, reused as .a.b -> .c if suffixes
     * .a.b and .c are active.
     */
    static struct ohash transforms;
    
    /* conflicts between suffixes are solved by suffix declaration order. */
    static int order = 0;
    
    /*
     * Structure describing an individual suffix.
     */
    struct Suff_ {
    	size_t nameLen;		/* optimisation: strlen(name) */
    	short flags;
    #define SUFF_ACTIVE	  0x08	/* We never destroy suffixes and rules, */
    				/* we just deactivate them. */
    #define SUFF_PATH	  0x10	/* False suffix: actually, the path keyword */
    	LIST searchPath;	/* The path along which files of this suffix
    			     	 * may be found */
    	int order;		/* order of declaration for conflict
    				 * resolution. */
    	LIST parents;		/* List of Suff we have a transformation to */
    	LIST children;		/* List of Suff we have a transformation from */
    	char name[1];
    };
    
    static struct ohash_info suff_info = {
    	offsetof(struct Suff_, name), NULL,
    	hash_calloc, hash_free, element_alloc
    };
    
    /*
     * Structure used in the search for implied sources.
     */
    typedef struct Src_ {
    	char *file;		/* The file to look for */
    	char *prefix;		/* Prefix from which file was formed */
    	Suff *suff;		/* The suffix on the file */
    	struct Src_ *parent;	/* The Src for which this is a source */
    	GNode *node;		/* The node describing the file */
    	int children;		/* Count of existing children (so we don't free
    				 * this thing too early or never nuke it) */
    #ifdef DEBUG_SRC
    	LIST	    cp; 	/* Debug; children list */
    #endif
    } Src;
    
    /*
     * A structure for passing more than one argument to the Lst-library-invoked
     * function...
     */
    typedef struct {
    	Lst l;
    	Src *s;
    } LstSrc;
    
    static Suff *emptySuff; /* The empty suffix required for POSIX
    			 * single-suffix transformation rules */
    
    
    #define parse_transform(s, p, q) parse_transformi(s, s + strlen(s), p, q)
    static bool parse_transformi(const char *, const char *, Suff **, Suff **);
    #define new_suffix(s)	new_suffixi(s, NULL)
    static Suff *new_suffixi(const char *, const char *);
    static void reverse_hash_add_char(uint32_t *, const char *);
    static uint32_t reverse_hashi(const char *, const char **);
    static unsigned int reverse_slot(struct ohash *, const char *, const char **);
    static void record_possible_suffix(Suff *, GNode *, char *, Lst, Lst);
    static void record_possible_suffixes(GNode *, Lst, Lst);
    static Suff *find_suffix_as_suffix(Lst, const char *, const char *);
    static Suff *add_suffixi(const char *, const char *);
    
    static void SuffInsert(Lst, Suff *);
    static void SuffAddSrc(void *, void *);
    static bool SuffRemoveSrc(Lst);
    static void SuffAddLevel(Lst, Src *);
    static Src *SuffFindThem(Lst, Lst);
    static Src *SuffFindCmds(Src *, Lst);
    static bool SuffApplyTransform(GNode *, GNode *, Suff *, Suff *);
    static void SuffFindDeps(GNode *, Lst);
    static void SuffFindArchiveDeps(GNode *, Lst);
    static void SuffFindNormalDeps(GNode *, Lst);
    static void SuffPrintName(void *);
    static void SuffPrintSuff(void *);
    static void SuffPrintTrans(GNode *);
    
    #define find_suff(name)	find_suffi(name, NULL)
    static Suff *find_suffi(const char *, const char *);
    static Suff *find_best_suffix(const char *, const char *);
    static GNode *find_transform(const char *);
    static GNode *find_or_create_transformi(const char *, const char *);
    static void setup_paths(void);
    static void build_suffixes_graph(void);
    static void special_path_hack(void);
    
    #ifdef DEBUG_SRC
    static void PrintAddr(void *);
    #endif
    
    /* hash functions for the suffixes hash */
    /* add one char to the hash */
    static void
    reverse_hash_add_char(uint32_t *pk, const char *s)
    {
    	*pk =  ((*pk << 2) | (*pk >> 30)) ^ *s;
    }
    
    /* build a full hash from end to start */
    static uint32_t
    reverse_hashi(const char *s, const char **e)
    {
    	const char *p;
    	uint32_t k;
    
    	if (*e == NULL)
    		*e = s + strlen(s);
    	p = *e;
    	if (p == s)
    		k = 0;
    	else
    		k = *--p;
    	while (p != s) {
    		reverse_hash_add_char(&k, --p);
    	}
    	return k;
    }
    
    static unsigned int
    reverse_slot(struct ohash *h, const char *s, const char **e)
    {
    	uint32_t hv;
    
    	hv = reverse_hashi(s, e);
    	return ohash_lookup_interval(h, s, *e, hv);
    }
    
    
    static char *
    suffix_is_suffix(Suff *s, const char *str, const char *estr)
    {
    	const char *p1; 	/* Pointer into suffix name */
    	const char *p2; 	/* Pointer into string being examined */
    
    	if (estr - str < (ptrdiff_t) s->nameLen)
    		return NULL;
    	p1 = s->name + s->nameLen;
    	p2 = estr;
    
    	while (p1 != s->name) {
    		p1--;
    		p2--;
    		if (*p1 != *p2)
    			return NULL;
    	}
    
    	return (char *)p2;
    }
    
    static Suff *
    find_suffi(const char *name, const char *ename)
    {
    	unsigned int slot;
    #ifdef STATS_SUFF
    	STAT_SUFF_LOOKUP_NAME++;
    #endif
    	slot = reverse_slot(&suffixes, name, &ename);
    	return ohash_find(&suffixes, slot);
    }
    
    static GNode *
    find_transform(const char *name)
    {
    	unsigned int slot;
    
    #ifdef STATS_SUFF
    	STAT_TRANSFORM_LOOKUP_NAME++;
    #endif
    	slot = ohash_qlookup(&transforms, name);
    
    	return ohash_find(&transforms, slot);
    }
    
    static GNode *
    find_or_create_transformi(const char *name, const char *end)
    {
    	GNode *r;
    	unsigned int slot;
    
    #ifdef STATS_SUFF
    	STAT_TRANSFORM_LOOKUP_NAME++;
    #endif
    	slot = ohash_qlookupi(&transforms, name, &end);
    
    	r = ohash_find(&transforms, slot);
    
    	if (r == NULL) {
    		r = Targ_NewGNi(name, end);
    		ohash_insert(&transforms, slot, r);
    	}
    	return r;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffInsert  --
     *	Insert the suffix into the list keeping the list ordered by suffix
     *	numbers.
     *
     * Side Effects:
     *	The reference count of the suffix is incremented
     *-----------------------------------------------------------------------
     */
    static void
    SuffInsert(Lst l, Suff *s)
    {
    	LstNode ln;		/* current element in l we're examining */
    	Suff *s2 = NULL;	/* the suffix descriptor in this element */
    
    	for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln)) {
    		s2 = Lst_Datum(ln);
    		if (s2->order >= s->order)
    			break;
    	}
    
    	if (DEBUG(SUFF))
    		printf("inserting %s(%d)...", s->name, s->order);
    	if (ln == NULL) {
    		if (DEBUG(SUFF))
    			printf("at end of list\n");
    		Lst_AtEnd(l, s);
    	} else if (s2->order != s->order) {
    		if (DEBUG(SUFF))
    			printf("before %s(%d)\n", s2->name, s2->order);
    		Lst_Insert(l, ln, s);
    	} else if (DEBUG(SUFF)) {
    		printf("already there\n");
    	}
    }
    
    /* Suff_DisableAllSuffixes
     *	mark all current suffixes as inactive, and reset precedence
     *	computation.  */
    void
    Suff_DisableAllSuffixes(void)
    {
    	unsigned int i;
    	Suff *s;
    
    	for (s = ohash_first(&suffixes, &i); s != NULL;
    	    s = ohash_next(&suffixes, &i))
    		s->flags &= ~SUFF_ACTIVE;
    
    	order = 0;
    	maxLen = 0;
    }
    
    
    /* okay = parse_transform(str, &src, &targ);
     * 	try parsing a string as a transformation rule, returns true if
     *	successful. Fills &src, &targ with the constituent suffixes.
     * Special hack: source suffixes must exist OR be the special SUFF_PATH
     * pseudo suffix (.PATH)
     */
    static bool
    parse_transformi(const char *str, const char *e, Suff **srcPtr, Suff **targPtr)
    {
    	Suff *src, *target, *best_src, *best_target;
    	const char *p;
    
    	size_t len;
    	uint32_t hv;
    	unsigned int slot;
    
    	/* empty string -> no suffix */
    	if (e == str)
    		return false;
    
    	len = e - str;
    
    	if (len > 2 * maxLen)
    		return false;
    
    	p = e;
    	best_src = best_target = NULL;
    
    	hv = *--p;
    	while (p != str) {
    		slot = ohash_lookup_interval(&suffixes, p, e, hv);
    		/* no double suffix in there */
    		if (p - str <= (ptrdiff_t)maxLen) {
    			target = ohash_find(&suffixes, slot);
    			if (target != NULL && (target->flags & SUFF_ACTIVE)) {
    				src = find_suffi(str, p);
    				if (src != NULL &&
    				    (src->flags & (SUFF_ACTIVE | SUFF_PATH))) {
    				/* XXX even if we find a set of suffixes, we
    				 * have to keep going to find the best one,
    				 * namely, the one whose src appears first in
    				 * .SUFFIXES
    				 */
    					if (best_src == NULL ||
    					    src->order < best_src->order) {
    						best_src = src;
    						best_target = target;
    					}
    				}
    			}
    		}
    		/* can't be a suffix anyways */
    		if (e - p >= (ptrdiff_t)maxLen)
    			break;
    		reverse_hash_add_char(&hv, --p);
    	}
    
    	if (p == str && best_src == NULL) {
    		/* no double suffix transformation, resort to single suffix if
    		 * we find one.  */
    		slot = ohash_lookup_interval(&suffixes, p, e, hv);
    		src = ohash_find(&suffixes, slot);
    		if (src != NULL && (src->flags & (SUFF_ACTIVE | SUFF_PATH))) {
    			best_src = src;
    			best_target = emptySuff;
    		}
    	}
    	if (best_src != NULL) {
    		*srcPtr = best_src;
    		*targPtr = best_target;
    		return true;
    	} else {
    		return false;
    	}
    }
    
    static void
    special_path_hack(void)
    {
    	Suff *path = add_suffixi(".PATH", NULL);
    	path->flags |= SUFF_PATH;
    }
    
    static Suff *
    find_best_suffix(const char *s, const char *e)
    {
    	const char *p;
    	uint32_t hv;
    	unsigned int slot;
    	Suff *best = NULL;
    	Suff *suff;
    
    	if (e == s)
    		return NULL;
    	p = e;
    	hv = *--p;
    	while (p != s) {
    		slot = ohash_lookup_interval(&suffixes, p, e, hv);
    		suff = ohash_find(&suffixes, slot);
    		if (suff != NULL)
    			if (best == NULL || suff->order < best->order)
    				best = suff;
    		if (e - p >= (ptrdiff_t)maxLen)
    			break;
    		reverse_hash_add_char(&hv, --p);
    	}
    	return best;
    }
    
    Lst
    find_best_path(const char *name)
    {
    	Suff *s = find_best_suffix(name, name + strlen(name));
    	if (s != NULL) {
    		if (DEBUG(SUFF))
    			printf("suffix is \"%s\"...", s->name);
    		return &s->searchPath;
    	} else
    		return defaultPath;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * Suff_ParseAsTransform --
     *	Try parsing a target line as a transformation rule, depending on
     *	existing suffixes.
     *
     *	Possibly create a new transform, or reset an existing one.
     *-----------------------------------------------------------------------
     */
    GNode *
    Suff_ParseAsTransform(const char *line, const char *end)
    {
    	GNode *gn;	/* GNode of transformation rule */
    	Suff *s;	/* source suffix */
    	Suff *t;	/* target suffix */
    
    	if (!parse_transformi(line, end, &s, &t))
    		return NULL;
    
    	gn = find_or_create_transformi(line, end);
    	/* In case the transform already exists, nuke old commands and children.
    	 * Note we can't free them, since there might be stuff that references
    	 * them elsewhere
    	 */
    	if (!Lst_IsEmpty(&gn->commands)) {
    		Lst_Destroy(&gn->commands, NOFREE);
    		Lst_Init(&gn->commands);
    	}
    	if (!Lst_IsEmpty(&gn->children)) {
    		Lst_Destroy(&gn->children, NOFREE);
    		Lst_Init(&gn->children);
    	}
    
    	gn->type = OP_TRANSFORM;
    	if (s->flags & SUFF_PATH) {
    		gn->special = SPECIAL_PATH;
    		gn->suffix = t;
    	}
    
    	if (DEBUG(SUFF))
    		printf("defining transformation from `%s' to `%s'\n",
    		    s->name, t->name);
    	return gn;
    }
    
    static void
    make_suffix_known(Suff *s)
    {
    	if ((s->flags & SUFF_ACTIVE) == 0) {
    		s->order = order++;
    		s->flags |= SUFF_ACTIVE;
    		if (s->nameLen > maxLen)
    			maxLen = s->nameLen;
    	}
    }
    
    static Suff *
    new_suffixi(const char *str, const char *eptr)
    {
    	Suff *s;
    
    	s = ohash_create_entry(&suff_info, str, &eptr);
    	s->nameLen = eptr - str;
    	Lst_Init(&s->searchPath);
    	Lst_Init(&s->children);
    	Lst_Init(&s->parents);
    	s->flags = 0;
    	return s;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * Suff_AddSuffix --
     *	Add the suffix in string to the end of the list of known suffixes.
     *	Should we restructure the suffix graph? Make doesn't...
     *
     * Side Effects:
     *	A GNode is created for the suffix and a Suff structure is created and
     *	added to the known suffixes, unless it was already known.
     *-----------------------------------------------------------------------
     */
    void
    Suff_AddSuffixi(const char *str, const char *end)
    {
    	(void)add_suffixi(str, end);
    }
    
    static Suff *
    add_suffixi(const char *str, const char *end)
    {
    	Suff *s;	    /* new suffix descriptor */
    
    	unsigned int slot;
    
    	slot = reverse_slot(&suffixes, str, &end);
    	s = ohash_find(&suffixes, slot);
    	if (s == NULL) {
    		s = new_suffixi(str, end);
    		ohash_insert(&suffixes, slot, s);
    	}
    	make_suffix_known(s);
    	return s;
    }
    
    Lst
    find_suffix_path(GNode *gn)
    {
    	if (gn->suffix != NULL && gn->suffix != emptySuff)
    		return &(gn->suffix->searchPath);
    	else
    		return defaultPath;
    }
    
    static void
    build_suffixes_graph(void)
    {
    	Suff *s, *s2;
    	GNode *gn;
    	unsigned int i;
    
    	for (gn = ohash_first(&transforms, &i); gn != NULL;
    	    gn = ohash_next(&transforms, &i)) {
    	    	if (Lst_IsEmpty(&gn->commands) && Lst_IsEmpty(&gn->children))
    			continue;
    		if (gn->special == SPECIAL_PATH)
    			continue;
    	    	if (parse_transform(gn->name, &s, &s2)) {
    			SuffInsert(&s2->children, s);
    			SuffInsert(&s->parents, s2);
    		}
    	}
    }
    
    /*-
     *-----------------------------------------------------------------------
     * setup_paths
     *	Extend the search paths for all suffixes to include the default
     *	search path.
     *
     * Side Effects:
     *	The searchPath field of all the suffixes is extended by the
     *	directories in defaultPath. 
     *-----------------------------------------------------------------------
     */
    static void
    setup_paths(void)
    {
    	unsigned int i;
    	Suff *s;
    
    	for (s = ohash_first(&suffixes, &i); s != NULL;
    	    s = ohash_next(&suffixes, &i)) {
    		if (!Lst_IsEmpty(&s->searchPath))
    			Dir_Concat(&s->searchPath, defaultPath);
    		else
    			Lst_Clone(&s->searchPath, defaultPath, Dir_CopyDir);
    	}
    }
    
    void
    process_suffixes_after_makefile_is_read(void)
    {
    	/* once the Makefile is finish reading, we can set up the default PATH
    	 * stuff, and build the final suffixes graph
    	 */
    	setup_paths();
    	/* and we link all transforms to active suffixes at this point. */
    	build_suffixes_graph();
    }
    	  /********** Implicit Source Search Functions *********/
    
    /*-
     *-----------------------------------------------------------------------
     * SuffAddSrc  --
     *	Add a suffix as a Src structure to the given list with its parent
     *	being the given Src structure. If the suffix is the null suffix,
     *	the prefix is used unaltered as the file name in the Src structure.
     *
     * Side Effects:
     *	A Src structure is created and tacked onto the end of the list
     *-----------------------------------------------------------------------
     */
    static void
    SuffAddSrc(
        void *sp,		/* suffix for which to create a Src structure */
        void *lsp)		/* list and parent for the new Src */
    {
    	Suff *s = sp;
    	LstSrc *ls = lsp;
    	Src *s2;	/* new Src structure */
    	Src *targ;	/* Target structure */
    
    	targ = ls->s;
    
    	s2 = emalloc(sizeof(Src));
    	s2->file = Str_concat(targ->prefix, s->name, 0);
    	s2->prefix = targ->prefix;
    	s2->parent = targ;
    	s2->node = NULL;
    	s2->suff = s;
    	s2->children = 0;
    	targ->children++;
    	Lst_AtEnd(ls->l, s2);
    #ifdef DEBUG_SRC
    	Lst_Init(&s2->cp);
    	Lst_AtEnd(&targ->cp, s2);
    	printf("2 add %x %x to %x:", targ, s2, ls->l);
    	Lst_Every(ls->l, PrintAddr);
    	printf("\n");
    #endif
    
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffAddLevel  --
     *	Add all the children of targ as Src structures to the given list
     *
     * Side Effects:
     *	Lots of structures are created and added to the list
     *-----------------------------------------------------------------------
     */
    static void
    SuffAddLevel(
        Lst l,	/* list to which to add the new level */
        Src *targ)	/* Src structure to use as the parent */
    {
    	LstSrc	   ls;
    
    	ls.s = targ;
    	ls.l = l;
    
    	Lst_ForEach(&targ->suff->children, SuffAddSrc, &ls);
    }
    
    /*-
     *----------------------------------------------------------------------
     * SuffRemoveSrc --
     *	Free Src structure with a zero reference count in a list
     *
     *	returns true if a src was removed
     *----------------------------------------------------------------------
     */
    static bool
    SuffRemoveSrc(Lst l)
    {
    	LstNode ln;
    	Src *s;
    
    #ifdef DEBUG_SRC
    	printf("cleaning %lx: ", (unsigned long)l);
    	Lst_Every(l, PrintAddr);
    	printf("\n");
    #endif
    
    
    	for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln)) {
    		s = Lst_Datum(ln);
    		if (s->children == 0) {
    			free(s->file);
    			if (!s->parent)
    				free(s->prefix);
    			else {
    #ifdef DEBUG_SRC
    				LstNode ln2 = Lst_Member(&s->parent->cp, s);
    				if (ln2 != NULL)
    				    Lst_Remove(&s->parent->cp, ln2);
    #endif
    				--s->parent->children;
    			}
    #ifdef DEBUG_SRC
    			printf("free: [l=%x] p=%x %d\n", l, s, s->children);
    			Lst_Destroy(&s->cp, NOFREE);
    #endif
    			Lst_Remove(l, ln);
    			free(s);
    			return true;
    		}
    #ifdef DEBUG_SRC
    		else {
    			printf("keep: [l=%x] p=%x %d: ", l, s, s->children);
    			Lst_Every(&s->cp, PrintAddr);
    			printf("\n");
    		}
    #endif
    	}
    
    	return false;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffFindThem --
     *	Find the first existing file/target in the list srcs
     *
     * Results:
     *	The lowest structure in the chain of transformations
     *-----------------------------------------------------------------------
     */
    static Src *
    SuffFindThem(
        Lst srcs,	/* list of Src structures to search through */
        Lst slst)
    {
    	Src *s;		/* current Src */
    	Src *rs; 	/* returned Src */
    	char *ptr;
    
    	rs = NULL;
    
    	while ((s = Lst_DeQueue(srcs)) != NULL) {
    		if (DEBUG(SUFF))
    			printf("\ttrying %s...", s->file);
    
    		/*
    		 * A file is considered to exist if either a node exists in the
    		 * graph for it or the file actually exists.
    		 */
    		if (Targ_FindNode(s->file, TARG_NOCREATE) != NULL) {
    #ifdef DEBUG_SRC
    			printf("remove %x from %x\n", s, srcs);
    #endif
    			rs = s;
    			break;
    		}
    
    		if ((ptr = Dir_FindFile(s->file, &s->suff->searchPath))
    		    != NULL) {
    			rs = s;
    #ifdef DEBUG_SRC
    			printf("remove %x from %x\n", s, srcs);
    #endif
    			free(ptr);
    			break;
    		}
    
    		if (DEBUG(SUFF))
    		    printf("not there\n");
    
    		SuffAddLevel(srcs, s);
    		Lst_AtEnd(slst, s);
    	}
    
    	if (DEBUG(SUFF) && rs)
    	    printf("got it\n");
    	return rs;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffFindCmds --
     *	See if any of the children of the target in the Src structure is
     *	one from which the target can be transformed. If there is one,
     *	a Src structure is put together for it and returned.
     *-----------------------------------------------------------------------
     */
    static Src *
    SuffFindCmds(Src *targ, Lst slst)
    {
    	LstNode ln;	/* General-purpose list node */
    	GNode *t;	/* Target GNode */
    	GNode *s;	/* Source GNode */
    	int prefixLen;	/* The length of the defined prefix */
    	Suff *suff;	/* Suffix on matching beastie */
    	Src *ret;	/* Return value */
    	const char *cp;
    
    	t = targ->node;
    	prefixLen = strlen(targ->prefix);
    
    	for (ln = Lst_First(&t->children); ln != NULL; ln = Lst_Adv(ln)) {
    		s = Lst_Datum(ln);
    
    		cp = strrchr(s->name, '/');
    		if (cp == NULL)
    			cp = s->name;
    		else
    			cp++;
    		if (strncmp(cp, targ->prefix, prefixLen) != 0)
    			continue;
    		/* The node matches the prefix ok, see if it has a known
    		 * suffix.	*/
    		suff = find_suff(&cp[prefixLen]);
    		if (suff == NULL)
    			continue;
    		/*
    		 * It even has a known suffix, see if there's a transformation
    		 * defined between the node's suffix and the target's suffix.
    		 *
    		 * XXX: Handle multi-stage transformations here, too.
    		 */
    		if (Lst_Member(&suff->parents, targ->suff) == NULL)
    			continue;
    		/*
    		 * Create a new Src structure to describe this transformation
    		 * (making sure to duplicate the source node's name so
    		 * Suff_FindDeps can free it again (ick)), and return the new
    		 * structure.
    		 */
    		ret = emalloc(sizeof(Src));
    		ret->file = estrdup(s->name);
    		ret->prefix = targ->prefix;
    		ret->suff = suff;
    		ret->parent = targ;
    		ret->node = s;
    		ret->children = 0;
    		targ->children++;
    #ifdef DEBUG_SRC
    		Lst_Init(&ret->cp);
    		printf("3 add %x %x\n", targ, ret);
    		Lst_AtEnd(&targ->cp, ret);
    #endif
    		Lst_AtEnd(slst, ret);
    		if (DEBUG(SUFF))
    			printf("\tusing existing source %s\n", s->name);
    		return ret;
    	}
    	return NULL;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffApplyTransform --
     *	Apply a transformation rule, given the source and target nodes
     *	and suffixes.
     *
     * Results:
     *	true if successful, false if not.
     *
     * Side Effects:
     *	The source and target are linked and the commands from the
     *	transformation are added to the target node's commands list.
     *	All attributes but OP_DEPMASK and OP_TRANSFORM are applied
     *	to the target. The target also inherits all the sources for
     *	the transformation rule.
     *-----------------------------------------------------------------------
     */
    static bool
    SuffApplyTransform(
        GNode	*tGn,	/* Target node */
        GNode	*sGn,	/* Source node */
        Suff	*t,	/* Target suffix */
        Suff	*s)	/* Source suffix */
    {
    	LstNode	ln;	/* General node */
    	char	*tname; /* Name of transformation rule */
    	GNode	*gn;	/* Node for same */
    
    	if (Lst_AddNew(&tGn->children, sGn)) {
    		/* Not already linked, so form the proper links between the
    		 * target and source.  */
    		LinkParent(sGn, tGn);
    	}
    
    	if ((sGn->type & OP_OPMASK) == OP_DOUBLEDEP) {
    		/* When a :: node is used as the implied source of a node, we
    		 * have to link all its cohorts in as sources as well. There's
    		 * only one implied src, as that will be sufficient to get
    		 * the .IMPSRC variable set for tGn.	*/
    		for (ln=Lst_First(&sGn->cohorts); ln != NULL; ln=Lst_Adv(ln)) {
    			gn = Lst_Datum(ln);
    
    			if (Lst_AddNew(&tGn->children, gn)) {
    				/* Not already linked, so form the proper links
    				 * between the target and source.  */
    				LinkParent(gn, tGn);
    			}
    		}
    	}
    	/* Locate the transformation rule itself.  */
    	tname = Str_concat(s->name, t->name, 0);
    	gn = find_transform(tname);
    	free(tname);
    
    	if (gn == NULL)
    		/*
    		 * Not really such a transformation rule (can happen when we're
    		 * called to link an OP_MEMBER and OP_ARCHV node), so return
    		 * false.
    		 */
    		return false;
    
    	if (DEBUG(SUFF))
    		printf("\tapplying %s -> %s to \"%s\"\n", s->name, t->name,
    		    tGn->name);
    
    	/* Record last child for expansion purposes.  */
    	ln = Lst_Last(&tGn->children);
    
    	/* Pass the buck to Make_HandleUse to apply the rule.  */
    	Make_HandleUse(gn, tGn);
    
    	/* Deal with wildcards and variables in any acquired sources.  */
    	expand_children_from(tGn, Lst_Succ(ln));
    
    	/* Keep track of another parent to which this beast is transformed so
    	 * the .IMPSRC variable can be set correctly for the parent.  */
    	tGn->impliedsrc = sGn;
    
    	return true;
    }
    
    static Suff *
    find_suffix_as_suffix(Lst l, const char *b, const char *e)
    {
    	LstNode ln;
    	Suff *s;
    
    	for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln)) {
    		s = Lst_Datum(ln);
    		if (suffix_is_suffix(s, b, e))
    			return s;
    	}
    	return NULL;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffFindArchiveDeps --
     *	Locate dependencies for an OP_ARCHV node.
     *
     * Side Effects:
     *	Same as Suff_FindDeps
     *-----------------------------------------------------------------------
     */
    static void
    SuffFindArchiveDeps(
        GNode	*gn,    /* Node for which to locate dependencies */
        Lst 	slst)
    {
    	char *eoarch;	/* End of archive portion */
    	char *eoname;	/* End of member portion */
    	GNode *mem;	/* Node for member */
    	Suff *ms;	/* Suffix descriptor for member */
    	char *name;	/* Start of member's name */
    
    	/* The node is an archive(member) pair. so we must find a suffix
    	 * for both of them.  */
    	eoarch = strchr(gn->name, '(');
    	if (eoarch == NULL)
    		return;
    
    	name = eoarch + 1;
    
    	eoname = strchr(name, ')');
    	if (eoname == NULL)
    		return;
    
    	/* To simplify things, call Suff_FindDeps recursively on the member now,
    	 * so we can simply compare the member's .PREFIX and .TARGET variables
    	 * to locate its suffix. This allows us to figure out the suffix to
    	 * use for the archive without having to do a quadratic search over the
    	 * suffix list, backtracking for each one...  */
    	mem = Targ_FindNodei(name, eoname, TARG_CREATE);
    	SuffFindDeps(mem, slst);
    
    	/* Create the link between the two nodes right off. */
    	if (Lst_AddNew(&gn->children, mem))
    		LinkParent(mem, gn);
    
    	/* Copy variables from member node to this one.  */
    	Var(TARGET_INDEX, gn) = Var(TARGET_INDEX, mem);
    	Var(PREFIX_INDEX, gn) = Var(PREFIX_INDEX, mem);
    
    	ms = mem->suffix;
    	if (ms == NULL) {
    		/* Didn't know what it was -- use .NULL suffix if not in make
    		 * mode.  */
    		if (DEBUG(SUFF))
    			printf("using empty suffix\n");
    		ms = emptySuff;
    	}
    
    
    	/* Set the other two local variables required for this target.  */
    	Var(MEMBER_INDEX, gn) = mem->name;
    	Var(ARCHIVE_INDEX, gn) = gn->name;
    
    	if (ms != NULL) {
    		/*
    		 * Member has a known suffix, so look for a transformation rule
    		 * from it to a possible suffix of the archive. Rather than
    		 * searching through the entire list, we just look at suffixes
    		 * to which the member's suffix may be transformed...
    		 */
    
    		Suff *suff;
    
    		suff = find_suffix_as_suffix(&ms->parents, gn->name, eoarch);
    
    		if (suff != NULL) {
    			/* Got one -- apply it.  */
    			if (!SuffApplyTransform(gn, mem, suff, ms) &&
    			    DEBUG(SUFF))
    				printf("\tNo transformation from %s -> %s\n",
    				   ms->name, suff->name);
    		}
    	}
    
    	/* Pretend gn appeared to the left of a dependency operator so
    	 * the user needn't provide a transformation from the member to the
    	 * archive.  */
    	if (OP_NOP(gn->type))
    		gn->type |= OP_DEPENDS;
    
    	/* Flag the member as such so we remember to look in the archive for
    	 * its modification time.  */
    	mem->type |= OP_MEMBER;
    }
    
    static void
    record_possible_suffix(Suff *s, GNode *gn, char *eoname, Lst srcs, Lst targs)
    {
    	int prefixLen;
    	Src *targ;
    
    	targ = emalloc(sizeof(Src));
    	targ->file = estrdup(gn->name);
    	targ->suff = s;
    	targ->node = gn;
    	targ->parent = NULL;
    	targ->children = 0;
    #ifdef DEBUG_SRC
    	Lst_Init(&targ->cp);
    #endif
    
    	/* Allocate room for the prefix, whose end is found by
    	 * subtracting the length of the suffix from the end of
    	 * the name.  */
    	prefixLen = (eoname - targ->suff->nameLen) - gn->name;
    	targ->prefix = emalloc(prefixLen + 1);
    	memcpy(targ->prefix, gn->name, prefixLen);
    	targ->prefix[prefixLen] = '\0';
    
    	/* Add nodes from which the target can be made.  */
    	SuffAddLevel(srcs, targ);
    
    	/* Record the target so we can nuke it.  */
    	Lst_AtEnd(targs, targ);
    }
    
    static void
    record_possible_suffixes(GNode *gn, Lst srcs, Lst targs)
    {
    	char *s = gn->name;
    	char *e =  s + strlen(s);
    	const char *p;
    	uint32_t hv;
    	unsigned int slot;
    	Suff *suff;
    
    	if (e == s)
    		return;
    
    	p = e;
    	hv = *--p;
    
    	while (p != s) {
    		slot = ohash_lookup_interval(&suffixes, p, e, hv);
    		suff = ohash_find(&suffixes, slot);
    		if (suff != NULL && (suff->flags & SUFF_ACTIVE))
    			record_possible_suffix(suff, gn, e, srcs, targs);
    		if (e - p >= (ptrdiff_t)maxLen)
    			break;
    		reverse_hash_add_char(&hv, --p);
    	}
    }
    
    /*-
     *-----------------------------------------------------------------------
     * SuffFindNormalDeps --
     *	Locate implicit dependencies for regular targets.
     *
     * Side Effects:
     *	Same as Suff_FindDeps...
     *-----------------------------------------------------------------------
     */
    static void
    SuffFindNormalDeps(
        GNode	*gn,	    /* Node for which to find sources */
        Lst 	slst)
    {
    	LIST srcs;	/* List of sources at which to look */
    	LIST targs;	/* List of targets to which things can be
    			 * transformed. They all have the same file,
    			 * but different suff and prefix fields */
    	Src *bottom;    /* Start of found transformation path */
    	Src *src;	/* General Src pointer */
    	char *prefix;	/* Prefix to use */
    	Src *targ;	/* General Src target pointer */
    
    
    	Lst_Init(&srcs);
    	Lst_Init(&targs);
    
    	/* We're caught in a catch-22 here. On the one hand, we want to use any
    	 * transformation implied by the target's sources, but we can't examine
    	 * the sources until we've expanded any variables/wildcards they may
    	 * hold, and we can't do that until we've set up the target's local
    	 * variables and we can't do that until we know what the proper suffix
    	 * for the target is (in case there are two suffixes one of which is a
    	 * suffix of the other) and we can't know that until we've found its
    	 * implied source, which we may not want to use if there's an existing
    	 * source that implies a different transformation.
    	 *
    	 * In an attempt to get around this, which may not work all the time,
    	 * but should work most of the time, we look for implied sources first,
    	 * checking transformations to all possible suffixes of the target, use
    	 * what we find to set the target's local variables, expand the
    	 * children, then look for any overriding transformations they imply.
    	 * Should we find one, we discard the one we found before.	*/
    
    
    	record_possible_suffixes(gn, &srcs, &targs);
    	/* Handle target of unknown suffix...  */
    	if (Lst_IsEmpty(&srcs)) {
    		if (DEBUG(SUFF))
    			printf("\tNo known suffix on %s. Using empty suffix\n",
    			    gn->name);
    
    		targ = emalloc(sizeof(Src));
    		targ->file = estrdup(gn->name);
    		targ->suff = emptySuff;
    		targ->node = gn;
    		targ->parent = NULL;
    		targ->children = 0;
    		targ->prefix = estrdup(gn->name);
    #ifdef DEBUG_SRC
    		Lst_Init(&targ->cp);
    #endif
    
    		/* Only use the default suffix rules if we don't have commands
    		 * or dependencies defined for this gnode.  */
    		if (Lst_IsEmpty(&gn->commands) && Lst_IsEmpty(&gn->children))
    			SuffAddLevel(&srcs, targ);
    		else {
    			if (DEBUG(SUFF))
    				printf("not ");
    		}
    
    		if (DEBUG(SUFF))
    			printf("adding suffix rules\n");
    
    		Lst_AtEnd(&targs, targ);
    	}
    
    	/* Using the list of possible sources built up from the target
    	 * suffix(es), try and find an existing file/target that matches.  */
    	bottom = SuffFindThem(&srcs, slst);
    
    	if (bottom == NULL) {
    		/* No known transformations -- use the first suffix found for
    		 * setting the local variables.  */
    		if (!Lst_IsEmpty(&targs))
    			targ = Lst_Datum(Lst_First(&targs));
    		else
    			targ = NULL;
    	} else {
    		/* Work up the transformation path to find the suffix of the
    		 * target to which the transformation was made.  */
    		for (targ = bottom; targ->parent != NULL; targ = targ->parent)
    			continue;
    	}
    
    	/* The .TARGET variable we always set to be the name at this point,
    	 * since it's only set to the path if the thing is only a source and
    	 * if it's only a source, it doesn't matter what we put here as far
    	 * as expanding sources is concerned, since it has none...	*/
    	Var(TARGET_INDEX, gn) = gn->name;
    
    	prefix = targ != NULL ? estrdup(targ->prefix) : gn->name;
    	Var(PREFIX_INDEX, gn) = prefix;
    
    	/* Now we've got the important local variables set, expand any sources
    	 * that still contain variables or wildcards in their names.  */
    	expand_all_children(gn);
    
    	if (targ == NULL) {
    		if (DEBUG(SUFF))
    			printf("\tNo valid suffix on %s\n", gn->name);
    
    sfnd_abort:
    		/* Deal with finding the thing on the default search path if the
    		 * node is only a source (not on the lhs of a dependency operator
    		 * or [XXX] it has neither children or commands).  */
    		if (OP_NOP(gn->type) ||
    		    (Lst_IsEmpty(&gn->children) &&
    		    Lst_IsEmpty(&gn->commands))) {
    			gn->path = Dir_FindFile(gn->name,
    			    (targ == NULL ? defaultPath :
    			    &targ->suff->searchPath));
    			if (gn->path != NULL) {
    				char *ptr;
    				Var(TARGET_INDEX, gn) = estrdup(gn->path);
    
    				if (targ != NULL) {
    					/* Suffix known for the thing -- trim
    					 * the suffix off the path to form the
    					 * proper .PREFIX variable.  */
    					int savep = strlen(gn->path) -
    					    targ->suff->nameLen;
    					char savec;
    
    					gn->suffix = targ->suff;
    
    					savec = gn->path[savep];
    					gn->path[savep] = '\0';
    
    					if ((ptr = strrchr(gn->path, '/')) !=
    					    NULL)
    						ptr++;
    					else
    						ptr = gn->path;
    
    					Var(PREFIX_INDEX, gn) = estrdup(ptr);
    
    					gn->path[savep] = savec;
    				} else {
    					/* The .PREFIX gets the full path if the
    					 * target has no known suffix.  */
    					gn->suffix = NULL;
    
    					if ((ptr = strrchr(gn->path, '/')) !=
    					    NULL)
    						ptr++;
    					else
    						ptr = gn->path;
    
    					Var(PREFIX_INDEX, gn) = estrdup(ptr);
    				}
    			}
    		} else {
    			/* Not appropriate to search for the thing -- set the
    			 * path to be the name so Dir_MTime won't go grovelling
    			 * for it.  */
    			gn->suffix = targ == NULL ? NULL : targ->suff;
    			free(gn->path);
    			gn->path = estrdup(gn->name);
    		}
    
    		goto sfnd_return;
    	}
    
    	/* Check for overriding transformation rule implied by sources.  */
    	if (!Lst_IsEmpty(&gn->children)) {
    		src = SuffFindCmds(targ, slst);
    
    		if (src != NULL) {
    			/* Free up all the Src structures in the transformation
    			 * path up to, but not including, the parent node.  */
    			while (bottom && bottom->parent != NULL) {
    				(void)Lst_AddNew(slst, bottom);
    				bottom = bottom->parent;
    			}
    			bottom = src;
    		}
    	}
    
    	if (bottom == NULL) {
    		/* No idea from where it can come -- return now.  */
    		goto sfnd_abort;
    	}
    
    	/* We now have a list of Src structures headed by 'bottom' and linked
    	 * via their 'parent' pointers. What we do next is create links between
    	 * source and target nodes (which may or may not have been created) and
    	 * set the necessary local variables in each target. The commands for
    	 * each target are set from the commands of the transformation rule
    	 * used to get from the src suffix to the targ suffix. Note that this
    	 * causes the commands list of the original node, gn, to be replaced by
    	 * the commands of the final transformation rule. Also, the children
    	 * to build field of gn is incremented.  Etc.  */
    	if (bottom->node == NULL) {
    		bottom->node = Targ_FindNode(bottom->file, TARG_CREATE);
    	}
    
    	for (src = bottom; src->parent != NULL; src = src->parent) {
    		targ = src->parent;
    
    		src->node->suffix = src->suff;
    
    		if (targ->node == NULL) {
    			targ->node = Targ_FindNode(targ->file, TARG_CREATE);
    		}
    
    		SuffApplyTransform(targ->node, src->node,
    				   targ->suff, src->suff);
    
    		if (targ->node != gn) {
    			/* Finish off the dependency-search process for any
    			 * nodes between bottom and gn (no point in questing
    			 * around the filesystem for their implicit source when
    			 * it's already known). Note that the node can't have
    			 * any sources that need expanding, since SuffFindThem
    			 * will stop on an existing node, so all we need to do
    			 * is set the standard and System V variables.  */
    			targ->node->type |= OP_DEPS_FOUND;
    
    			Var(PREFIX_INDEX, targ->node) = estrdup(targ->prefix);
    
    			Var(TARGET_INDEX, targ->node) = targ->node->name;
    		}
    	}
    
    	gn->suffix = src->suff;
    
    	/* So Dir_MTime doesn't go questing for it...  */
    	free(gn->path);
    	gn->path = estrdup(gn->name);
    
    	/* Nuke the transformation path and the Src structures left over in the
    	 * two lists.  */
    sfnd_return:
    	if (bottom)
    		(void)Lst_AddNew(slst, bottom);
    
    	while (SuffRemoveSrc(&srcs) || SuffRemoveSrc(&targs))
    		continue;
    
    	Lst_ConcatDestroy(slst, &srcs);
    	Lst_ConcatDestroy(slst, &targs);
    }
    
    
    /*-
     *-----------------------------------------------------------------------
     * Suff_FindDeps  --
     *	Find implicit sources for the target described by the graph node
     *	gn
     *
     * Side Effects:
     *	Nodes are added to the graph below the passed-in node. The nodes
     *	are marked to have their IMPSRC variable filled in. The
     *	PREFIX variable is set for the given node and all its
     *	implied children.
     *
     * Notes:
     *	The path found by this target is the shortest path in the
     *	transformation graph, which may pass through non-existent targets,
     *	to an existing target. The search continues on all paths from the
     *	root suffix until a file is found. I.e. if there's a path
     *	.o -> .c -> .l -> .l,v from the root and the .l,v file exists but
     *	the .c and .l files don't, the search will branch out in
     *	all directions from .o and again from all the nodes on the
     *	next level until the .l,v node is encountered.
     *-----------------------------------------------------------------------
     */
    
    void
    Suff_FindDeps(GNode *gn)
    {
    
    	SuffFindDeps(gn, &srclist);
    	while (SuffRemoveSrc(&srclist))
    		continue;
    }
    
    
    static void
    SuffFindDeps(GNode *gn, Lst slst)
    {
    	if (gn->type & OP_DEPS_FOUND) {
    		/*
    		 * If dependencies already found, no need to do it again...
    		 */
    		return;
    	} else {
    		gn->type |= OP_DEPS_FOUND;
    	}
    
    	if (DEBUG(SUFF))
    		printf("SuffFindDeps (%s)\n", gn->name);
    
    	current_node = gn;
    	if (gn->type & OP_ARCHV)
    		SuffFindArchiveDeps(gn, slst);
    	else
    		SuffFindNormalDeps(gn, slst);
    	current_node = NULL;
    }
    
    /*-
     *-----------------------------------------------------------------------
     * Suff_Init --
     *	Initialize suffixes module
     *
     * Side Effects:
     *	Many
     *-----------------------------------------------------------------------
     */
    void
    Suff_Init(void)
    {
    	Static_Lst_Init(&srclist);
    	ohash_init(&transforms, 4, &gnode_info);
    
    	/* Create null suffix for single-suffix rules (POSIX). The thing doesn't
    	 * actually go on the suffix list as it matches everything.  */
    	emptySuff = new_suffix("");
    	emptySuff->flags = SUFF_ACTIVE;
    	emptySuff->order = 0;
    	Dir_Concat(&emptySuff->searchPath, defaultPath);
    	ohash_init(&suffixes, 4, &suff_info);
    	special_path_hack();
    }
    
    
    /********************* DEBUGGING FUNCTIONS **********************/
    
    static void
    SuffPrintName(void *p)
    {
    	const Suff *s = p;
    	printf("%s ", s == emptySuff ? "<empty>" : s->name);
    }
    
    static void
    SuffPrintSuff(void *sp)
    {
    	Suff    *s = sp;
    
    	printf("# %-5s ", s->name);
    
    	if (!Lst_IsEmpty(&s->parents)) {
    		printf(" ->");
    		Lst_Every(&s->parents, SuffPrintName);
    	}
    	if (!Lst_IsEmpty(&s->children)) {
    		printf(" <-");
    		Lst_Every(&s->children, SuffPrintName);
    	}
    	fputc('\n', stdout);
    }
    
    static void
    SuffPrintTrans(GNode *t)
    {
    	printf("%-16s: ", t->name);
    	Targ_PrintType(t->type);
    	fputc('\n', stdout);
    	Lst_Every(&t->commands, Targ_PrintCmd);
    	fputc('\n', stdout);
    }
    
    static int 
    compare_order(const void *a, const void *b)
    {
    	const Suff **pa = (const Suff **)a;
    	const Suff **pb = (const Suff **)b;
    	return (*pb)->order - (*pa)->order;
    }
    
    static void
    print_path(Suff *s)
    {
    	/* do we need to print it ? compare against defaultPath */
    	LstNode ln1, ln2;
    	bool first = true;
    
    	for (ln1 = Lst_First(&s->searchPath), ln2 = Lst_First(defaultPath);
    	    ln1 != NULL && ln2 != NULL; 
    	    ln1 = Lst_Adv(ln1)) {
    		if (Lst_Datum(ln1) == Lst_Datum(ln2)) {
    			ln2 = Lst_Adv(ln2);
    			continue;
    		}
    		if (first) {
    			printf(".PATH%s:", s->name);
    			first = false;
    		}
    		printf(" %s", PathEntry_name(Lst_Datum(ln1)));
    	}
    	if (!first)
    		printf("\n\n");
    }
    
    void
    Suff_PrintAll(void)
    {
    	Suff **t;
    	GNode **u;
    	unsigned int i;
    	bool reprint;
    
    
    	printf("# Suffixes graph\n");
    	t = sort_ohash_by_name(&suffixes);
    	for (i = 0; t[i] != NULL; i++)
    		if (!(t[i]->flags & SUFF_PATH))
    			SuffPrintSuff(t[i]);
    
    	printf("\n.PATH: ");
    	Dir_PrintPath(defaultPath);
    	printf("\n\n");
    	for (i = 0; t[i] != NULL; i++)
    		if (!(t[i]->flags & SUFF_PATH))
    			print_path(t[i]);
    	free(t);
    
    	reprint = false;
    	t = sort_ohash(&suffixes, compare_order);
    	printf(".SUFFIXES:");
    	for (i = 0; t[i] != NULL; i++) {
    		if (t[i]->flags & SUFF_PATH)
    			continue;
    		printf(" %s", t[i]->name);
    		if (!(t[i]->flags & SUFF_ACTIVE))
    			reprint = true;
    	}
    	printf("\n\n");
    	u = sort_ohash_by_name(&transforms);
    	for (i = 0; u[i] != NULL; i++)
    	    	SuffPrintTrans(u[i]);
    	free(u);
    
    	if (reprint) {
    		printf(".SUFFIXES:");
    		for (i = 0; t[i] != NULL; i++)
    			if (t[i]->flags & SUFF_ACTIVE)
    				printf(" %s", t[i]->name);
    		printf("\n");
    	}
    	free(t);
    }
    
    #ifdef DEBUG_SRC
    static void
    PrintAddr(void *a)
    {
    	printf("%lx ", (unsigned long)a);
    }
    #endif