Пример #1
0
void checksum_update(TARGET *t, MD5SUM buildmd5sum)
{
	CHECKSUMDATA cachedata, *c = &cachedata;
	char buildmd5sumstring[33];

	/* If the buildmd5sum is empty, then the file doesn't exist. */
	if (ismd5empty(buildmd5sum))
		return;

	/* if the target is available in the cache */
	strcpy(buildmd5sumstring, md5tostring(buildmd5sum));
	c->boundname = buildmd5sumstring;

	/* Search for the appropriate .link file that matches the target. */
	if (!hashcheck(checksumhash, (HASHDATA **)&c)) {
		if (hashenter(checksumhash, (HASHDATA **)&c)) {
			c->boundname = newstr( c->boundname );
			c->next = checksumdatalist;
			checksumdatalist = c;
		}
	}

	memcpy(&c->contentmd5sum, t->contentchecksum->contentmd5sum, MD5_SUMSIZE);
	c->mtime = 1;
	c->age = 0;
	checksumsdirty = 1;
}
Пример #2
0
void profile_enter( char * rulename, profile_frame * frame )
{
    if ( DEBUG_PROFILE )
    {
        clock_t start = clock();
        profile_info info;
        profile_info * p = &info;

        if ( !rulename ) p = &profile_other;

        if ( !profile_hash && rulename )
            profile_hash = hashinit( sizeof( profile_info ), "profile" );

        info.name = rulename;

        if ( rulename && hashenter( profile_hash, (HASHDATA * *)&p ) )
            p->cumulative = p->net = p->num_entries = p->stack_count = p->memory = 0;

        ++p->num_entries;
        ++p->stack_count;

        frame->info = p;

        frame->caller = profile_stack;
        profile_stack = frame;

        frame->entry_time = clock();
        frame->overhead = 0;
        frame->subrules = 0;

        /* caller pays for the time it takes to play with the hash table */
        if ( frame->caller )
            frame->caller->overhead += frame->entry_time - start;
    }
}
Пример #3
0
const char *
newstr( const char *string )
{
	STRING str, *s = &str;

	if( !strhash )
	    strhash = hashinit( sizeof( STRING ), "strings" );

	*s = string;

	if( hashenter( strhash, (HASHDATA **)&s ) )
	{
	    int l = strlen( string );
#if 1
		if (!stralloc)
			stralloc = alloc2_init(4096);
		char *m = alloc2_enter(stralloc, l + 1);
#else
	    char *m = (char *)malloc( l + 1 );
#endif
	    if (DEBUG_MEM)
		    printf("newstr: allocating %d bytes\n", l + 1 );

	    strtotal += l + 1;
	    memcpy( m, string, l + 1 );
	    *s = m;
	}

	return *s;
}
Пример #4
0
void declare_native_rule( const char * module, const char * rule, const char * * args,
                          LIST * (*f)( FRAME *, int ), int version )
{
    OBJECT * module_obj = 0;
    module_t * m;
    if ( module )
    {
        module_obj = object_new( module );
    }
    m = bindmodule( module_obj );
    if ( module_obj )
    {
        object_free( module_obj );
    }
    if (m->native_rules == 0) {
        m->native_rules = hashinit( sizeof( native_rule_t ), "native rules");
    }

    {
        native_rule_t n, *np = &n;
        n.name = object_new( rule );
        if (args)
        {
            n.arguments = args_new();
            lol_build( n.arguments->data, args );
        }
        else
        {
            n.arguments = 0;
        }
        n.procedure = function_builtin( f, 0 );
        n.version = version;
        hashenter(m->native_rules, (HASHDATA**)&np);
    }
}
Пример #5
0
OBJECT * path_as_key( OBJECT * path )
{
    struct path_key_entry e, *result = &e;

    if ( ! path_key_cache )
        path_key_cache = hashinit( sizeof( struct path_key_entry ), "path to key" );

    result->path = path;
    if ( hashenter( path_key_cache, (HASHDATA * *)&result ) )
    {
        string buf[1];
        char * s;
        string_copy( buf, object_str( path ) );
        for ( s = buf->value; s < buf->value + buf->size; ++s )
        {
            if ( *s == '/' )
                *s = '\\';
            else
                *s = tolower( *s );
        }
        result->path = object_copy( path );
        result->key = object_new( buf->value );
        string_free( buf );
    }

    return result->key;
}
Пример #6
0
module_t * bindmodule( OBJECT * name )
{

    if ( !name )
    {
        return &root;
    }
    else
    {
        PROFILE_ENTER( BINDMODULE );

        module_t m_;
        module_t * m = &m_;

        if ( !module_hash )
            module_hash = hashinit( sizeof( module_t ), "modules" );

        m->name = name;

        if ( hashenter( module_hash, (HASHDATA * *)&m ) )
        {
            m->name = object_copy( name );
            m->variables = 0;
            m->rules = 0;
            m->imported_modules = 0;
            m->class_module = 0;
            m->native_rules = 0;
            m->user_module = 0;
        }

        PROFILE_EXIT( BINDMODULE );

        return m;
    }
}
Пример #7
0
file_info_t * file_info( OBJECT * filename )
{
    file_info_t *finfo = &filecache_finfo;

    if ( !filecache_hash )
        filecache_hash = hashinit( sizeof( file_info_t ), "file_info" );

    filename = path_as_key( filename );

    finfo->name = filename;
    finfo->is_file = 0;
    finfo->is_dir = 0;
    finfo->size = 0;
    finfo->time = 0;
    finfo->files = 0;
    if ( hashenter( filecache_hash, (HASHDATA**)&finfo ) )
    {
        /* printf( "file_info: %s\n", filename ); */
        finfo->name = object_copy( finfo->name );
    }

    object_free( filename );

    return finfo;
}
Пример #8
0
void setcachedmd5sum( TARGET *t )
{
	CHECKSUMDATA checksumdata, *c = &checksumdata;
	const char *target = t->boundname;
# ifdef DOWNSHIFT_PATHS
	char path[ MAXJPATH ];
	char *p = path;

	do *p++ = (char)tolower( *target ); while( *target++ );

	target = path;
# endif

	c->boundname = target;

	if( !hashcheck( checksumhash, (HASHDATA **) &c ) )
	{
		if( hashenter( checksumhash, (HASHDATA **)&c ) ) {
			c->boundname = newstr( c->boundname );
			c->next = checksumdatalist;
			checksumdatalist = c;
		}
	}

	memcpy(c->contentmd5sum, t->contentmd5sum, sizeof(MD5SUM));
	c->mtime = t->time;
	c->age = 0;
	checksumsdirty = 1;
}
Пример #9
0
static void bind_explicitly_located_target(void* xtarget, void* data)
{
    TARGET* t = (TARGET*)xtarget;
    if (! (t->flags & T_FLAG_NOTFILE) )
    {
        /* Check if there's a setting for LOCATE_TARGET */
        SETTINGS* s = t->settings;
        for(; s ; s = s->next)
        {            
            if (strcmp(s->symbol, "LOCATE") == 0) 
            {
                pushsettings(t->settings);
                t->boundname = search( t->name, &t->time );
                t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
                popsettings(t->settings);

                {
                    LOCATED_TARGET lt, *lta = &lt;
                    lt.file_name = t->boundname;
                    lt.target = t;
                    if (!located_targets)
                        located_targets = hashinit( sizeof(LOCATED_TARGET),
                                                    "located targets" );

                    /* TODO: should check if we've entered the item or not. */
                    hashenter(located_targets, (HASHDATA **)&lta);
                }

                break;
            }
        }
    }
}
Пример #10
0
static void profile_enter( char* rulename, profile_frame* frame )
{
    clock_t start = clock();
    profile_info info, *p = &info;
    
    if ( !profile_hash )
        profile_hash = hashinit(sizeof(profile_info), "profile");

    info.name = rulename;
    
    if ( hashenter( profile_hash, (HASHDATA **)&p ) )
        p->cumulative = p->net = p->num_entries = p->stack_count = 0;

    ++(p->num_entries);
    ++(p->stack_count);
    
    frame->info = p;
    
    frame->caller = profile_stack;
    profile_stack = frame;

    frame->entry_time = clock();
    frame->overhead = 0;
    frame->subrules = 0;

    /* caller pays for the time it takes to play with the hash table */
    if ( frame->caller )
        frame->caller->overhead += frame->entry_time - start;
}
Пример #11
0
RULE *
bindrule( const char *rulename )
{
	RULE rule, *r = &rule;

	if( !rulehash )
	    rulehash = hashinit( sizeof( RULE ), "rules" );

	r->name = rulename;

	if( hashenter( rulehash, (HASHDATA **)&r ) )
	{
	    r->name = newstr( rulename );	/* never freed */
	    r->procedure = (PARSE *)0;
	    r->actions = (char *)0;
	    r->bindlist = L0;
	    r->params = L0;
	    r->flags = 0;

# ifdef OPT_RULE_PROFILING_EXT
		if ( DEBUG_PROFILE_RULES )
		{
			r->invocations = 0;
			r->invocation_time = 0;
			r->next_profiling_rule = profiling_rule_list;
			profiling_rule_list = r;
		}
# endif
	}

	return r;
}
Пример #12
0
static void
time_enter( 
	void	*closure,
	char	*target,
	int	found,
	time_t	time )
{
	BINDING	binding, *b = &binding;
	struct hash *bindhash = (struct hash *)closure;

# ifdef DOWNSHIFT_PATHS
	char path[ MAXJPATH ];
	char *p = path;

	do *p++ = tolower( *target );
	while( *target++ );

	target = path;
# endif

	b->name = target;
	b->flags = 0;

	if( hashenter( bindhash, (HASHDATA **)&b ) )
	    b->name = newstr( target );		/* never freed */

	b->time = time;
	b->progress = found ? BIND_FOUND : BIND_SPOTTED;

	if( DEBUG_BINDSCAN )
	    printf( "time ( %s ) : %s\n", target, time_progress[b->progress] );
}
Пример #13
0
int hcache_getrulemd5sum( TARGET *t )
{
	HCACHEDATA cachedata, *c = &cachedata;
	const char *target = t->name;
# ifdef DOWNSHIFT_PATHS
	char path[ MAXJPATH ];
	char *p;
# endif

	HCACHEFILE	*file = hcachefile_get( t );
	if ( !file->cachefilename )
		return 1;

# ifdef DOWNSHIFT_PATHS
	p = path;

	do *p++ = (char)tolower( *target );
	while( *target++ );

	target = path;
# endif

	c->boundname = target;

	if( hashcheck( hcachehash, (HASHDATA **) &c ) )
	{
		if( memcmp( &c->currentrulemd5sum, &t->rulemd5sum, MD5_SUMSIZE ) == 0 )
			return 1;

		memcpy( &c->currentrulemd5sum, &t->rulemd5sum, MD5_SUMSIZE );
	}
	else
	{
		// Enter it into the cache.
		if( hashenter( hcachehash, (HASHDATA **)&c ) )
		{
			c->boundname = newstr( c->boundname );
			c->file = file;
			c->next = c->file->hcachelist;
			c->file->hcachelist = c;
			c->mtime = 0;
			memcpy( &c->currentrulemd5sum, &t->rulemd5sum, MD5_SUMSIZE );
			memset( &c->rulemd5sum, 0, MD5_SUMSIZE );
			memset( &c->currentcontentmd5sum, 0, MD5_SUMSIZE );
			memset( &c->contentmd5sum, 0, MD5_SUMSIZE );
			c->time = 0;
			c->age = 0;
			c->includes = NULL;
			c->hdrscan = NULL;
		}
	}

	c->file->dirty = 1;

	return 0;
}
Пример #14
0
void
macro_headers( TARGET *t )
{
    static regexp *re = 0;
    FILE    *f;
    char    buf[ 1024 ];

    if ( DEBUG_HEADER )
        printf( "macro header scan for %s\n", t->name );

    /* this regexp is used to detect lines of the form       */
    /* "#define  MACRO  <....>" or "#define  MACRO  "....."  */
    /* in the header macro files..                           */
    if ( re == 0 )
    {
        re = regex_compile(
            "^[     ]*#[    ]*define[   ]*([A-Za-z][A-Za-z0-9_]*)[  ]*"
            "[<\"]([^\">]*)[\">].*$" );
    }

    if ( !( f = fopen( t->boundname, "r" ) ) )
        return;

    while ( fgets( buf, sizeof( buf ), f ) )
    {
        HEADER_MACRO var;
        HEADER_MACRO *v = &var;

        if ( regexec( re, buf ) && re->startp[1] )
        {
            /* we detected a line that looks like "#define  MACRO  filename */
            re->endp[1][0] = '\0';
            re->endp[2][0] = '\0';

            if ( DEBUG_HEADER )
                printf( "macro '%s' used to define filename '%s' in '%s'\n",
                        re->startp[1], re->startp[2], t->boundname );

            /* add macro definition to hash table */
            if ( !header_macros_hash )
                header_macros_hash = hashinit( sizeof( HEADER_MACRO ), "hdrmacros" );

            v->symbol   = re->startp[1];
            v->filename = 0;
            if ( hashenter( header_macros_hash, (HASHDATA **)&v ) )
            {
                v->symbol   = newstr( re->startp[1] );  /* never freed */
                v->filename = newstr( re->startp[2] );  /* never freed */
            }
            /* XXXX: FOR NOW, WE IGNORE MULTIPLE MACRO DEFINITIONS !! */
            /*       WE MIGHT AS WELL USE A LIST TO STORE THEM..      */
        }
    }

    fclose( f );
}
Пример #15
0
regexp* regex_compile( const char* pattern )
{
    regex_entry entry, *e = &entry;
    entry.pattern = pattern;

    if ( !regex_hash )
        regex_hash = hashinit(sizeof(regex_entry), "regex");

    if ( hashenter( regex_hash, (HASHDATA **)&e ) )
        e->regex = regcomp( (char*)pattern );

    return e->regex;
}
Пример #16
0
HEADER *headersDepth(const char *t, time_t time, int depth)
{
	HEADER hdr, *h = &hdr;
	LIST *l;
	const char* cachekey=t;

	/* D support (doesn't affect C(++), because a source file is never included) */
	if(depth == 0)
	{
		cachekey=malloc(strlen(t)+sizeof("source:"));
		strcpy((char*)cachekey,"source:");
		strcpy((char*)cachekey+7,t);
	}
	
	if (!headerhash)
		headerhash = hashinit(sizeof(HEADER), "headers");

	h->key = cachekey;
	h->includes = 0;
	h->time = time;
	h->headers = 0;
	h->newest = 0;
	if (!hashenter(headerhash, (HASHDATA **)&h))
		return h;

	h->key = newstr(t);
#ifdef USE_CACHE
	if (!cache_check(cachekey, time, &h->includes))
	{
		h->includes = headers1(t, depth);
		cache_enter(cachekey, time, h->includes);
	}
#else
	h->includes = headers1(t, depth);
#endif
	if(depth == 0)
		free((char*)cachekey);

	l = h->includes;
	while (l)
	{
		const char *t2 = search(t, l->string, &time);
		if (time)
			h->headers = headerentry(h->headers, headersDepth(t2, time, depth+1));
		l = list_next(l);
	}

	return h;
}
Пример #17
0
/*!	\brief Adds a jcache_entry to a jamfile_cache.
	\param cache The jamfile_cache.
	\param entry The jcache_entry to be added.
	\return \c !0, if everything went fine, 0, if an error occured (out of
			memory).
*/
static
int
add_jcache_entry(jamfile_cache* cache, jcache_entry* entry)
{
	int result = 0;
	if (cache && entry) {
		result = push_string(cache->filenames, entry->filename);
		if (result) {
			result = hashenter(cache->entries, (HASHDATA**)&entry);
			if (!result)
				pop_string(cache->filenames);
		}
	}
	return result;
}
Пример #18
0
static VARIABLE *
var_enter( char	*symbol )
{
	VARIABLE var, *v = &var;

	if( !varhash )
	    varhash = hashinit( sizeof( VARIABLE ), "variables" );

	v->symbol = symbol;
	v->value = 0;

	if( hashenter( varhash, (HASHDATA **)&v ) )
	    v->symbol = newstr( symbol );	/* never freed */

	return v;
}
Пример #19
0
static VARIABLE * var_enter( OBJECT * symbol )
{
    VARIABLE var;
    VARIABLE * v = &var;

    if ( !varhash )
        varhash = hashinit( sizeof( VARIABLE ), "variables" );

    v->symbol = symbol;
    v->value = 0;

    if ( hashenter( varhash, (HASHDATA * *)&v ) )
        v->symbol = object_copy( symbol );

    return v;
}
Пример #20
0
/*
 * Set cached md5sum of a file.
 */
