/*
 * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.
 *
 * This file is part of Jam - see jam.c for Copyright information.
 */

/*  This file is ALSO:
 *  Copyright 2001-2004 David Abrahams.
 *  Distributed under the Boost Software License, Version 1.0.
 *  (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 */

# include "jam.h"

# include "hash.h"
# include "filesys.h"
# include "pathsys.h"
# include "timestamp.h"
# include "newstr.h"
# include "strings.h"

/*
 * timestamp.c - get the timestamp of a file or archive member
 *
 * 09/22/00 (seiwald) - downshift names on OS2, too
 */

/*
 * BINDING - all known files
 */

typedef struct _binding BINDING;

struct _binding {
    char    *name;
    short   flags;

# define BIND_SCANNED   0x01    /* if directory or arch, has been scanned */

    short   progress;

# define BIND_INIT  0   /* never seen */
# define BIND_NOENTRY   1   /* timestamp requested but file never found */
# define BIND_SPOTTED   2   /* file found but not timed yet */
# define BIND_MISSING   3   /* file found but can't get timestamp */
# define BIND_FOUND 4   /* file found and time stamped */

    time_t  time;       /* update time - 0 if not exist */
};

static struct hash * bindhash = 0;
static void time_enter( void *, char *, int, time_t );

static char * time_progress[] =
{
    "INIT",
    "NOENTRY",
    "SPOTTED",
    "MISSING",
    "FOUND"
};


/*
 * timestamp() - return timestamp on a file, if present.
 */

void timestamp( char * target, time_t * time )
{
    PROFILE_ENTER( timestamp );

    PATHNAME   f1;
    PATHNAME   f2;
    BINDING    binding;
    BINDING  * b = &binding;
    string     buf[ 1 ];
#ifdef DOWNSHIFT_PATHS
    string     path;
    char     * p;
#endif

#ifdef DOWNSHIFT_PATHS
    string_copy( &path, target );
    p = path.value;

    do
    {
        *p = tolower( *p );
#ifdef NT
        /* On NT, we must use backslashes or the file will not be found. */
        if ( *p == '/' )
            *p = PATH_DELIM;
#endif
    }
    while ( *p++ );

    target = path.value;
#endif  /* #ifdef DOWNSHIFT_PATHS */
    string_new( buf );

    if ( !bindhash )
        bindhash = hashinit( sizeof( BINDING ), "bindings" );

    /* Quick path - is it there? */
    b->name = target;
    b->time = b->flags = 0;
    b->progress = BIND_INIT;

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

    if ( b->progress != BIND_INIT )
        goto afterscanning;

    b->progress = BIND_NOENTRY;

    /* Not found - have to scan for it. */
    path_parse( target, &f1 );

    /* Scan directory if not already done so. */
    {
        BINDING binding;
        BINDING * b = &binding;

        f2 = f1;
        f2.f_grist.len = 0;
        path_parent( &f2 );
        path_build( &f2, buf, 0 );

        b->name = buf->value;
        b->time = b->flags = 0;
        b->progress = BIND_INIT;

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

        if ( !( b->flags & BIND_SCANNED ) )
        {
            file_dirscan( buf->value, time_enter, bindhash );
            b->flags |= BIND_SCANNED;
        }
    }

    /* Scan archive if not already done so. */
    if ( f1.f_member.len )
    {
        BINDING binding;
        BINDING * b = &binding;

        f2 = f1;
        f2.f_grist.len = 0;
        f2.f_member.len = 0;
        string_truncate( buf, 0 );
        path_build( &f2, buf, 0 );

        b->name = buf->value;
        b->time = b->flags = 0;
        b->progress = BIND_INIT;

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

        if ( !( b->flags & BIND_SCANNED ) )
        {
            file_archscan( buf->value, time_enter, bindhash );
            b->flags |= BIND_SCANNED;
        }
    }

    afterscanning:

    if ( b->progress == BIND_SPOTTED )
    {
         b->progress = file_time( b->name, &b->time ) < 0
            ? BIND_MISSING
            : BIND_FOUND;
    }

    *time = b->progress == BIND_FOUND ? b->time : 0;
        string_free( buf );
#ifdef DOWNSHIFT_PATHS
    string_free( &path );
#endif

    PROFILE_EXIT( timestamp );
}


static void time_enter( void * closure, char * target, int found, time_t time )
{
    BINDING 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 ] );
}


/*
 * stamps_done() - free timestamp tables.
 */

void stamps_done()
{
    hashdone( bindhash );
}