/*
 * Copyright 1993, 2000 Christopher Seiwald.
 *
 * 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 "lists.h"
# include "parse.h"
# include "compile.h"
# include "rules.h"
# include "variable.h"
# include "regexp.h"
# include "headers.h"
# include "hdrmacro.h"
# include "newstr.h"

#ifdef OPT_HEADER_CACHE_EXT
# include "hcache.h"
#endif

/*
 * headers.c - handle #includes in source files
 *
 * Using regular expressions provided as the variable $(HDRSCAN),
 * headers() searches a file for #include files and phonies up a
 * rule invocation:
 *
 *  $(HDRRULE) <target> : <include files> ;
 *
 * External routines:
 *    headers() - scan a target for include files and call HDRRULE
 *
 * Internal routines:
 *    headers1() - using regexp, scan a file and build include LIST
 *
 * 04/13/94 (seiwald) - added shorthand L0 for null list pointer
 * 09/10/00 (seiwald) - replaced call to compile_rule with evaluate_rule,
 *      so that headers() doesn't have to mock up a parse structure
 *      just to invoke a rule.
 */

#ifndef OPT_HEADER_CACHE_EXT
static LIST *headers1( LIST *l, char *file, int rec, regexp *re[]);
#endif

/*
 * headers() - scan a target for include files and call HDRRULE
 */

# define MAXINC 10

void
headers( TARGET *t )
{
    LIST   * hdrscan;
    LIST   * hdrrule;
	#ifndef OPT_HEADER_CACHE_EXT
    LIST   * headlist = 0;
	#endif
    regexp * re[ MAXINC ];
    int rec = 0;

    if ( !( hdrscan = var_get( "HDRSCAN" ) ) ||
        !( hdrrule = var_get( "HDRRULE" ) ) )
        return;

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

    /* Compile all regular expressions in HDRSCAN */
    while ( ( rec < MAXINC ) && hdrscan )
    {
        re[ rec++ ] = regex_compile( hdrscan->string );
        hdrscan = list_next( hdrscan );
    }

    /* Doctor up call to HDRRULE rule */
    /* Call headers1() to get LIST of included files. */
    {
        FRAME   frame[1];
        frame_init( frame );
        lol_add( frame->args, list_new( L0, t->name ) );
#ifdef OPT_HEADER_CACHE_EXT
        lol_add( frame->args, hcache( t, rec, re, hdrscan ) );
#else
        lol_add( frame->args, headers1( headlist, t->boundname, rec, re ) );
#endif

        if ( lol_get( frame->args, 1 ) )
        {
            /* The third argument to HDRRULE is the bound name of
             * $(<) */
            lol_add( frame->args, list_new( L0, t->boundname ) );

            list_free( evaluate_rule( hdrrule->string, frame ) );
        }

        /* Clean up. */
        frame_free( frame );
    }
}


/*
 * headers1() - using regexp, scan a file and build include LIST.
 */

#ifdef OPT_HEADER_CACHE_EXT
LIST *
#else
static LIST *
#endif
headers1(
    LIST    * l,
    char    * file,
    int rec,
    regexp  * re[] )
{
    FILE * f;
    char buf[ 1024 ];
    int  i;
    static regexp * re_macros = 0;

#ifdef OPT_IMPROVED_PATIENCE_EXT
    static int count = 0;
    ++count;
    if ( ((count == 100) || !( count % 1000 )) && DEBUG_MAKE )
        printf("...patience...\n");
#endif

    /* the following regexp is used to detect cases where a  */
    /* file is included through a line line "#include MACRO" */
    if ( re_macros == 0 )
        re_macros = regex_compile(
            "^[     ]*#[    ]*include[  ]*([A-Za-z][A-Za-z0-9_]*).*$" );

    if ( !( f = fopen( file, "r" ) ) )
        return l;

    while ( fgets( buf, sizeof( buf ), f ) )
    {
        int size = strlen( buf );
        /* Remove trailing \r and \n, if any. */
        while ( ( size > 0 ) &&
            ( buf[ size - 1 ] == '\n' ) &&
            ( buf[ size - 1 ] == '\r' ) )
        {
            buf[ size - 1 ] = '\0';
            --size;
        }

        for ( i = 0; i < rec; ++i )
            if ( regexec( re[i], buf ) && re[i]->startp[1] )
            {
                re[i]->endp[1][0] = '\0';

                if ( DEBUG_HEADER )
                    printf( "header found: %s\n", re[i]->startp[1] );

                l = list_new( l, newstr( re[i]->startp[1] ) );
            }

        /* special treatment for #include MACRO */
        if ( regexec( re_macros, buf ) && re_macros->startp[1] )
        {
            char*  header_filename;

            re_macros->endp[1][0] = '\0';

            if ( DEBUG_HEADER )
                printf( "macro header found: %s", re_macros->startp[1] );

            header_filename = macro_header_get( re_macros->startp[1] );
            if ( header_filename )
            {
                if ( DEBUG_HEADER )
                    printf( " resolved to '%s'\n", header_filename );
                l = list_new( l, newstr( header_filename ) );
            }
            else
            {
                if ( DEBUG_HEADER )
                    printf( " ignored !!\n" );
            }
        }
    }

    fclose( f );

    return l;
}


void regerror( char * s )
{
    printf( "re error %s\n", s );
}