void setcachedmd5sum( TARGET *t )
{
	HCACHEDATA cachedata, *c = &cachedata;
	const char *target = t->boundname;
# ifdef DOWNSHIFT_PATHS
	char path[ MAXJPATH ];
	char *p;
#endif

	HCACHEFILE	*file = hcachefile_get( t );

# ifdef DOWNSHIFT_PATHS
	p = path;

	do *p++ = (char)tolower( *target );
	while( *target++ );

	target = path;
# endif

	c->boundname = target;

	if( !hashcheck( hcachehash, (HASHDATA **) &c ) )
	{
		if( hashenter( hcachehash, (HASHDATA **)&c ) ) {
			c->boundname = newstr( c->boundname );
			c->file = file;
			c->next = c->file->hcachelist;
			c->file->hcachelist = c;
			c->time = t->time;
			c->includes = NULL;
			c->hdrscan = NULL;
		}
	}

	c->file->dirty = 1;

	/* 'c' points at the cache entry.  Its out of date. */

	memcpy(c->rulemd5sum, t->rulemd5sum, sizeof(MD5SUM));
	memcpy(c->currentrulemd5sum, t->rulemd5sum, sizeof(MD5SUM));
	memcpy(c->contentmd5sum, t->contentmd5sum, sizeof(MD5SUM));
	memcpy(c->currentcontentmd5sum, t->contentmd5sum, sizeof(MD5SUM));
	c->mtime = t->time;
	c->age = 0;
}
Пример #21
0
const char *
newstr( const char *string )
{
	STRING str, *s = &str;

	if( !strhash )
	    strhash = hashinit( sizeof( STRING ), "strings" );

	*s = string;

	if( hashenter( strhash, (HASHDATA **)&s ) )
	{
	    size_t l = strlen( string );
#ifdef OPT_IMPROVED_MEMUSE_EXT
	    char *m;

	    if (!str_allocbuf || (str_allocbuf + l + 1) >= str_allocend) {
		str_allocbuf = malloc( l + 1 );
		str_allocend = str_allocbuf + l + 1;
#ifdef OPT_DEBUG_MEM_TOTALS_EXT
		++str_allocs;
#endif
	    }
	    m = str_allocbuf;
	    str_allocbuf += (l + 1);
#else
	    char *m = (char *)malloc( l + 1 );

	    if (DEBUG_MEM)
		    printf("newstr: allocating %d bytes\n", l + 1 );
#endif

	    strtotal += l + 1;
#ifdef OPT_DEBUG_MEM_TOTALS_EXT
#ifndef OPT_IMPROVED_MEMUSE_EXT
	    ++str_allocs;
#endif
#endif
	    memcpy( m, string, l + 1 );
	    *s = m;
	}

	return *s;
}
Пример #22
0
TARGET * bindtarget( OBJECT * target_name )
{
    TARGET target;
    TARGET * t = &target;

    if ( !targethash )
        targethash = hashinit( sizeof( TARGET ), "targets" );

    t->name = target_name;

    if ( hashenter( targethash, (HASHDATA * *)&t ) )
    {
        memset( (char *)t, '\0', sizeof( *t ) );
        t->name = object_copy( target_name );
        t->boundname = object_copy( t->name );  /* default for T_FLAG_NOTFILE */
    }

    return t;
}
Пример #23
0
TARGET *
bindtarget( char *targetname )
{
	TARGET target, *t = &target;

	if( !targethash )
	    targethash = hashinit( sizeof( TARGET ), "targets" );

	t->name = targetname;

	if( hashenter( targethash, (HASHDATA **)&t ) )
	{
	    memset( (char *)t, '\0', sizeof( *t ) );
	    t->name = newstr( targetname );	/* never freed */
	    t->boundname = t->name;		/* default for T_FLAG_NOTFILE */
	}

	return t;
}
Пример #24
0
/*
 * enter_rule() - return pointer to RULE, creating it if necessary in
 * target_module.
 */
static RULE *
enter_rule( char *rulename, module *target_module )
{
    RULE rule, *r = &rule;

    r->name = rulename;

    if ( hashenter( target_module->rules, (HASHDATA **)&r ) )
    {
        r->name = newstr( rulename );	/* never freed */
        r->procedure = (PARSE *)0;
        r->module = 0;
        r->actions = 0;
        r->arguments = 0;
        r->exported = 0;
        r->module = target_module;
    }
    return r;
}
Пример #25
0
OBJECT * make_class_module( LIST * xname, LIST * bases, FRAME * frame )
{
    OBJECT     * name = class_module_name( xname->value );
    OBJECT   * * pp = &xname->value;
    module_t   * class_module = 0;
    module_t   * outer_module = frame->module;
    OBJECT     * name_ = object_new( "__name__" );
    OBJECT     * bases_ = object_new( "__bases__" );

    if ( !classes )
        classes = hashinit( sizeof( OBJECT * ), "classes" );

    if ( hashenter( classes, (HASHDATA * *)&pp ) )
    {
        *pp = object_copy( xname->value );
    }
    else
    {
        printf( "Class %s already defined\n", object_str( xname->value ) );
        abort();
    }
    check_defined( bases );

    class_module = bindmodule( name );

    exit_module( outer_module );
    enter_module( class_module );

    var_set( name_, xname, VAR_SET );
    var_set( bases_, bases, VAR_SET );

    exit_module( class_module );
    enter_module( outer_module );

    for ( ; bases; bases = bases->next )
        import_base_rules( class_module, bases->value );

    object_free( bases_ );
    object_free( name_ );

    return name;
}
Пример #26
0
static HDR *hdr_enter(const char *file)
{
	HDR hdr, *h = &hdr;

	if (!hdrhash)
		hdrhash = hashinit(sizeof (HDR), "headers");

	h->file = file;
	h->time = 0;
	h->includes = 0;
	h->next = 0;
	h->tail = 0;

	if (hashenter(hdrhash, (HASHDATA **)&h))
	{
		h->file = newstr(file);	/* never freed */
		h->tail = h;
		hdrlist = hdr_append(hdrlist, h);
	}

	return h;
}
Пример #27
0
static RULE * enter_rule( OBJECT * rulename, module_t * target_module )
{
    RULE rule;
    RULE * r = &rule;

    r->name = rulename;

    if ( hashenter( demand_rules( target_module ), (HASHDATA * *)&r ) )
    {
        r->name = object_copy( rulename );
        r->procedure = 0;
        r->module = 0;
        r->actions = 0;
        r->arguments = 0;
        r->exported = 0;
        r->module = target_module;
#ifdef HAVE_PYTHON
        r->python_function = 0;
#endif
    }
    return r;
}
Пример #28
0
void import_module( LIST * module_names, module_t * target_module )
{
    PROFILE_ENTER( IMPORT_MODULE );

    struct hash * h;

    if ( !target_module->imported_modules )
        target_module->imported_modules = hashinit( sizeof( char * ), "imported" );
    h = target_module->imported_modules;

    for ( ; module_names; module_names = module_names->next )
    {
        OBJECT * s = module_names->value;
        OBJECT * * ss = &s;
        if( hashenter( h, (HASHDATA * *)&ss ) )
        {
            *ss = object_copy( s );
        }
    }

    PROFILE_EXIT( IMPORT_MODULE );
}
Пример #29
0
/*
 * enter_rule() - return pointer to RULE, creating it if necessary in
 * target_module.
 */
static RULE *
enter_rule( char *rulename, module_t *target_module )
{
    RULE rule, *r = &rule;

    r->name = rulename;

    if ( hashenter( demand_rules( target_module ), (HASHDATA **)&r ) )
    {
        r->name = newstr( rulename );	/* never freed */
        r->procedure = (PARSE *)0;
        r->module = 0;
        r->actions = 0;
        r->arguments = 0;
        r->exported = 0;
        r->module = target_module;
#ifdef HAVE_PYTHON
        r->python_function = 0;
#endif
    }
    return r;
}
Пример #30
0
OBJECT *
search(
    OBJECT * target,
    time_t *time,
    OBJECT * * another_target,
    int file
)
{
    PATHNAME f[1];
    LIST   * varlist;
    string   buf[1];
    int      found = 0;
    /* Will be set to 1 if target location is specified via LOCATE. */
    int      explicitly_located = 0;
    OBJECT * boundname = 0;
    OBJECT * varname;

    if ( another_target )
        *another_target = 0;

    if (! explicit_bindings )
        explicit_bindings = hashinit( sizeof(BINDING),
                                      "explicitly specified locations");

    string_new( buf );
    /* Parse the filename */

    path_parse( object_str( target ), f );

    f->f_grist.ptr = 0;
    f->f_grist.len = 0;

    varname = object_new( "LOCATE" );
    varlist = var_get( varname );
    object_free( varname );
    if ( varlist )
    {
        OBJECT * key;
        f->f_root.ptr = object_str( varlist->value );
        f->f_root.len = strlen( object_str( varlist->value ) );

        path_build( f, buf, 1 );

        if ( DEBUG_SEARCH )
            printf( "locate %s: %s\n", object_str( target ), buf->value );

        explicitly_located = 1;

        key = object_new( buf->value );
        timestamp( key, time );
        object_free( key );
        found = 1;
    }
    else if ( ( varname = object_new( "SEARCH" ),
                varlist = var_get( varname ),
                object_free( varname ),
                varlist ) )
    {
        while ( varlist )
        {
            BINDING b, *ba = &b;
            file_info_t *ff;
            OBJECT * key;
            OBJECT * test_path;

            f->f_root.ptr = object_str( varlist->value );
            f->f_root.len = strlen( object_str( varlist->value ) );

            string_truncate( buf, 0 );
            path_build( f, buf, 1 );

            if ( DEBUG_SEARCH )
                printf( "search %s: %s\n", object_str( target ), buf->value );

            test_path = object_new( buf->value );
            key = path_as_key( test_path );
            object_free( test_path );
            ff = file_query( key );
            timestamp( key, time );

            b.binding = key;

            if ( hashcheck( explicit_bindings, (HASHDATA**)&ba ) )
            {
                if ( DEBUG_SEARCH )
                    printf(" search %s: found explicitly located target %s\n",
                           object_str( target ), object_str( ba->target ) );
                if ( another_target )
                    *another_target = ba->target;
                found = 1;
                object_free( key );
                break;
            }
            else if ( ff && ff->time )
            {
                if ( !file || ff->is_file )
                {
                    found = 1;
                    object_free( key );
                    break;
                }
            }
            object_free( key );

            varlist = list_next( varlist );
        }
    }

    if ( !found )
    {
        /* Look for the obvious */
        /* This is a questionable move.  Should we look in the */
        /* obvious place if SEARCH is set? */
        OBJECT * key;

        f->f_root.ptr = 0;
        f->f_root.len = 0;

        string_truncate( buf, 0 );
        path_build( f, buf, 1 );

        if ( DEBUG_SEARCH )
            printf( "search %s: %s\n", object_str( target ), buf->value );

        key = object_new( buf->value );
        timestamp( key, time );
        object_free( key );
    }

    boundname = object_new( buf->value );
    string_free( buf );

    if ( explicitly_located )
    {
        BINDING b;
        BINDING * ba = &b;
        OBJECT * key = path_as_key( boundname );
        b.binding = key;
        b.target = target;
        /* CONSIDER: we probably should issue a warning is another file
           is explicitly bound to the same location. This might break
           compatibility, though. */
        if ( !hashenter( explicit_bindings, (HASHDATA * *)&ba ) )
        {
            object_free( key );
        }
    }

    /* prepare a call to BINDRULE if the variable is set */
    call_bind_rule( target, boundname );

    return boundname;
}